mirror of
https://github.com/shoelace-style/webawesome.git
synced 2026-01-12 04:09:12 +00:00
fixed changelog conflict
This commit is contained in:
@@ -76,6 +76,7 @@ export default async function (eleventyConfig) {
|
||||
//
|
||||
eleventyConfig.addGlobalData('package', packageData);
|
||||
eleventyConfig.addGlobalData('layout', 'page.njk');
|
||||
eleventyConfig.addGlobalData('pageType', 'docs'); // Default page type
|
||||
eleventyConfig.addGlobalData('server', {
|
||||
head: '',
|
||||
loginOrAvatar: '',
|
||||
|
||||
@@ -15,8 +15,14 @@
|
||||
{% if hasSidebar %}<script type="module" src="/assets/scripts/sidebar.js"></script>{% endif %}
|
||||
<script defer data-domain="webawesome.com" src="https://plausible.io/js/script.js"></script>
|
||||
|
||||
{# Docs styles #}
|
||||
<link rel="stylesheet" href="/assets/styles/docs.css" />
|
||||
{% if pageType == 'marketing' %}
|
||||
{# Marketing styles #}
|
||||
<link rel="stylesheet" href="/assets/styles/marketing.css" />
|
||||
{% else %}
|
||||
{# Docs styles (default) #}
|
||||
<link rel="stylesheet" href="/assets/styles/docs.css" />
|
||||
{% endif %}
|
||||
|
||||
|
||||
{% block head %}{% endblock %}
|
||||
|
||||
@@ -42,51 +48,55 @@
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body class="layout-{{ layout | stripExtension }}{{ ' page-wide' if wide }}">
|
||||
<body class="layout-{{ layout | stripExtension }} page-{{ page.fileSlug or 'home' }}{{ ' page-wide' if wide }} page-{{ pageType or 'docs' }}">
|
||||
<!-- use view="desktop" as default to reduce layout jank on desktop site. -->
|
||||
<wa-page view="desktop" disable-navigation-toggle mobile-breakpoint="1180">
|
||||
<header slot="header" class="wa-split">
|
||||
{# Logo #}
|
||||
<div id="docs-branding">
|
||||
{# Nav toggle #}
|
||||
<wa-button appearance="plain" size="small" data-toggle-nav>
|
||||
<wa-icon name="bars" label="Toggle navigation"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-page view="desktop" disable-navigation-toggle {% if pageType == 'marketing' %}disable-sticky="header"{% endif %} mobile-breakpoint="1180">
|
||||
{% if pageHeader %}
|
||||
{% include pageHeader %}
|
||||
{% else %}
|
||||
<header slot="header" class="wa-split">
|
||||
{# Logo #}
|
||||
<div id="docs-branding">
|
||||
{# Nav toggle #}
|
||||
<wa-button appearance="plain" size="small" data-toggle-nav>
|
||||
<wa-icon name="bars" label="Toggle navigation"></wa-icon>
|
||||
</wa-button>
|
||||
|
||||
<a href="/" aria-label="Web Awesome">
|
||||
<span class="wa-desktop-only">{% include "logo.njk" %}</span>
|
||||
<span class="wa-mobile-only">{% include "logo-simple.njk" %}</span>
|
||||
</a>
|
||||
<small id="version-number" class="wa-desktop-only">{{ package.version }}</small>
|
||||
<wa-badge variant="brand" appearance="filled" class="wa-desktop-only">Beta</wa-badge>
|
||||
</div>
|
||||
|
||||
<div id="docs-toolbar" class="wa-cluster">
|
||||
{# Desktop selectors #}
|
||||
<div class="wa-desktop-only wa-cluster wa-gap-xs">
|
||||
{% include "theme-selector.njk" %}
|
||||
{% include "color-scheme-selector.njk" %}
|
||||
<a href="/" aria-label="Web Awesome">
|
||||
<span class="wa-desktop-only">{% include "logo.njk" %}</span>
|
||||
<span class="wa-mobile-only">{% include "logo-simple.njk" %}</span>
|
||||
</a>
|
||||
<small id="version-number" class="wa-desktop-only">{{ package.version }}</small>
|
||||
<wa-badge variant="brand" appearance="filled" class="wa-desktop-only">Beta</wa-badge>
|
||||
</div>
|
||||
|
||||
<wa-divider orientation="vertical" class="wa-desktop-only"></wa-divider>
|
||||
<div id="docs-toolbar" class="wa-cluster">
|
||||
{# Desktop selectors #}
|
||||
<div class="wa-desktop-only wa-cluster wa-gap-xs">
|
||||
{% include "theme-selector.njk" %}
|
||||
{% include "color-scheme-selector.njk" %}
|
||||
</div>
|
||||
|
||||
<div id="github-buttons" class="wa-cluster wa-gap-xs">
|
||||
<wa-button id="github-repo-button" href="https://github.com/shoelace-style/webawesome" rel="noopener noreferrer" target="_blank" appearance="filled" size="small">
|
||||
<wa-icon family="brands" name="github" label="GitHub"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-tooltip for="github-repo-button" distance="2">GitHub</wa-tooltip>
|
||||
<wa-button id="github-star-button" href="https://github.com/shoelace-style/webawesome/stargazers" rel="noopener noreferrer" target="_blank" appearance="filled" size="small">
|
||||
<wa-icon name="star" variant="regular" label="Star this repository"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-tooltip for="github-star-button" distance="2">Star this repository</wa-tooltip>
|
||||
<wa-divider orientation="vertical" class="wa-desktop-only"></wa-divider>
|
||||
|
||||
<div id="github-buttons" class="wa-cluster wa-gap-xs">
|
||||
<wa-button id="github-repo-button" href="https://github.com/shoelace-style/webawesome" rel="noopener noreferrer" target="_blank" appearance="filled" size="small">
|
||||
<wa-icon family="brands" name="github" label="GitHub"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-tooltip for="github-repo-button" distance="2">GitHub</wa-tooltip>
|
||||
<wa-button id="github-star-button" href="https://github.com/shoelace-style/webawesome/stargazers" rel="noopener noreferrer" target="_blank" appearance="filled" size="small">
|
||||
<wa-icon name="star" variant="regular" label="Star this repository"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-tooltip for="github-star-button" distance="2">Star this repository</wa-tooltip>
|
||||
</div>
|
||||
|
||||
<wa-divider orientation="vertical"></wa-divider>
|
||||
|
||||
{# Login #}
|
||||
{% server "loginOrAvatar" %}
|
||||
</div>
|
||||
|
||||
<wa-divider orientation="vertical"></wa-divider>
|
||||
|
||||
{# Login #}
|
||||
{% server "loginOrAvatar" %}
|
||||
</div>
|
||||
</header>
|
||||
</header>
|
||||
{% endif %}
|
||||
|
||||
{# Sidebar #}
|
||||
{% if hasSidebar %}
|
||||
@@ -138,6 +148,11 @@
|
||||
</main>
|
||||
|
||||
{% include 'search.njk' %}
|
||||
|
||||
{# Footer #}
|
||||
{% if pageFooter %}
|
||||
{% include pageFooter %}
|
||||
{% endif %}
|
||||
</wa-page>
|
||||
|
||||
</body>
|
||||
|
||||
@@ -98,6 +98,7 @@
|
||||
<li><a href="/docs/components/icon/">Icon</a></li>
|
||||
<li><a href="/docs/components/include/">Include</a></li>
|
||||
<li><a href="/docs/components/input/">Input</a></li>
|
||||
<li><a href="/docs/components/intersection-observer">Intersection Observer</a></li>
|
||||
<li><a href="/docs/components/mutation-observer/">Mutation Observer</a></li>
|
||||
<li><a href="/docs/components/popover/">Popover</a></li>
|
||||
<li><a href="/docs/components/popup/">Popup</a></li>
|
||||
|
||||
@@ -504,7 +504,6 @@ wa-card .page-name {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
--background-color-hover: transparent;
|
||||
font-family: var(--wa-font-family-code);
|
||||
|
||||
&::part(button) {
|
||||
@@ -514,6 +513,7 @@ wa-card .page-name {
|
||||
}
|
||||
|
||||
&::part(button):hover {
|
||||
background-color: transparent;
|
||||
cursor: copy;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,297 @@
|
||||
---
|
||||
title: Intersection Observer
|
||||
description: Tracks immediate child elements and fires events as they move in and out of view.
|
||||
layout: component
|
||||
---
|
||||
|
||||
This component leverages the [IntersectionObserver API](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver) to track when its direct children enter or leave a designated root element. The `wa-intersect` event fires whenever elements cross the visibility threshold.
|
||||
|
||||
```html {.example}
|
||||
<div id="intersection__overview">
|
||||
<wa-intersection-observer threshold="1" intersect-class="visible">
|
||||
<div class="box"><wa-icon name="bulb"></wa-icon></div>
|
||||
</wa-intersection-observer>
|
||||
</div>
|
||||
|
||||
<small>Scroll to see the element intersect at 100% visibility</small>
|
||||
|
||||
<style>
|
||||
/* Container styles */
|
||||
#intersection__overview {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2rem;
|
||||
height: 300px;
|
||||
border: solid 2px var(--wa-color-surface-border);
|
||||
padding: 1rem;
|
||||
overflow-y: auto;
|
||||
|
||||
/* Spacers to demonstrate scrolling */
|
||||
&::before {
|
||||
content: '';
|
||||
height: 260px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
height: 260px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* Box styles */
|
||||
.box {
|
||||
flex-shrink: 0;
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
background-color: var(--wa-color-neutral-fill-normal);
|
||||
color: var(--wa-color-neutral-10);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-inline: auto;
|
||||
transition: all 50ms cubic-bezier(0.68, -0.55, 0.265, 1.55);
|
||||
|
||||
wa-icon {
|
||||
font-size: 3rem;
|
||||
stroke-width: 1px;
|
||||
}
|
||||
|
||||
&.visible {
|
||||
background-color: var(--wa-color-brand-60);
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
||||
+ small {
|
||||
display: block;
|
||||
text-align: center;
|
||||
margin-block-start: 1rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
:::info
|
||||
Keep in mind that only direct children of the host element are monitored. Nested elements won't trigger intersection events.
|
||||
:::
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Adding Observable Content
|
||||
|
||||
The intersection observer tracks only its direct children. The component uses [`display: contents`](https://developer.mozilla.org/en-US/docs/Web/CSS/display#contents) styling, which makes it seamless to integrate with flex and grid layouts from a parent container.
|
||||
|
||||
```html
|
||||
<div style="display: flex; flex-direction: column;">
|
||||
<wa-intersection-observer>
|
||||
<div class="box">Box 1</div>
|
||||
<div class="box">Box 2</div>
|
||||
<div class="box">Box 3</div>
|
||||
</wa-intersection-observer>
|
||||
</div>
|
||||
```
|
||||
|
||||
The component tracks elements as they enter and exit the root element (viewport by default) and emits the `wa-intersect` event on state changes. The event provides `event.detail.entry`, an [`IntersectionObserverEntry`](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserverEntry) object with intersection details.
|
||||
|
||||
You can identify the triggering element through `entry.target`. Check `entry.isIntersecting` to determine if an element is entering or exiting the viewport.
|
||||
|
||||
```javascript
|
||||
observer.addEventListener('wa-intersect', event => {
|
||||
const entry = event.detail.entry;
|
||||
|
||||
if (entry.isIntersecting) {
|
||||
console.log('Element entered viewport:', entry.target);
|
||||
} else {
|
||||
console.log('Element left viewport:', entry.target);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Setting a Custom Root Element
|
||||
|
||||
You can observe intersections within a specific container by assigning the `root` attribute to the [root element's](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver/root) ID. Apply [`rootMargin`](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver/rootMargin) with the `root-margin` attribute to expand or contract the observation area.
|
||||
|
||||
```html
|
||||
<div id="scroll-container">
|
||||
<wa-intersection-observer root="scroll-container" root-margin="50px 0px"> ... </wa-intersection-observer>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Configuring Multiple Thresholds
|
||||
|
||||
Track different visibility percentages by providing multiple [`threshold`](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API#threshold) values as a space-separated list.
|
||||
|
||||
```html
|
||||
<wa-intersection-observer threshold="0 0.25 0.5 0.75 1"> ... </wa-intersection-observer>
|
||||
```
|
||||
|
||||
### Applying Classes on Intersect
|
||||
|
||||
The `intersect-class` attribute automatically toggles the specified class on direct children when they become visible. This enables pure CSS styling without JavaScript event handlers.
|
||||
|
||||
```html {.example}
|
||||
<div id="intersection__classes">
|
||||
<wa-intersection-observer threshold="0.5" intersect-class="visible" root="intersection__classes">
|
||||
<div class="box fade">Fade In</div>
|
||||
<div class="box slide">Slide In</div>
|
||||
<div class="box scale">Scale & Rotate</div>
|
||||
<div class="box bounce">Bounce</div>
|
||||
</wa-intersection-observer>
|
||||
</div>
|
||||
|
||||
<small>Scroll to see elements transition at 50% visibility</small>
|
||||
|
||||
<style>
|
||||
/* Container styles */
|
||||
#intersection__classes {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2rem;
|
||||
height: 300px;
|
||||
border: solid 2px var(--wa-color-surface-border);
|
||||
padding: 1rem;
|
||||
overflow-y: auto;
|
||||
|
||||
/* Spacers to demonstrate scrolling */
|
||||
&::before {
|
||||
content: '';
|
||||
height: 260px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
height: 260px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
+ small {
|
||||
display: block;
|
||||
text-align: center;
|
||||
margin-block-start: 1rem;
|
||||
}
|
||||
|
||||
/* Shared box styles */
|
||||
.box {
|
||||
flex-shrink: 0;
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
color: white;
|
||||
opacity: 0;
|
||||
padding: 2rem;
|
||||
margin-inline: auto;
|
||||
|
||||
/* Fade */
|
||||
&.fade {
|
||||
background: var(--wa-color-brand-fill-loud);
|
||||
color: var(--wa-color-brand-on-loud);
|
||||
transform: translateY(30px);
|
||||
transition: all 0.6s ease;
|
||||
|
||||
&.visible {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Slide */
|
||||
&.slide {
|
||||
background: var(--wa-color-brand-fill-loud);
|
||||
color: var(--wa-color-brand-on-loud);
|
||||
transform: translateX(-50px);
|
||||
transition: all 0.5s ease;
|
||||
|
||||
&.visible {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Scale */
|
||||
&.scale {
|
||||
background: var(--wa-color-brand-fill-loud);
|
||||
color: var(--wa-color-brand-on-loud);
|
||||
transform: scale(0.6) rotate(-15deg);
|
||||
transition: all 0.7s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
|
||||
&.visible {
|
||||
opacity: 1;
|
||||
transform: scale(1) rotate(0deg);
|
||||
}
|
||||
}
|
||||
|
||||
/* Bounce In and Out */
|
||||
&.bounce {
|
||||
background: var(--wa-color-brand-fill-loud);
|
||||
color: var(--wa-color-brand-on-loud);
|
||||
opacity: 0;
|
||||
transform: scale(0.8);
|
||||
transition: none;
|
||||
|
||||
&.visible {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
animation: bounceIn 0.8s cubic-bezier(0.68, -0.55, 0.265, 1.55) forwards;
|
||||
}
|
||||
|
||||
&:not(.visible) {
|
||||
animation: bounceOut 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55) forwards;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes bounceIn {
|
||||
0% {
|
||||
transform: scale(0.8);
|
||||
}
|
||||
40% {
|
||||
transform: scale(1.08);
|
||||
}
|
||||
65% {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
80% {
|
||||
transform: scale(1.02);
|
||||
}
|
||||
90% {
|
||||
transform: scale(0.99);
|
||||
}
|
||||
100% {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes bounceOut {
|
||||
0% {
|
||||
transform: scale(1);
|
||||
opacity: 1;
|
||||
}
|
||||
20% {
|
||||
transform: scale(1.02);
|
||||
opacity: 1;
|
||||
}
|
||||
40% {
|
||||
transform: scale(0.98);
|
||||
opacity: 0.8;
|
||||
}
|
||||
60% {
|
||||
transform: scale(1.05);
|
||||
opacity: 0.6;
|
||||
}
|
||||
80% {
|
||||
transform: scale(0.95);
|
||||
opacity: 0.3;
|
||||
}
|
||||
100% {
|
||||
transform: scale(0.8);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
```
|
||||
@@ -18,6 +18,7 @@ Components with the <wa-badge variant="warning">Experimental</wa-badge> badge sh
|
||||
- Removed the `fixed-width` attribute as it's now the default behavior
|
||||
- 🚨 BREAKING: Renamed the `icon-position` attribute to `icon-placement` in `<wa-details>` [discuss:1340]
|
||||
- 🚨 BREAKING: Removed the `size` attribute from `<wa-button-group>` as it only set the initial size and gets out of sync when buttons are updated (apply a `size` to each button instead)
|
||||
- Added the `<wa-intersection-observer>` component
|
||||
- Added the Hindi translation [pr:1307]
|
||||
- Added `--show-duration` and `--hide-duration` to `<wa-select>` [issue:1281]
|
||||
- Fixed incorrectly named exported tooltip parts in `<wa-slider>` [pr:1277]
|
||||
@@ -33,6 +34,8 @@ Components with the <wa-badge variant="warning">Experimental</wa-badge> badge sh
|
||||
- Fixed spacing in `<wa-input>` when both clear and password toggle icons are present [issue:1325]
|
||||
- Fixed a bug in `<wa-radio-group>` and `<wa-radio>` where changing appearances dynamically would render incorrectly [issue:1178]
|
||||
- Fixed a bug in `<wa-input>` that prevented the value from changing when assigning non-string values to `value` [issue:1323]
|
||||
- Fixed a bug in `<wa-color-picker>` that prevent the picker from staying in the viewport
|
||||
- Fixed a bug that in `<wa-icon>` that caused `library`, `family`, `variant` and `name` to not reflect [pr:#1395]
|
||||
- Added horizontal orientation support with `orientation="horizontal"` for `<wa-card>`
|
||||
|
||||
## 3.0.0-beta.4
|
||||
|
||||
@@ -59,7 +59,6 @@ Color is organized by three main categories:
|
||||
- [Foundational colors](#foundational-colors) that lay the groundwork for your theme
|
||||
- [Semantic colors](#semantic-colors) that draw attention and convey meaning
|
||||
|
||||
|
||||
## Color Scales
|
||||
|
||||
Color scales are determined by your [color palette](/docs/color-palettes) and are made up of the lowest level color tokens in your theme. Each token is identified by a name, like red or gray, and numerical tint based on the color's lightness. On this scale, 100 is equal to pure white and 0 is equal to pure black.
|
||||
@@ -73,6 +72,7 @@ You can use these tints to ensure accessible color contrast per [WCAG 2.1 succes
|
||||
You have several hand-crafted [color palettes](/docs/color-palettes) to choose from. Each palette defines 10 hues each with a scale of 11 tints using the format `--wa-color-{hue}-{tint}`.
|
||||
|
||||
{% for hue in ['red', 'orange', 'yellow', 'green', 'cyan', 'blue', 'indigo', 'purple', 'pink', 'gray'] -%}
|
||||
|
||||
<div class="color-name">{{ hue | capitalize }}</div>
|
||||
<ul class="color-group">
|
||||
{% for tint in ['95', '90', '80', '70', '60', '50', '40', '30', '20', '10', '05'] -%}
|
||||
@@ -91,6 +91,7 @@ You have several hand-crafted [color palettes](/docs/color-palettes) to choose f
|
||||
Any hue can be mapped to `brand`, `neutral`, `success`, `warning`, and `danger` scales. Like the tokens in a color scale, each token is identified by its semantic group and a numerical tint using the format `--wa-color-{group}-{tint}`.
|
||||
|
||||
{% for group in ['brand', 'neutral', 'success', 'warning', 'danger'] -%}
|
||||
|
||||
<div class="color-name">{{ group | capitalize }}</div>
|
||||
<ul class="color-group">
|
||||
{% for tint in ['95', '90', '80', '70', '60', '50', '40', '30', '20', '10', '05'] -%}
|
||||
@@ -112,19 +113,19 @@ Foundational colors lay the groundwork for the content and structure of your pro
|
||||
|
||||
Surfaces are background layers that other content rests on. Surface colors help convey hierarchy through a sense of elevation, where `--wa-color-surface-raised` is the closest to the user (e.g., dialogs and popup menus) and `--wa-color-surface-lowered` is the farthest away (e.g., wells).
|
||||
|
||||
| Custom Property | Preview |
|
||||
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `--wa-color-surface-raised` | <div class="swatch" style="background-color: var(--wa-color-surface-raised); box-shadow:var(--wa-shadow-s)"></div> |
|
||||
| `--wa-color-surface-default` | <div class="swatch" style="background-color: var(--wa-color-surface-default)"></div> |
|
||||
| `--wa-color-surface-lowered` | <div class="swatch" style="background-color: var(--wa-color-surface-lowered); box-shadow: inset var(--wa-shadow-s)"></div> |
|
||||
| `--wa-color-surface-border` | <div class="swatch" style="border-color: var(--wa-color-surface-border)"></div> |
|
||||
| Custom Property | Preview |
|
||||
| ---------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `--wa-color-surface-raised` | <div class="swatch" style="background-color: var(--wa-color-surface-raised); box-shadow:var(--wa-shadow-s)"></div> |
|
||||
| `--wa-color-surface-default` | <div class="swatch" style="background-color: var(--wa-color-surface-default)"></div> |
|
||||
| `--wa-color-surface-lowered` | <div class="swatch" style="background-color: var(--wa-color-surface-lowered); box-shadow: inset var(--wa-shadow-s)"></div> |
|
||||
| `--wa-color-surface-border` | <div class="swatch" style="border-color: var(--wa-color-surface-border)"></div> |
|
||||
|
||||
### Text
|
||||
|
||||
Text colors are used for standard text elements. We recommend a minimum 4.5:1 contrast ratio between text colors and surface colors.
|
||||
|
||||
| Custom Property | Preview |
|
||||
| ------------------------ | ---------------------------------------------------------- |
|
||||
| Custom Property | Preview |
|
||||
| ------------------------ | -------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `--wa-color-text-normal` | <div class="swatch" value="--wa-color-text-normal" style="color: var(--wa-color-text-normal); display: inline-block;">AaBb</div> |
|
||||
| `--wa-color-text-quiet` | <div class="swatch" value="--wa-color-text-normal" style="color: var(--wa-color-text-quiet); display: inline-block;">AaBb</div> |
|
||||
| `--wa-color-text-link` | <div class="swatch" value="--wa-color-text-normal" style="color: var(--wa-color-text-link); display: inline-block;">AaBb</div> |
|
||||
@@ -153,23 +154,23 @@ This is used alongside other [shadow tokens](/docs/tokens/shadows) to construct
|
||||
|
||||
Web Awesome uses a single focus color for predictable keyboard navigation. This is used alongside other [focus tokens](/docs/tokens/focus) to construct `--wa-focus-ring`. We recommend a minimum 3:1 contrast ratio against surface colors and background colors wherever possible.
|
||||
|
||||
| Custom Property | Preview |
|
||||
| ------------------ | ----------------------------------------------------------------------------------------------------------------------- |
|
||||
| Custom Property | Preview |
|
||||
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `--wa-color-focus` | <div class="swatch" value="--wa-color-focus" style="outline: var(--wa-focus-ring-style) var(--wa-focus-ring-width) var(--wa-color-focus)"></div> |
|
||||
|
||||
#### Hover and Active
|
||||
|
||||
Web Awesome leverages `color-mix()` to achieve consistent hover and active states across components without the need for untold numbers of handpicked colors. Through `color-mix()`, these custom properties contextually generate hover and active colors based on the color of the component.
|
||||
|
||||
| Custom Property | Preview |
|
||||
| ----------------------- | ---------------------------------------------------------------------------------------------------------------- |
|
||||
| `--wa-color-mix-hover` | <div class="swatch color-mix-example" value="--wa-color-mix-hover" style="--mix-color: var(--wa-color-mix-hover)"><small>mixed</small></div> |
|
||||
| Custom Property | Preview |
|
||||
| ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `--wa-color-mix-hover` | <div class="swatch color-mix-example" value="--wa-color-mix-hover" style="--mix-color: var(--wa-color-mix-hover)"><small>mixed</small></div> |
|
||||
| `--wa-color-mix-active` | <div class="swatch color-mix-example" value="--wa-color-mix-active" style="--mix-color: var(--wa-color-mix-active)"><small>mixed</small></div> |
|
||||
|
||||
|
||||
## Semantic Colors
|
||||
|
||||
Semantic colors reinforce a specific message, intended usage, or expected results through familiar, meaningful hues. Each color is identified by its semantic group, role, and attention using the format `--wa-color-{group}-{role}-{attention}`. There are five groups of semantic colors:
|
||||
|
||||
- **Brand** to emphasize your brand color
|
||||
- **Success** for validity or confirmation
|
||||
- **Neutral** for ordinary or inactive content
|
||||
@@ -177,16 +178,19 @@ Semantic colors reinforce a specific message, intended usage, or expected result
|
||||
- **Danger** for errors or risk
|
||||
|
||||
Each group defines colors for specific roles so that colors can be easily assembled with predictable results and readable contrast. There are three roles:
|
||||
|
||||
- **Fill** for background colors or areas larger than a few pixels
|
||||
- **Border** for borders, dividers, and other stroke-width elements
|
||||
- **On** for content displayed on a fill (e.g., pair `--wa-color-danger-on-loud` with `--wa-color-danger-fill-loud`)
|
||||
|
||||
Finally, each color is named according to how much attention it draws. Here, we use noise as an analogy: a loud noise draws more attention than a quiet one. There are three levels of attention:
|
||||
|
||||
- **Quiet** draws the least attention
|
||||
- **Normal** draws some attention
|
||||
- **Loud** draws the most attention
|
||||
|
||||
{% set variants = ['brand', 'success', 'neutral', 'warning', 'danger'] %}
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
@@ -1327,6 +1327,10 @@ export default class WaColorPicker extends WebAwesomeFormAssociatedElement {
|
||||
distance="0"
|
||||
skidding="0"
|
||||
sync="width"
|
||||
flip
|
||||
flip-fallback-strategy="best-fit"
|
||||
shift
|
||||
shift-padding="10"
|
||||
aria-disabled=${this.disabled ? 'true' : 'false'}
|
||||
@wa-after-show=${this.handleAfterShow}
|
||||
@wa-after-hide=${this.handleAfterHide}
|
||||
|
||||
@@ -46,7 +46,7 @@ export default class WaIcon extends WebAwesomeElement {
|
||||
@state() private svg: SVGElement | HTMLTemplateResult | null = null;
|
||||
|
||||
/** The name of the icon to draw. Available names depend on the icon library being used. */
|
||||
@property() name?: string;
|
||||
@property({ reflect: true }) name?: string;
|
||||
|
||||
/**
|
||||
* The family of icons to choose from. For Font Awesome Free, valid options include `classic` and `brands`. For
|
||||
@@ -54,14 +54,14 @@ export default class WaIcon extends WebAwesomeElement {
|
||||
* A valid kit code must be present to show pro icons via CDN. You can set `<html data-fa-kit-code="...">` to provide
|
||||
* one.
|
||||
*/
|
||||
@property() family: string;
|
||||
@property({ reflect: true }) family: string;
|
||||
|
||||
/**
|
||||
* The name of the icon's variant. For Font Awesome, valid options include `thin`, `light`, `regular`, and `solid` for
|
||||
* the `classic` and `sharp` families. Some variants require a Font Awesome Pro subscription. Custom icon libraries
|
||||
* may or may not use this property.
|
||||
*/
|
||||
@property() variant: string;
|
||||
@property({ reflect: true }) variant: string;
|
||||
|
||||
/** Sets the width of the icon to match the cropped SVG viewBox. This operates like the Font `fa-width-auto` class. */
|
||||
@property({ attribute: 'auto-width', type: Boolean, reflect: true }) autoWidth: false;
|
||||
@@ -82,7 +82,7 @@ export default class WaIcon extends WebAwesomeElement {
|
||||
@property() label = '';
|
||||
|
||||
/** The name of a registered custom icon library. */
|
||||
@property() library = 'default';
|
||||
@property({ reflect: true }) library = 'default';
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { getKitCode } from '../../utilities/base-path.js';
|
||||
import type { IconLibrary } from './library.js';
|
||||
|
||||
const FA_VERSION = '7.0.0';
|
||||
const FA_VERSION = '7.0.1';
|
||||
|
||||
function getIconUrl(name: string, family: string, variant: string) {
|
||||
const kitCode = getKitCode();
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
:host {
|
||||
display: contents;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { expect, fixture, html } from '@open-wc/testing';
|
||||
|
||||
describe('<wa-intersection-observer>', () => {
|
||||
it('should render a component', async () => {
|
||||
const el = await fixture(html` <wa-intersection-observer></wa-intersection-observer> `);
|
||||
|
||||
expect(el).to.exist;
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,200 @@
|
||||
import { html } from 'lit';
|
||||
import { customElement, property } from 'lit/decorators.js';
|
||||
import { WaIntersectEvent } from '../../events/intersect.js';
|
||||
import { clamp } from '../../internal/math.js';
|
||||
import { parseSpaceDelimitedTokens } from '../../internal/parse.js';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import WebAwesomeElement from '../../internal/webawesome-element.js';
|
||||
import styles from './intersection-observer.css';
|
||||
|
||||
/**
|
||||
* @summary Tracks immediate child elements and fires events as they move in and out of view.
|
||||
* @documentation https://webawesome.com/docs/components/intersection-observer
|
||||
* @status stable
|
||||
* @since 2.0
|
||||
*
|
||||
* @slot - Elements to track. Only immediate children of the host are monitored.
|
||||
*
|
||||
* @event {{ entry: IntersectionObserverEntry }} wa-intersect - Fired when a tracked element begins or ceases intersecting.
|
||||
*/
|
||||
@customElement('wa-intersection-observer')
|
||||
export default class WaIntersectionObserver extends WebAwesomeElement {
|
||||
static css = styles;
|
||||
|
||||
private intersectionObserver: IntersectionObserver | null = null;
|
||||
private observedElements = new Map<Element, boolean>();
|
||||
|
||||
/** Element ID to define the viewport boundaries for tracked targets. */
|
||||
@property() root: string | null = null;
|
||||
|
||||
/** Offset space around the root boundary. Accepts values like CSS margin syntax. */
|
||||
@property({ attribute: 'root-margin' }) rootMargin = '0px';
|
||||
|
||||
/** One or more space-separated values representing visibility percentages that trigger the observer callback. */
|
||||
@property() threshold = '0';
|
||||
|
||||
/**
|
||||
* CSS class applied to elements during intersection. Automatically removed when elements leave
|
||||
* the viewport, enabling pure CSS styling based on visibility state.
|
||||
*/
|
||||
@property({ attribute: 'intersect-class' }) intersectClass = '';
|
||||
|
||||
/** If enabled, observation ceases after initial intersection. */
|
||||
@property({ type: Boolean, reflect: true }) once = false;
|
||||
|
||||
/** Deactivates the intersection observer functionality. */
|
||||
@property({ type: Boolean, reflect: true }) disabled = false;
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
|
||||
if (!this.disabled) {
|
||||
this.updateComplete.then(() => {
|
||||
this.startObserver();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
this.stopObserver();
|
||||
}
|
||||
|
||||
private handleSlotChange() {
|
||||
if (!this.disabled) {
|
||||
this.startObserver();
|
||||
}
|
||||
}
|
||||
|
||||
/** Converts threshold property string into numeric array. */
|
||||
private parseThreshold(): number[] {
|
||||
const tokens = parseSpaceDelimitedTokens(this.threshold);
|
||||
return tokens.map((token: string) => {
|
||||
const num = parseFloat(token);
|
||||
return isNaN(num) ? 0 : clamp(num, 0, 1);
|
||||
});
|
||||
}
|
||||
|
||||
/** Locates and returns the root element using the specified ID. */
|
||||
private resolveRoot(): Element | null {
|
||||
if (!this.root) return null;
|
||||
|
||||
try {
|
||||
const doc = this.getRootNode() as Document | ShadowRoot;
|
||||
const target = doc.getElementById(this.root);
|
||||
|
||||
if (!target) {
|
||||
console.warn(`Root element with ID "${this.root}" could not be found.`, this);
|
||||
}
|
||||
|
||||
return target;
|
||||
} catch {
|
||||
console.warn(`Invalid selector for root: "${this.root}"`, this);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/** Initializes or reinitializes the intersection observer instance. */
|
||||
private startObserver() {
|
||||
this.stopObserver();
|
||||
|
||||
// Skip setup if functionality is disabled
|
||||
if (this.disabled) return;
|
||||
|
||||
// Convert threshold string to numeric values
|
||||
const threshold = this.parseThreshold();
|
||||
|
||||
// Locate the root boundary element
|
||||
const rootElement = this.resolveRoot();
|
||||
|
||||
// Set up unified observer for all child elements
|
||||
this.intersectionObserver = new IntersectionObserver(
|
||||
entries => {
|
||||
entries.forEach(entry => {
|
||||
const wasIntersecting = this.observedElements.get(entry.target) ?? false;
|
||||
const isIntersecting = entry.isIntersecting;
|
||||
|
||||
// Record current intersection state
|
||||
this.observedElements.set(entry.target, isIntersecting);
|
||||
|
||||
// Toggle intersection class based on visibility
|
||||
if (this.intersectClass) {
|
||||
if (isIntersecting) {
|
||||
entry.target.classList.add(this.intersectClass);
|
||||
} else {
|
||||
entry.target.classList.remove(this.intersectClass);
|
||||
}
|
||||
}
|
||||
|
||||
// Emit the intersection event
|
||||
const changeEvent = new WaIntersectEvent({ entry });
|
||||
this.dispatchEvent(changeEvent);
|
||||
|
||||
if (isIntersecting && !wasIntersecting) {
|
||||
// When once mode is active, cease tracking after first intersection
|
||||
if (this.once) {
|
||||
this.intersectionObserver?.unobserve(entry.target);
|
||||
this.observedElements.delete(entry.target);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
{
|
||||
root: rootElement,
|
||||
rootMargin: this.rootMargin,
|
||||
threshold,
|
||||
},
|
||||
);
|
||||
|
||||
// Begin tracking all immediate child elements
|
||||
const slot = this.shadowRoot!.querySelector('slot');
|
||||
if (slot !== null) {
|
||||
const elements = slot.assignedElements({ flatten: true });
|
||||
elements.forEach(element => {
|
||||
this.intersectionObserver?.observe(element);
|
||||
// Set initial non-intersecting state
|
||||
this.observedElements.set(element, false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/** Halts the intersection observer and cleans up. */
|
||||
private stopObserver() {
|
||||
// Clear intersection classes from all tracked elements before stopping
|
||||
if (this.intersectClass) {
|
||||
this.observedElements.forEach((_, element) => {
|
||||
element.classList.remove(this.intersectClass);
|
||||
});
|
||||
}
|
||||
|
||||
this.intersectionObserver?.disconnect();
|
||||
this.intersectionObserver = null;
|
||||
this.observedElements.clear();
|
||||
}
|
||||
|
||||
@watch('disabled', { waitUntilFirstUpdate: true })
|
||||
handleDisabledChange() {
|
||||
if (this.disabled) {
|
||||
this.stopObserver();
|
||||
} else {
|
||||
this.startObserver();
|
||||
}
|
||||
}
|
||||
|
||||
@watch('root', { waitUntilFirstUpdate: true })
|
||||
@watch('rootMargin', { waitUntilFirstUpdate: true })
|
||||
@watch('threshold', { waitUntilFirstUpdate: true })
|
||||
handleOptionsChange() {
|
||||
this.startObserver();
|
||||
}
|
||||
|
||||
render() {
|
||||
return html` <slot @slotchange=${this.handleSlotChange}></slot> `;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'wa-intersection-observer': WaIntersectionObserver;
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ export type { WaExpandEvent } from './expand.js';
|
||||
export type { WaFinishEvent } from './finish.js';
|
||||
export type { WaHideEvent } from './hide.js';
|
||||
export type { WaHoverEvent } from './hover.js';
|
||||
export type { WaIntersectEvent } from './intersect.js';
|
||||
export type { WaInvalidEvent } from './invalid.js';
|
||||
export type { WaLazyChangeEvent } from './lazy-change.js';
|
||||
export type { WaLazyLoadEvent } from './lazy-load.js';
|
||||
|
||||
19
packages/webawesome/src/events/intersect.ts
Normal file
19
packages/webawesome/src/events/intersect.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
/** Emitted when an element's intersection state changes. */
|
||||
export class WaIntersectEvent extends Event {
|
||||
readonly detail?: WaIntersectEventDetail;
|
||||
|
||||
constructor(detail?: WaIntersectEventDetail) {
|
||||
super('wa-intersect', { bubbles: false, cancelable: false, composed: true });
|
||||
this.detail = detail;
|
||||
}
|
||||
}
|
||||
|
||||
interface WaIntersectEventDetail {
|
||||
entry?: IntersectionObserverEntry;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface GlobalEventHandlersEventMap {
|
||||
'wa-intersect': WaIntersectEvent;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,9 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "Running the build!"
|
||||
git clone "https://konnorrogers:$GITHUB_ACCESS_TOKEN@github.com/shoelace-style/webawesome-pro" packages/webawesome-pro
|
||||
|
||||
if [[ "$CLONE_PRO" != "false" ]]; then
|
||||
git clone "https://konnorrogers:$GITHUB_ACCESS_TOKEN@github.com/shoelace-style/webawesome-pro" packages/webawesome-pro
|
||||
fi
|
||||
|
||||
cd packages/webawesome-pro && npm run build
|
||||
|
||||
Reference in New Issue
Block a user