Compare commits

..

27 Commits

Author SHA1 Message Date
Cory LaViska
46ef967978 reword 2025-09-10 11:05:33 -04:00
Brian Talbot
26b29189db fixing GitHub discussion links to point to webawesome project (#1425) 2025-09-10 10:53:38 -04:00
trent
ac20e495d9 allow bundlers access to package.json (#1415) 2025-09-10 10:53:17 -04:00
Cory LaViska
4ac31e06f3 remove whitespace; closes #1417 (#1418) 2025-09-10 10:38:06 -04:00
Konnor Rogers
4993b1034f add notes about skips (#1419)
* add notes about skips

* prettier

* re-add support for 'unpublished: true'

* prettier
2025-09-10 10:32:48 -04:00
Cory LaViska
b3b93091f7 Add intersection observer component (#1406)
* add intersection observer component

* remove prefix

* update description
2025-09-08 17:22:45 -04:00
Brian Talbot
8cf20d9938 Docs: Fix Color Swatch Hover States (#1407)
* removing unused `--background-color-hover` from `docs.css`
* fixing `.swatch` hover-based `background-color`
* linting + formatting `docs/tokens/color.md`
2025-09-05 16:30:15 -04:00
randomguy-2650
63296b7ed5 update FA version from 7.0.0 to 7.0.1 (#1399) 2025-09-03 15:01:35 -04:00
Brian Talbot
0e56ed0cbb Prep for Pro + Product Marketing (#1390)
* adding conditional header + footer includes to `base.njk`

- support for custom headers via `header` front matter variable
- support for custom footers via `footer` front matter variable
- backward compatibility with default header

* revising `base.njk`'s body classes

* adding fileSlug-based class to body
* adding pageType-based class (default `docs`) to body

* adding conditional CSS asset logic to `base.njk`

* to load marketing-based CSS based on pageType
* to load docs-based CSS by default

* disabling wa-page's sticky header for marketing pageType
2025-09-03 12:37:28 -04:00
Cory LaViska
e70cb5c66d add changelog entry (#1398) 2025-09-03 08:13:55 -04:00
Michael Romer
913abd0db1 Reflect icon properties to its attributes (#1395) 2025-09-03 08:12:11 -04:00
Cory LaViska
d2798a96da flip color picker to keep it in the viewport (#1391) 2025-09-02 18:02:48 -04:00
Konnor Rogers
42729153db update vercel sh clone (#1392) 2025-09-02 17:52:25 -04:00
Cory LaViska
c2df5ca1ea fix typo (#1381) 2025-08-29 11:34:48 -04:00
Cory LaViska
c49813c7c1 Update themer to use bunny fonts instead of google fonts (#1378)
* use fonts.bunny.net for web fonts

* use bunny
2025-08-27 19:24:44 +00:00
Cory LaViska
f63dfbfff0 fix var name (#1372) 2025-08-25 16:17:58 -04:00
Cory LaViska
9a7947debd fix radio group styles; closes #1178 (#1360) 2025-08-25 14:30:59 -04:00
Cory LaViska
d2e1653519 fix value assignment; closes #1323 (#1368) 2025-08-25 12:35:34 -04:00
Cory LaViska
5b7004e72d Fix edge case where interactive elements inside <wa-details> would cause it to open/close (#1350)
* fixes #1252

* fixes #1250

* update changelog
2025-08-25 11:08:36 -04:00
Lindsay M
7ccfed9e93 Use FA7 icons in native styles (#1362)
* replace icons with FA7 paths

* replace details icon
2025-08-22 10:47:12 -04:00
Lindsay M
4f1de3918b Fix button positioning for real (#1361) 2025-08-21 18:00:09 -04:00
Konnor Rogers
adaea2fa5f fix wa-page fallback loading in free repo (#1359) 2025-08-21 14:12:39 -04:00
Lindsay M
e016e4bd48 Fix home page icons (#1358) 2025-08-21 13:29:09 -04:00
Cory LaViska
5e0b5f3fe6 update system icon view boxes (#1357) 2025-08-21 11:46:09 -04:00
Cory LaViska
493e1c06b9 Fixes spacing between clear and password toggle icons in <wa-input> (#1351)
* fixes spacing between icons; closes #1325

* use form control padding token

---------

Co-authored-by: lindsaym-fa <dev@lindsaym.design>
2025-08-21 11:35:05 -04:00
Konnor Rogers
8b763d0392 remove page (#1352) 2025-08-20 17:45:28 -04:00
Brian Talbot
9d82749abb 💅 Docs: Sidebar Visual + UI Polish (#1341)
* using layout utilities for sidebar heading link styling
* syncing up sidebar heading + general link styles
* updating sidebar heading text + icon alignment
* updating layout of experimental + pro badge decoratives
* increasing visual padding of `#search-trigger`'s `<kbd>`
* fixing `<li>` indentations
2025-08-20 17:24:36 -04:00
48 changed files with 884 additions and 988 deletions

View File

@@ -92,6 +92,7 @@
"heroicons",
"hexa",
"Hotwire",
"hrefs",
"Iconoir",
"Iframes",
"iife",

2
package-lock.json generated
View File

@@ -14000,7 +14000,7 @@
},
"packages/webawesome-pro": {
"name": "@shoelace-style/webawesome-pro",
"version": "3.0.0-beta.3",
"version": "3.0.0-beta.4",
"dependencies": {
"@ctrl/tinycolor": "^4.1.0",
"@floating-ui/dom": "^1.6.13",

View File

@@ -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: '',
@@ -159,6 +160,15 @@ export default async function (eleventyConfig) {
// Use our own markdown instance
eleventyConfig.setLibrary('md', markdown);
// for files with `unpublished: true`, it will make sure they do not make it into the final build at all, but will be usable in development.
eleventyConfig.addPreprocessor('unpublished', '*', (data, content) => {
if (data.unpublished && process.env.ELEVENTY_RUN_MODE === 'build') {
return false;
}
return content;
});
// Add anchors to headings
eleventyConfig.addTransform('doc-transforms', function (content) {
let doc = HTMLParse(content, { blockTextElements: { code: true } });

View File

@@ -74,12 +74,12 @@ export const themes = [
body: {
name: 'Quicksand',
css: 'Quicksand, sans-serif',
href: 'https://fonts.googleapis.com/css2?family=Crimson+Pro:ital,wght@0,200..900;1,200..900&family=Quicksand:wght@300..700&display=swap',
href: 'https://fonts.bunny.net/css2?family=Crimson+Pro:ital,wght@0,200..900;1,200..900&family=Quicksand:wght@300..700&display=swap',
},
heading: {
name: 'Quicksand',
css: 'Quicksand, sans-serif',
href: 'https://fonts.googleapis.com/css2?family=Crimson+Pro:ital,wght@0,200..900;1,200..900&family=Quicksand:wght@300..700&display=swap',
href: 'https://fonts.bunny.net/css2?family=Crimson+Pro:ital,wght@0,200..900;1,200..900&family=Quicksand:wght@300..700&display=swap',
},
code: {
name: 'OS Default',
@@ -89,7 +89,7 @@ export const themes = [
longform: {
name: 'Crimson Pro',
css: '"Crimson Pro", serif',
href: 'https://fonts.googleapis.com/css2?family=Crimson+Pro:ital,wght@0,200..900;1,200..900&family=Quicksand:wght@300..700&display=swap',
href: 'https://fonts.bunny.net/css2?family=Crimson+Pro:ital,wght@0,200..900;1,200..900&family=Quicksand:wght@300..700&display=swap',
},
},
icons: {
@@ -194,22 +194,22 @@ export const themes = [
body: {
name: 'Inter',
css: 'Inter, sans-serif',
href: 'https://fonts.googleapis.com/css2?family=Aleo:ital,wght@0,100..900;1,100..900&family=Geist+Mono:wght@100..900&family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap',
href: 'https://fonts.bunny.net/css2?family=Aleo:ital,wght@0,100..900;1,100..900&family=Geist+Mono:wght@100..900&family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap',
},
heading: {
name: 'Inter',
css: 'Inter, sans-serif',
href: 'https://fonts.googleapis.com/css2?family=Aleo:ital,wght@0,100..900;1,100..900&family=Geist+Mono:wght@100..900&family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap',
href: 'https://fonts.bunny.net/css2?family=Aleo:ital,wght@0,100..900;1,100..900&family=Geist+Mono:wght@100..900&family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap',
},
code: {
name: 'Geist Mono',
css: '"Geist Mono", monospace',
href: 'https://fonts.googleapis.com/css2?family=Aleo:ital,wght@0,100..900;1,100..900&family=Geist+Mono:wght@100..900&family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap',
href: 'https://fonts.bunny.net/css2?family=Aleo:ital,wght@0,100..900;1,100..900&family=Geist+Mono:wght@100..900&family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap',
},
longform: {
name: 'Aleo',
css: 'Aleo, serif',
href: 'https://fonts.googleapis.com/css2?family=Aleo:ital,wght@0,100..900;1,100..900&family=Geist+Mono:wght@100..900&family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap',
href: 'https://fonts.bunny.net/css2?family=Aleo:ital,wght@0,100..900;1,100..900&family=Geist+Mono:wght@100..900&family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap',
},
},
icons: {
@@ -254,22 +254,22 @@ export const themes = [
body: {
name: 'Space Grotesk',
css: '"Space Grotesk", sans-serif',
href: 'https://fonts.googleapis.com/css2?family=IBM+Plex+Sans+Condensed:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;1,100;1,200;1,300;1,400;1,500;1,600;1,700&family=Podkova:wght@400..800&family=Space+Grotesk:wght@300..700&family=Space+Mono:ital,wght@0,400;0,700;1,400;1,700&display=swap',
href: 'https://fonts.bunny.net/css2?family=IBM+Plex+Sans+Condensed:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;1,100;1,200;1,300;1,400;1,500;1,600;1,700&family=Podkova:wght@400..800&family=Space+Grotesk:wght@300..700&family=Space+Mono:ital,wght@0,400;0,700;1,400;1,700&display=swap',
},
heading: {
name: 'IBM Plex Sans Condensed',
css: '"IBM Plex Sans Condensed", sans-serif',
href: 'https://fonts.googleapis.com/css2?family=IBM+Plex+Sans+Condensed:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;1,100;1,200;1,300;1,400;1,500;1,600;1,700&family=Podkova:wght@400..800&family=Space+Grotesk:wght@300..700&family=Space+Mono:ital,wght@0,400;0,700;1,400;1,700&display=swap',
href: 'https://fonts.bunny.net/css2?family=IBM+Plex+Sans+Condensed:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;1,100;1,200;1,300;1,400;1,500;1,600;1,700&family=Podkova:wght@400..800&family=Space+Grotesk:wght@300..700&family=Space+Mono:ital,wght@0,400;0,700;1,400;1,700&display=swap',
},
code: {
name: 'Space Mono',
css: '"Space Mono", monospace',
href: 'https://fonts.googleapis.com/css2?family=IBM+Plex+Sans+Condensed:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;1,100;1,200;1,300;1,400;1,500;1,600;1,700&family=Podkova:wght@400..800&family=Space+Grotesk:wght@300..700&family=Space+Mono:ital,wght@0,400;0,700;1,400;1,700&display=swap',
href: 'https://fonts.bunny.net/css2?family=IBM+Plex+Sans+Condensed:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;1,100;1,200;1,300;1,400;1,500;1,600;1,700&family=Podkova:wght@400..800&family=Space+Grotesk:wght@300..700&family=Space+Mono:ital,wght@0,400;0,700;1,400;1,700&display=swap',
},
longform: {
name: 'Podkova',
css: 'Podkova, serif',
href: 'https://fonts.googleapis.com/css2?family=IBM+Plex+Sans+Condensed:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;1,100;1,200;1,300;1,400;1,500;1,600;1,700&family=Podkova:wght@400..800&family=Space+Grotesk:wght@300..700&family=Space+Mono:ital,wght@0,400;0,700;1,400;1,700&display=swap',
href: 'https://fonts.bunny.net/css2?family=IBM+Plex+Sans+Condensed:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;1,100;1,200;1,300;1,400;1,500;1,600;1,700&family=Podkova:wght@400..800&family=Space+Grotesk:wght@300..700&family=Space+Mono:ital,wght@0,400;0,700;1,400;1,700&display=swap',
},
},
icons: {
@@ -314,22 +314,22 @@ export const themes = [
body: {
name: 'Figtree',
css: 'Figtree, sans-serif',
href: 'https://fonts.googleapis.com/css2?family=Chivo+Mono:ital,wght@0,100..900;1,100..900&family=Figtree:ital,wght@0,300..900;1,300..900&family=Fraunces:ital,opsz,wght@0,9..144,100..900;1,9..144,100..900&display=swap',
href: 'https://fonts.bunny.net/css2?family=Chivo+Mono:ital,wght@0,100..900;1,100..900&family=Figtree:ital,wght@0,300..900;1,300..900&family=Fraunces:ital,opsz,wght@0,9..144,100..900;1,9..144,100..900&display=swap',
},
heading: {
name: 'Figtree',
css: 'Figtree, sans-serif',
href: 'https://fonts.googleapis.com/css2?family=Chivo+Mono:ital,wght@0,100..900;1,100..900&family=Figtree:ital,wght@0,300..900;1,300..900&family=Fraunces:ital,opsz,wght@0,9..144,100..900;1,9..144,100..900&display=swap',
href: 'https://fonts.bunny.net/css2?family=Chivo+Mono:ital,wght@0,100..900;1,100..900&family=Figtree:ital,wght@0,300..900;1,300..900&family=Fraunces:ital,opsz,wght@0,9..144,100..900;1,9..144,100..900&display=swap',
},
code: {
name: 'Chivo Mono',
css: '"Chivo Mono", monospace',
href: 'https://fonts.googleapis.com/css2?family=Chivo+Mono:ital,wght@0,100..900;1,100..900&family=Figtree:ital,wght@0,300..900;1,300..900&family=Fraunces:ital,opsz,wght@0,9..144,100..900;1,9..144,100..900&display=swap',
href: 'https://fonts.bunny.net/css2?family=Chivo+Mono:ital,wght@0,100..900;1,100..900&family=Figtree:ital,wght@0,300..900;1,300..900&family=Fraunces:ital,opsz,wght@0,9..144,100..900;1,9..144,100..900&display=swap',
},
longform: {
name: 'Fraunces',
css: 'Fraunces, serif',
href: 'https://fonts.googleapis.com/css2?family=Chivo+Mono:ital,wght@0,100..900;1,100..900&family=Figtree:ital,wght@0,300..900;1,300..900&family=Fraunces:ital,opsz,wght@0,9..144,100..900;1,9..144,100..900&display=swap',
href: 'https://fonts.bunny.net/css2?family=Chivo+Mono:ital,wght@0,100..900;1,100..900&family=Figtree:ital,wght@0,300..900;1,300..900&family=Fraunces:ital,opsz,wght@0,9..144,100..900;1,9..144,100..900&display=swap',
},
},
icons: {
@@ -374,22 +374,22 @@ export const themes = [
body: {
name: 'Wix Madefor Text',
css: '"Wix Madefor Text", sans-serif',
href: 'https://fonts.googleapis.com/css2?family=Roboto+Mono:ital,wght@0,100..700;1,100..700&family=Roboto+Serif:ital,opsz,wght@0,8..144,100..900;1,8..144,100..900&family=Roboto:ital,wght@0,100..900;1,100..900&family=Wix+Madefor+Text:ital,wght@0,400..800;1,400..800&display=swap',
href: 'https://fonts.bunny.net/css2?family=Roboto+Mono:ital,wght@0,100..700;1,100..700&family=Roboto+Serif:ital,opsz,wght@0,8..144,100..900;1,8..144,100..900&family=Roboto:ital,wght@0,100..900;1,100..900&family=Wix+Madefor+Text:ital,wght@0,400..800;1,400..800&display=swap',
},
heading: {
name: 'Wix Madefor Text',
css: '"Wix Madefor Text", sans-serif',
href: 'https://fonts.googleapis.com/css2?family=Roboto+Mono:ital,wght@0,100..700;1,100..700&family=Roboto+Serif:ital,opsz,wght@0,8..144,100..900;1,8..144,100..900&family=Roboto:ital,wght@0,100..900;1,100..900&family=Wix+Madefor+Text:ital,wght@0,400..800;1,400..800&display=swap',
href: 'https://fonts.bunny.net/css2?family=Roboto+Mono:ital,wght@0,100..700;1,100..700&family=Roboto+Serif:ital,opsz,wght@0,8..144,100..900;1,8..144,100..900&family=Roboto:ital,wght@0,100..900;1,100..900&family=Wix+Madefor+Text:ital,wght@0,400..800;1,400..800&display=swap',
},
code: {
name: 'Roboto Mono',
css: '"Roboto Mono", monospace',
href: 'https://fonts.googleapis.com/css2?family=Roboto+Mono:ital,wght@0,100..700;1,100..700&family=Roboto+Serif:ital,opsz,wght@0,8..144,100..900;1,8..144,100..900&family=Roboto:ital,wght@0,100..900;1,100..900&family=Wix+Madefor+Text:ital,wght@0,400..800;1,400..800&display=swap',
href: 'https://fonts.bunny.net/css2?family=Roboto+Mono:ital,wght@0,100..700;1,100..700&family=Roboto+Serif:ital,opsz,wght@0,8..144,100..900;1,8..144,100..900&family=Roboto:ital,wght@0,100..900;1,100..900&family=Wix+Madefor+Text:ital,wght@0,400..800;1,400..800&display=swap',
},
longform: {
name: 'Roboto Serif',
css: '"Roboto Serif", serif',
href: 'https://fonts.googleapis.com/css2?family=Roboto+Mono:ital,wght@0,100..700;1,100..700&family=Roboto+Serif:ital,opsz,wght@0,8..144,100..900;1,8..144,100..900&family=Roboto:ital,wght@0,100..900;1,100..900&family=Wix+Madefor+Text:ital,wght@0,400..800;1,400..800&display=swap',
href: 'https://fonts.bunny.net/css2?family=Roboto+Mono:ital,wght@0,100..700;1,100..700&family=Roboto+Serif:ital,opsz,wght@0,8..144,100..900;1,8..144,100..900&family=Roboto:ital,wght@0,100..900;1,100..900&family=Wix+Madefor+Text:ital,wght@0,400..800;1,400..800&display=swap',
},
},
icons: {
@@ -434,12 +434,12 @@ export const themes = [
body: {
name: 'Mulish',
css: 'Mulish, sans-serif',
href: 'https://fonts.googleapis.com/css2?family=Lora:ital,wght@0,400..700;1,400..700&family=Mulish:ital,wght@0,200..1000;1,200..1000&display=swap',
href: 'https://fonts.bunny.net/css2?family=Lora:ital,wght@0,400..700;1,400..700&family=Mulish:ital,wght@0,200..1000;1,200..1000&display=swap',
},
heading: {
name: 'Lora',
css: 'Lora, serif',
href: 'https://fonts.googleapis.com/css2?family=Lora:ital,wght@0,400..700;1,400..700&family=Mulish:ital,wght@0,200..1000;1,200..1000&display=swap',
href: 'https://fonts.bunny.net/css2?family=Lora:ital,wght@0,400..700;1,400..700&family=Mulish:ital,wght@0,200..1000;1,200..1000&display=swap',
},
code: {
name: 'OS Default',
@@ -449,7 +449,7 @@ export const themes = [
longform: {
name: 'Lora',
css: 'Lora, serif',
href: 'https://fonts.googleapis.com/css2?family=Lora:ital,wght@0,400..700;1,400..700&family=Mulish:ital,wght@0,200..1000;1,200..1000&display=swap',
href: 'https://fonts.bunny.net/css2?family=Lora:ital,wght@0,400..700;1,400..700&family=Mulish:ital,wght@0,200..1000;1,200..1000&display=swap',
},
},
icons: {
@@ -494,22 +494,22 @@ export const themes = [
body: {
name: 'Nunito',
css: 'Nunito, sans-serif',
href: 'https://fonts.googleapis.com/css2?family=Azeret+Mono:ital,wght@0,100..900;1,100..900&family=BioRhyme:wght@200..800&family=Fredoka:wght@300..700&family=Nunito:ital,wght@0,200..1000;1,200..1000&display=swap',
href: 'https://fonts.bunny.net/css2?family=Azeret+Mono:ital,wght@0,100..900;1,100..900&family=BioRhyme:wght@200..800&family=Fredoka:wght@300..700&family=Nunito:ital,wght@0,200..1000;1,200..1000&display=swap',
},
heading: {
name: 'Fredoka',
css: 'Fredoka, sans-serif',
href: 'https://fonts.googleapis.com/css2?family=Azeret+Mono:ital,wght@0,100..900;1,100..900&family=BioRhyme:wght@200..800&family=Fredoka:wght@300..700&family=Nunito:ital,wght@0,200..1000;1,200..1000&display=swap',
href: 'https://fonts.bunny.net/css2?family=Azeret+Mono:ital,wght@0,100..900;1,100..900&family=BioRhyme:wght@200..800&family=Fredoka:wght@300..700&family=Nunito:ital,wght@0,200..1000;1,200..1000&display=swap',
},
code: {
name: 'Azeret Mono',
css: '"Azeret Mono", monospace',
href: 'https://fonts.googleapis.com/css2?family=Azeret+Mono:ital,wght@0,100..900;1,100..900&family=BioRhyme:wght@200..800&family=Fredoka:wght@300..700&family=Nunito:ital,wght@0,200..1000;1,200..1000&display=swap',
href: 'https://fonts.bunny.net/css2?family=Azeret+Mono:ital,wght@0,100..900;1,100..900&family=BioRhyme:wght@200..800&family=Fredoka:wght@300..700&family=Nunito:ital,wght@0,200..1000;1,200..1000&display=swap',
},
longform: {
name: 'BioRhyme',
css: 'BioRhyme, serif',
href: 'https://fonts.googleapis.com/css2?family=Azeret+Mono:ital,wght@0,100..900;1,100..900&family=BioRhyme:wght@200..800&family=Fredoka:wght@300..700&family=Nunito:ital,wght@0,200..1000;1,200..1000&display=swap',
href: 'https://fonts.bunny.net/css2?family=Azeret+Mono:ital,wght@0,100..900;1,100..900&family=BioRhyme:wght@200..800&family=Fredoka:wght@300..700&family=Nunito:ital,wght@0,200..1000;1,200..1000&display=swap',
},
},
icons: {
@@ -554,12 +554,12 @@ export const themes = [
body: {
name: 'DM Sans',
css: '"DM Sans", sans-serif',
href: 'https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,100..1000;1,9..40,100..1000&family=Playfair+Display:ital,wght@0,400..900;1,400..900&family=Playfair:ital,opsz,wght@0,5..1200,300..900;1,5..1200,300..900&display=swap',
href: 'https://fonts.bunny.net/css2?family=DM+Sans:ital,opsz,wght@0,9..40,100..1000;1,9..40,100..1000&family=Playfair+Display:ital,wght@0,400..900;1,400..900&family=Playfair:ital,opsz,wght@0,5..1200,300..900;1,5..1200,300..900&display=swap',
},
heading: {
name: 'Playfair Display',
css: '"Playfair Display", serif',
href: 'https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,100..1000;1,9..40,100..1000&family=Playfair+Display:ital,wght@0,400..900;1,400..900&family=Playfair:ital,opsz,wght@0,5..1200,300..900;1,5..1200,300..900&display=swap',
href: 'https://fonts.bunny.net/css2?family=DM+Sans:ital,opsz,wght@0,9..40,100..1000;1,9..40,100..1000&family=Playfair+Display:ital,wght@0,400..900;1,400..900&family=Playfair:ital,opsz,wght@0,5..1200,300..900;1,5..1200,300..900&display=swap',
},
code: {
name: 'OS Default',
@@ -569,7 +569,7 @@ export const themes = [
longform: {
name: 'Playfair',
css: 'Playfair, serif',
href: 'https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,100..1000;1,9..40,100..1000&family=Playfair+Display:ital,wght@0,400..900;1,400..900&family=Playfair:ital,opsz,wght@0,5..1200,300..900;1,5..1200,300..900&display=swap',
href: 'https://fonts.bunny.net/css2?family=DM+Sans:ital,opsz,wght@0,9..40,100..1000;1,9..40,100..1000&family=Playfair+Display:ital,wght@0,400..900;1,400..900&family=Playfair:ital,opsz,wght@0,5..1200,300..900;1,5..1200,300..900&display=swap',
},
},
icons: {
@@ -614,12 +614,12 @@ export const themes = [
body: {
name: 'Inter',
css: 'Inter, sans-serif',
href: 'https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap',
href: 'https://fonts.bunny.net/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap',
},
heading: {
name: 'Inter',
css: 'Inter, sans-serif',
href: 'https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap',
href: 'https://fonts.bunny.net/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap',
},
code: {
name: 'OS Default',

View File

@@ -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>

View File

@@ -26,5 +26,17 @@
{% endfor %}
<link rel="stylesheet" href="/dist/styles/webawesome.css" />
<script type="module">
document.addEventListener("wa-discovery-complete", loadLayout)
function loadLayout () {
if (!customElements.get("wa-layout")) {
import("{% cdnUrl 'components/page/page.js' %}")
.catch((e) => {
// known errors with dual registration. This is only a thing in the free repo.
})
}
}
</script>
{# Used by Web Awesome App to inject other assets into the head. #}
{% server "head" %}

View File

@@ -31,16 +31,19 @@
<!-- Components -->
<wa-details appearance="outlined">
<h2 slot="summary">
<a href="/docs/components/" title="Overview">
Components
<a class="wa-cluster wa-gap-xs" href="/docs/components/" title="Overview">
<wa-icon name="grid-2" aria-hidden="true"></wa-icon>
Components
</a>
</h2>
<ul>
<li>
<a href="/docs/components/page/">Page</a>
<wa-icon name="flask" aria-hidden="true"></wa-icon>
<wa-badge class="pro" appearance="accent" attention="none">PRO</wa-badge>
<span class="wa-split">
<a class="wa-cluster wa-gap-xs" href="/docs/components/page/">
Page <wa-icon name="flask" aria-hidden="true"></wa-icon>
</a>
<wa-badge class="pro" appearance="accent" attention="none">PRO</wa-badge>
</span>
</li>
<li><a href="/docs/components/animated-image/">Animated Image</a></li>
<li><a href="/docs/components/animation/">Animation</a></li>
@@ -57,12 +60,16 @@
<li><a href="/docs/components/callout/">Callout</a></li>
<li><a href="/docs/components/card/">Card</a></li>
<li>
<a href="/docs/components/carousel/">Carousel</a>
<wa-icon name="flask" aria-hidden="true"></wa-icon>
<a class="wa-cluster wa-gap-xs" href="/docs/components/carousel/">
Carousel
<wa-icon name="flask" aria-hidden="true"></wa-icon>
</a>
<ul>
<li>
<a href="/docs/components/carousel-item/">Carousel Item</a>
<wa-icon name="flask" aria-hidden="true"></wa-icon>
<a class="wa-cluster wa-gap-xs" href="/docs/components/carousel-item/">
Carousel Item
<wa-icon name="flask" aria-hidden="true"></wa-icon>
</a>
</li>
</ul>
</li>
@@ -70,8 +77,10 @@
<li><a href="/docs/components/color-picker/">Color Picker</a></li>
<li><a href="/docs/components/comparison/">Comparison</a></li>
<li>
<a href="/docs/components/copy-button/">Copy Button</a>
<wa-icon name="flask" aria-hidden="true"></wa-icon>
<a class="wa-cluster wa-gap-xs" href="/docs/components/copy-button/">
Copy Button
<wa-icon name="flask" aria-hidden="true"></wa-icon>
</a>
</li>
<li><a href="/docs/components/details/">Details</a></li>
<li><a href="/docs/components/dialog/">Dialog</a></li>
@@ -89,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>
@@ -136,9 +146,9 @@
<!-- Style utilities -->
<wa-details appearance="outlined">
<h2 slot="summary">
<a href="/docs/utilities/" title="Overview">
Style Utilities
<a class="wa-cluster wa-gap-xs" href="/docs/utilities/" title="Overview">
<wa-icon name="grid-2" aria-hidden="true"></wa-icon>
Style Utilities
</a>
</h2>
<ul>
@@ -154,9 +164,9 @@
<!-- Layout -->
<wa-details appearance="outlined">
<h2 slot="summary">
<a href="/docs/layout/" title="Overview">
Layout
<a class="wa-cluster wa-gap-xs" href="/docs/layout/" title="Overview">
<wa-icon name="grid-2" aria-hidden="true"></wa-icon>
Layout
</a>
</h2>
<ul>
@@ -169,9 +179,12 @@
<li><a href="/docs/utilities/split/">Split</a></li>
<li><a href="/docs/utilities/stack/">Stack</a></li>
<li>
<a href="/docs/components/page/">Page</a>
<wa-icon name="flask" aria-hidden="true"></wa-icon>
<wa-badge class="pro" appearance="accent" attention="none">PRO</wa-badge>
<span class="wa-split">
<a class="wa-cluster wa-gap-xs" href="/docs/components/page/">
Page <wa-icon name="flask" aria-hidden="true"></wa-icon>
</a>
<wa-badge class="pro" appearance="accent" attention="none">PRO</wa-badge>
</span>
</li>
</ul>
</wa-details>
@@ -179,15 +192,17 @@
<!-- Patterns -->
<wa-details appearance="outlined">
<h2 slot="summary">
<a href="/docs/patterns/" title="Overview">
Patterns
<a class="wa-cluster wa-gap-xs" href="/docs/patterns/" title="Overview">
<wa-icon name="grid-2" aria-hidden="true"></wa-icon>
Patterns
</a>
</h2>
<ul>
<li>
<a href="/docs/patterns/app/">App</a>
<wa-badge class="pro" appearance="accent" attention="none">PRO</wa-badge>
<span class="wa-split">
<a href="/docs/patterns/app/">App</a>
<wa-badge class="pro" appearance="accent" attention="none">PRO</wa-badge>
</span>
<ul>
<li>
<a href="/docs/patterns/app/action-panel/">Action Panel</a>
@@ -230,8 +245,10 @@
</ul>
</li>
<li>
<a href="/docs/patterns/blog-news/">Blog &amp; News</a>
<wa-badge class="pro" appearance="accent" attention="none">PRO</wa-badge>
<span class="wa-split">
<a href="/docs/patterns/blog-news/">Blog &amp; News</a>
<wa-badge class="pro" appearance="accent" attention="none">PRO</wa-badge>
</span>
<ul>
<li>
<a href="/docs/patterns/blog-news/banners/">Banners</a>
@@ -275,7 +292,7 @@
<li>
<a href="/docs/patterns/blog-news/login/">Sign Up &amp; Login</a>
</li>
<li>
<li>
<a href="/docs/patterns/blog-news/numbers/">Numbers</a>
</li>
<li>
@@ -284,14 +301,16 @@
<li>
<a href="/docs/patterns/blog-news/teams/">Teams</a>
</li>
<li>
<li>
<a href="/docs/patterns/blog-news/testimonials/">Testimonials</a>
</li>
</ul>
</li>
<li>
<a href="/docs/patterns/ecommerce/">Ecommerce</a>
<wa-badge class="pro" appearance="accent" attention="none">PRO</wa-badge>
<span class="wa-split">
<a href="/docs/patterns/ecommerce/">Ecommerce</a>
<wa-badge class="pro" appearance="accent" attention="none">PRO</wa-badge>
</span>
<ul>
<li>
<a href="/docs/patterns/ecommerce/category-filter/">Category Filter</a>
@@ -329,8 +348,10 @@
</ul>
</li>
<li>
<a href="/docs/patterns/layouts/">Layouts</a>
<wa-badge class="pro" appearance="accent" attention="none">PRO</wa-badge>
<span class="wa-split">
<a href="/docs/patterns/layouts/">Layouts</a>
<wa-badge class="pro" appearance="accent" attention="none">PRO</wa-badge>
</span>
<ul>
<li>
<a href="/docs/patterns/layouts/ecommerce/">Ecommerce</a>
@@ -352,17 +373,19 @@
<li><a href="/docs/color-palettes">Color Palettes</a></li>
<li><a href="/docs/themes">Themes</a></li>
<li>
<a href="/themer" data-turbo="false">Theme Builder</a>
<wa-badge class="pro" appearance="accent" attention="none">PRO</wa-badge>
<span class="wa-split">
<a href="/themer" data-turbo="false">Theme Builder</a>
<wa-badge class="pro" appearance="accent" attention="none">PRO</wa-badge>
</span>
</li>
</ul>
<!-- Design tokens -->
<wa-details appearance="outlined">
<h2 slot="summary">
<a href="/docs/tokens/" title="Overview">
Design Tokens
<a class="wa-cluster wa-gap-xs" href="/docs/tokens/" title="Overview">
<wa-icon name="grid-2" aria-hidden="true"></wa-icon>
Design Tokens
</a>
</h2>
<ul>

View File

@@ -181,11 +181,6 @@ wa-page > header {
li wa-icon {
color: var(--wa-color-text-quiet);
vertical-align: middle;
&[name='flask'] {
margin-inline: 0.125em;
}
}
}
@@ -205,25 +200,16 @@ wa-badge.pro {
color: var(--wa-color-text-quiet);
a {
display: flex;
align-items: center;
gap: 0.4em;
color: var(--wa-color-text-normal);
text-decoration: none;
wa-icon {
margin-block-end: -0.15em;
font-size: var(--wa-font-size-s);
font-weight: var(--wa-font-weight-action);
color: var(--wa-color-gray-70);
}
&:hover {
color: var(--wa-color-brand-on-normal);
wa-icon {
color: var(--wa-color-brand-on-quiet);
}
text-decoration: underline;
}
}
}
@@ -415,6 +401,7 @@ wa-button#search-trigger {
#search-trigger kbd {
font-size: var(--wa-font-size-2xs);
line-height: var(--wa-form-control-value-line-height);
padding-inline: 0.33em;
}
/* Search list pages */
@@ -517,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) {
@@ -527,6 +513,7 @@ wa-card .page-name {
}
&::part(button):hover {
background-color: transparent;
cursor: copy;
}

View File

@@ -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>
```

View File

@@ -191,7 +191,7 @@ This gives you inline documentation, autocomplete, and type-safe validation for
```json
{
"compilerOptions": {
"types": ["node-modules/@awesome.me/webawesome/dist/custom-elements-jsx.d.ts"]
"types": ["node_modules/@awesome.me/webawesome/dist/custom-elements-jsx.d.ts"]
}
}
```

View File

@@ -56,7 +56,7 @@ To contribute new translations or improvements to existing translations, please
Regional translations are welcome! For example, if a German translation (`de`) exists it's perfectly acceptable to submit a German (Switzerland) (`de-CH`) translation.
If you have any questions, please start a [discussion](https://github.com/shoelace-style/shoelace/discussions) or ask in the [community chat](https://discord.gg/mg8f26C).
If you have any questions, please start a [discussion](https://github.com/shoelace-style/webawesome/discussions) or ask in the [community chat](https://discord.gg/mg8f26C).
:::info
Web Awesome provides a localization mechanism for component internals. This is not designed to be used as localization tool for your entire application. You should use a more appropriate tool such as [i18next](https://www.i18next.com/) if you need to localize content in your app.

View File

@@ -14,7 +14,7 @@ Furthermore, accessibility doesnt stop at the component level. Using accessib
My commitment to Web Awesome users is this: Everything we develop will be built with accessibility in mind. We will test and improve every component to the best of our ability and knowledge. We will work around upstream issues, such as browser bugs and limitations, to the best of our ability and within reason.
Were fully aware that we may not get it right every time for every user, so we invite the community to participate in this ongoing effort by submitting [issues](https://github.com/shoelace-style/shoelace/issues?q=is%3Aissue+is%3Aopen+label%3Aa11y), [pull requests](https://github.com/shoelace-style/shoelace/pulls?q=is%3Aopen+is%3Apr+label%3Aa11y), and [discussions](https://github.com/shoelace-style/shoelace/discussions). Many accessibility improvements have already been made thanks to contributors submitting code, feedback, and suggestions.
Were fully aware that we may not get it right every time for every user, so we invite the community to participate in this ongoing effort by submitting [issues](https://github.com/shoelace-style/shoelace/issues?q=is%3Aissue+is%3Aopen+label%3Aa11y), [pull requests](https://github.com/shoelace-style/shoelace/pulls?q=is%3Aopen+is%3Apr+label%3Aa11y), and [discussions](https://github.com/shoelace-style/webawesome/discussions). Many accessibility improvements have already been made thanks to contributors submitting code, feedback, and suggestions.
This is the path forward. Together, we will continue to make Web Awesome accessible to as many users as possible.

View File

@@ -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]
@@ -28,7 +29,14 @@ Components with the <wa-badge variant="warning">Experimental</wa-badge> badge sh
- Fixed a bug in JSX typings that generated the incorrect component imports [issue:1303]
- Fixed a bug in `<wa-slider>` that prevented the thumb from receiving focus when clicking/tapping [issue:1312]
- Fixed a bug in `<wa-scroller>` that caused the shadow to appear below relatively-positioned elements [issue:1326]
- Fixed a bug in `<wa-details>` that caused it to expand/collapse when clicking on interactive elements in the summary [issue:1252]
- Fixed `<wa-button>` to have `static` positioning by default and `relative` positioning only when used with `<wa-badge>` [pr:1346]
- 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]
- Fixed a bug in `<wa-format-date>` and `<wa-relative-time>` that caused spaces to appear before and after the output [#1417]
## 3.0.0-beta.4

View File

@@ -10,14 +10,14 @@ Please be respectful of other users and remember that Web Awesome is an open sou
## Discussion Forum
The [discussion forum](https://github.com/shoelace-style/shoelace/discussions) is open to anyone with a GitHub account. This is the best place to:
The [discussion forum](https://github.com/shoelace-style/webawesome/discussions) is open to anyone with a GitHub account. This is the best place to:
- Ask for help
- Share ideas and get feedback
- Show the community what you're working on
- Learn more about the project, its values, and its roadmap
<wa-button variant="brand" href="https://github.com/shoelace-style/shoelace/discussions" target="_blank" style="margin-block-end: var(--wa-content-spacing);">
<wa-button variant="brand" href="https://github.com/shoelace-style/webawesome/discussions" target="_blank" style="margin-block-end: var(--wa-content-spacing);">
<wa-icon name="github" family="brands" slot="start"></wa-icon>
Join the Discussion
</wa-button>
@@ -40,9 +40,9 @@ The [community chat](https://discord.gg/mg8f26C) is open to the public and power
Follow [@webawesomer](https://twitter.com/webawesomer) on Twitter for general updates and announcements about Web Awesome. This is a great place to say "hi" or to share something you're working on.
**Please avoid using Twitter for support questions.** The [discussion forum](https://github.com/shoelace-style/shoelace/discussions) is a much better place to share code snippets, screenshots, and other troubleshooting info. You'll have much better luck there, as more users will have a chance to help you.
**Please avoid using Twitter for support questions.** The [discussion forum](https://github.com/shoelace-style/webawesome/discussions) is a much better place to share code snippets, screenshots, and other troubleshooting info. You'll have much better luck there, as more users will have a chance to help you.
<wa-button variant="brand" href="https://twitter.com/webawesomer" target="_blank" style="margin-block-end: var(--wa-content-spacing);">
<wa-icon name="twitter" family="brands" slot="start"></wa-icon>
Follow on Twitter
</wa-button>
</wa-button>

View File

@@ -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>

View File

@@ -7,7 +7,7 @@ tags: styleUtilities
Often, components are shown before their logic and styles have had a chance to load, also known as a [Flash of Undefined Custom Elements](https://www.abeautifulsite.net/posts/flash-of-undefined-custom-elements/).
The FOUCE style utility (which is automatically applied if you use our [style utilities](/docs/utilities/)) automatically takes care of hiding custom elements until **both they and their contents** have been registered, up to a maximum of two seconds.
The FOUCE style utility takes care of hiding custom elements until **both they and their contents** have been registered, up to a maximum of two seconds.
In many cases, this is not enough, and you may wish to hide a broader wrapper element or even the entire page until all WA elements within it have loaded.
To do that, you can add the `wa-cloak` class to any element on the page or even apply it to the whole page by placing the class on the `<html>` element:

View File

@@ -141,10 +141,11 @@ layout: page
display: flex;
align-items: center;
justify-content: center;
block-size: 2em;
inline-size: 2em;
background-color: var(--wa-color-neutral-fill-loud);
color: var(--wa-color-neutral-on-loud);
border-radius: 0.25rem;
aspect-ratio: 1;
padding: 0.5em;
&.brand-orange {

View File

@@ -29,7 +29,8 @@
"./dist/react": "./dist/react/index.js",
"./dist/react/*": "./dist/react/*",
"./dist/translations": "./dist/translations",
"./dist/translations/*": "./dist/translations/*"
"./dist/translations/*": "./dist/translations/*",
"./package.json": "./package.json"
},
"files": [
"README.md",

View File

@@ -123,6 +123,7 @@ export async function build(options = {}) {
function generateReactWrappers() {
// Used by webawesome-app to make re-rendering not miserable with extra React file generation.
if (process.env.SKIP_SLOW_STEPS === 'true') {
spinner.info('Skipping React Wrapper generation.');
return Promise.resolve();
}
@@ -162,6 +163,7 @@ export async function build(options = {}) {
async function generateTypes() {
// Used by webawesome-app to make re-rendering not miserable with extra TS compilations.
if (process.env.SKIP_SLOW_STEPS === 'true') {
spinner.info('Skipping TypeScript compiler.');
return Promise.resolve();
}

View File

@@ -1,8 +1,17 @@
@layer wa-component {
:host {
display: inline-block;
/* Workaround because Chrome doesn't like :host(:has()) below
* https://issues.chromium.org/issues/40062355
* Firefox doesn't like this nested rule, so both are needed */
&:has(wa-badge) {
position: relative;
}
}
/* Apply relative positioning only when needed to position wa-badge
* This avoids creating a new stacking context for every button */
:host(:has(wa-badge)) {
position: relative;
}

View File

@@ -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}

View File

@@ -7,9 +7,10 @@ import { WaAfterShowEvent } from '../../events/after-show.js';
import { WaHideEvent } from '../../events/hide.js';
import { WaShowEvent } from '../../events/show.js';
import { animate, parseDuration } from '../../internal/animate.js';
import { getTargetElement, waitForEvent } from '../../internal/event.js';
import { waitForEvent } from '../../internal/event.js';
import { watch } from '../../internal/watch.js';
import WebAwesomeElement from '../../internal/webawesome-element.js';
import { WebAwesomeFormAssociatedElement } from '../../internal/webawesome-form-associated-element.js';
import { LocalizeController } from '../../utilities/localize.js';
import '../icon/icon.js';
import styles from './details.css';
@@ -112,9 +113,27 @@ export default class WaDetails extends WebAwesomeElement {
}
private handleSummaryClick(event: MouseEvent) {
let targetElement = getTargetElement(event);
const eventPath = event.composedPath() as HTMLElement[];
if (targetElement?.closest('a, button, wa-button, input, wa-input, textarea, wa-textarea, select, wa-select')) {
// Check if any element in the path is interactive
const hasInteractiveElement = eventPath.some(element => {
if (!(element instanceof HTMLElement)) return false;
// Check native interactive elements
const tagName = element.tagName?.toLowerCase();
if (['a', 'button', 'input', 'textarea', 'select'].includes(tagName)) {
return true;
}
// Check for Web Awesome form controls
if (element instanceof WebAwesomeFormAssociatedElement) {
return !('disabled' in element) || !element.disabled;
}
return false;
});
if (hasInteractiveElement) {
// Let interactive elements handle their own clicks, fixes #309
return;
}

View File

@@ -66,23 +66,22 @@ export default class WaFormatDate extends WebAwesomeElement {
return undefined;
}
return html`
<time datetime=${date.toISOString()}>
${this.localize.date(date, {
weekday: this.weekday,
era: this.era,
year: this.year,
month: this.month,
day: this.day,
hour: this.hour,
minute: this.minute,
second: this.second,
timeZoneName: this.timeZoneName,
timeZone: this.timeZone,
hour12: hour12,
})}
</time>
`;
const displayDate = this.localize.date(date, {
weekday: this.weekday,
era: this.era,
year: this.year,
month: this.month,
day: this.day,
hour: this.hour,
minute: this.minute,
second: this.second,
timeZoneName: this.timeZoneName,
timeZone: this.timeZone,
hour12: hour12,
});
// No whitespace before or after
return html`<time datetime=${date.toISOString()}>${displayDate}</time>`;
}
}

View File

@@ -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();

View File

@@ -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();

View File

@@ -9,31 +9,31 @@ export const icons: { [key: string]: { [key: string]: string } } = {
// Solid variant
//
solid: {
check: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--!Font Awesome Free v7.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M530.8 134.1C545.1 144.5 548.3 164.5 537.9 178.8L281.9 530.8C276.4 538.4 267.9 543.1 258.5 543.9C249.1 544.7 240 541.2 233.4 534.6L105.4 406.6C92.9 394.1 92.9 373.8 105.4 361.3C117.9 348.8 138.2 348.8 150.7 361.3L252.2 462.8L486.2 141.1C496.6 126.8 516.6 123.6 530.9 134z"/></svg>`,
'chevron-down': `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--!Font Awesome Free v7.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M297.4 470.6C309.9 483.1 330.2 483.1 342.7 470.6L534.7 278.6C547.2 266.1 547.2 245.8 534.7 233.3C522.2 220.8 501.9 220.8 489.4 233.3L320 402.7L150.6 233.4C138.1 220.9 117.8 220.9 105.3 233.4C92.8 245.9 92.8 266.2 105.3 278.7L297.3 470.7z"/></svg>`,
'chevron-left': `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--!Font Awesome Free v7.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M169.4 297.4C156.9 309.9 156.9 330.2 169.4 342.7L361.4 534.7C373.9 547.2 394.2 547.2 406.7 534.7C419.2 522.2 419.2 501.9 406.7 489.4L237.3 320L406.6 150.6C419.1 138.1 419.1 117.8 406.6 105.3C394.1 92.8 373.8 92.8 361.3 105.3L169.3 297.3z"/></svg>`,
'chevron-right': `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--!Font Awesome Free v7.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M471.1 297.4C483.6 309.9 483.6 330.2 471.1 342.7L279.1 534.7C266.6 547.2 246.3 547.2 233.8 534.7C221.3 522.2 221.3 501.9 233.8 489.4L403.2 320L233.9 150.6C221.4 138.1 221.4 117.8 233.9 105.3C246.4 92.8 266.7 92.8 279.2 105.3L471.2 297.3z"/></svg>`,
circle: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--!Font Awesome Free v7.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M64 320C64 178.6 178.6 64 320 64C461.4 64 576 178.6 576 320C576 461.4 461.4 576 320 576C178.6 576 64 461.4 64 320z"/></svg>`,
eyedropper: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--!Font Awesome Free v7.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M405.6 93.2L304 194.8L294.6 185.4C282.1 172.9 261.8 172.9 249.3 185.4C236.8 197.9 236.8 218.2 249.3 230.7L409.3 390.7C421.8 403.2 442.1 403.2 454.6 390.7C467.1 378.2 467.1 357.9 454.6 345.4L445.2 336L546.8 234.4C585.8 195.4 585.8 132.2 546.8 93.3C507.8 54.4 444.6 54.3 405.7 93.3zM119.4 387.3C104.4 402.3 96 422.7 96 443.9L96 486.3L69.4 526.2C60.9 538.9 62.6 555.8 73.4 566.6C84.2 577.4 101.1 579.1 113.8 570.6L153.7 544L196.1 544C217.3 544 237.7 535.6 252.7 520.6L362.1 411.2L316.8 365.9L207.4 475.3C204.4 478.3 200.3 480 196.1 480L160 480L160 443.9C160 439.7 161.7 435.6 164.7 432.6L274.1 323.2L228.8 277.9L119.4 387.3z"/></svg>`,
'grip-vertical': `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--!Font Awesome Free v7.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M288 104C288 81.9 270.1 64 248 64L200 64C177.9 64 160 81.9 160 104L160 152C160 174.1 177.9 192 200 192L248 192C270.1 192 288 174.1 288 152L288 104zM288 296C288 273.9 270.1 256 248 256L200 256C177.9 256 160 273.9 160 296L160 344C160 366.1 177.9 384 200 384L248 384C270.1 384 288 366.1 288 344L288 296zM160 488L160 536C160 558.1 177.9 576 200 576L248 576C270.1 576 288 558.1 288 536L288 488C288 465.9 270.1 448 248 448L200 448C177.9 448 160 465.9 160 488zM480 104C480 81.9 462.1 64 440 64L392 64C369.9 64 352 81.9 352 104L352 152C352 174.1 369.9 192 392 192L440 192C462.1 192 480 174.1 480 152L480 104zM352 296L352 344C352 366.1 369.9 384 392 384L440 384C462.1 384 480 366.1 480 344L480 296C480 273.9 462.1 256 440 256L392 256C369.9 256 352 273.9 352 296zM480 488C480 465.9 462.1 448 440 448L392 448C369.9 448 352 465.9 352 488L352 536C352 558.1 369.9 576 392 576L440 576C462.1 576 480 558.1 480 536L480 488z"/></svg>`,
check: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Pro 7.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2025 Fonticons, Inc. --><path fill="currentColor" d="M434.8 70.1c14.3 10.4 17.5 30.4 7.1 44.7l-256 352c-5.5 7.6-14 12.3-23.4 13.1s-18.5-2.7-25.1-9.3l-128-128c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0l101.5 101.5 234-321.7c10.4-14.3 30.4-17.5 44.7-7.1z"/></svg>`,
'chevron-down': `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Pro 7.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2025 Fonticons, Inc. --><path fill="currentColor" d="M201.4 406.6c12.5 12.5 32.8 12.5 45.3 0l192-192c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L224 338.7 54.6 169.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3l192 192z"/></svg>`,
'chevron-left': `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><!--! Font Awesome Pro 7.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2025 Fonticons, Inc. --><path fill="currentColor" d="M9.4 233.4c-12.5 12.5-12.5 32.8 0 45.3l192 192c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L77.3 256 246.6 86.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0l-192 192z"/></svg>`,
'chevron-right': `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><!--! Font Awesome Pro 7.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2025 Fonticons, Inc. --><path fill="currentColor" d="M311.1 233.4c12.5 12.5 12.5 32.8 0 45.3l-192 192c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3L243.2 256 73.9 86.6c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0l192 192z"/></svg>`,
circle: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 7.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2025 Fonticons, Inc. --><path fill="currentColor" d="M0 256a256 256 0 1 1 512 0 256 256 0 1 1 -512 0z"/></svg>`,
eyedropper: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 7.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2025 Fonticons, Inc. --><path fill="currentColor" d="M341.6 29.2l-101.6 101.6-9.4-9.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3l160 160c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3l-9.4-9.4 101.6-101.6c39-39 39-102.2 0-141.1s-102.2-39-141.1 0zM55.4 323.3c-15 15-23.4 35.4-23.4 56.6l0 42.4-26.6 39.9c-8.5 12.7-6.8 29.6 4 40.4s27.7 12.5 40.4 4l39.9-26.6 42.4 0c21.2 0 41.6-8.4 56.6-23.4l109.4-109.4-45.3-45.3-109.4 109.4c-3 3-7.1 4.7-11.3 4.7l-36.1 0 0-36.1c0-4.2 1.7-8.3 4.7-11.3l109.4-109.4-45.3-45.3-109.4 109.4z"/></svg>`,
'grip-vertical': `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><!--! Font Awesome Pro 7.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2025 Fonticons, Inc. --><path fill="currentColor" d="M128 40c0-22.1-17.9-40-40-40L40 0C17.9 0 0 17.9 0 40L0 88c0 22.1 17.9 40 40 40l48 0c22.1 0 40-17.9 40-40l0-48zm0 192c0-22.1-17.9-40-40-40l-48 0c-22.1 0-40 17.9-40 40l0 48c0 22.1 17.9 40 40 40l48 0c22.1 0 40-17.9 40-40l0-48zM0 424l0 48c0 22.1 17.9 40 40 40l48 0c22.1 0 40-17.9 40-40l0-48c0-22.1-17.9-40-40-40l-48 0c-22.1 0-40 17.9-40 40zM320 40c0-22.1-17.9-40-40-40L232 0c-22.1 0-40 17.9-40 40l0 48c0 22.1 17.9 40 40 40l48 0c22.1 0 40-17.9 40-40l0-48zM192 232l0 48c0 22.1 17.9 40 40 40l48 0c22.1 0 40-17.9 40-40l0-48c0-22.1-17.9-40-40-40l-48 0c-22.1 0-40 17.9-40 40zM320 424c0-22.1-17.9-40-40-40l-48 0c-22.1 0-40 17.9-40 40l0 48c0 22.1 17.9 40 40 40l48 0c22.1 0 40-17.9 40-40l0-48z"/></svg>`,
indeterminate: `<svg part="indeterminate-icon" class="icon" viewBox="0 0 16 16"><g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round"><g stroke="currentColor" stroke-width="2"><g transform="translate(2.285714 6.857143)"><path d="M10.2857143,1.14285714 L1.14285714,1.14285714"/></g></g></g></svg>`,
minus: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--!Font Awesome Free v7.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M96 320C96 302.3 110.3 288 128 288L512 288C529.7 288 544 302.3 544 320C544 337.7 529.7 352 512 352L128 352C110.3 352 96 337.7 96 320z"/></svg>`,
pause: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--!Font Awesome Free v7.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M176 96C149.5 96 128 117.5 128 144L128 496C128 522.5 149.5 544 176 544L240 544C266.5 544 288 522.5 288 496L288 144C288 117.5 266.5 96 240 96L176 96zM400 96C373.5 96 352 117.5 352 144L352 496C352 522.5 373.5 544 400 544L464 544C490.5 544 512 522.5 512 496L512 144C512 117.5 490.5 96 464 96L400 96z"/></svg>`,
play: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--!Font Awesome Free v7.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M187.2 100.9C174.8 94.1 159.8 94.4 147.6 101.6C135.4 108.8 128 121.9 128 136L128 504C128 518.1 135.5 531.2 147.6 538.4C159.7 545.6 174.8 545.9 187.2 539.1L523.2 355.1C536 348.1 544 334.6 544 320C544 305.4 536 291.9 523.2 284.9L187.2 100.9z"/></svg>`,
star: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--!Font Awesome Free v7.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M341.5 45.1C337.4 37.1 329.1 32 320.1 32C311.1 32 302.8 37.1 298.7 45.1L225.1 189.3L65.2 214.7C56.3 216.1 48.9 222.4 46.1 231C43.3 239.6 45.6 249 51.9 255.4L166.3 369.9L141.1 529.8C139.7 538.7 143.4 547.7 150.7 553C158 558.3 167.6 559.1 175.7 555L320.1 481.6L464.4 555C472.4 559.1 482.1 558.3 489.4 553C496.7 547.7 500.4 538.8 499 529.8L473.7 369.9L588.1 255.4C594.5 249 596.7 239.6 593.9 231C591.1 222.4 583.8 216.1 574.8 214.7L415 189.3L341.5 45.1z"/></svg>`,
user: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--!Font Awesome Free v7.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M320 312C386.3 312 440 258.3 440 192C440 125.7 386.3 72 320 72C253.7 72 200 125.7 200 192C200 258.3 253.7 312 320 312zM290.3 368C191.8 368 112 447.8 112 546.3C112 562.7 125.3 576 141.7 576L498.3 576C514.7 576 528 562.7 528 546.3C528 447.8 448.2 368 349.7 368L290.3 368z"/></svg>`,
xmark: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--!Font Awesome Free v7.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M183.1 137.4C170.6 124.9 150.3 124.9 137.8 137.4C125.3 149.9 125.3 170.2 137.8 182.7L275.2 320L137.9 457.4C125.4 469.9 125.4 490.2 137.9 502.7C150.4 515.2 170.7 515.2 183.2 502.7L320.5 365.3L457.9 502.6C470.4 515.1 490.7 515.1 503.2 502.6C515.7 490.1 515.7 469.8 503.2 457.3L365.8 320L503.1 182.6C515.6 170.1 515.6 149.8 503.1 137.3C490.6 124.8 470.3 124.8 457.8 137.3L320.5 274.7L183.1 137.4z"/></svg>`,
minus: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Pro 7.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2025 Fonticons, Inc. --><path fill="currentColor" d="M0 256c0-17.7 14.3-32 32-32l384 0c17.7 0 32 14.3 32 32s-14.3 32-32 32L32 288c-17.7 0-32-14.3-32-32z"/></svg>`,
pause: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!--! Font Awesome Pro 7.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2025 Fonticons, Inc. --><path fill="currentColor" d="M48 32C21.5 32 0 53.5 0 80L0 432c0 26.5 21.5 48 48 48l64 0c26.5 0 48-21.5 48-48l0-352c0-26.5-21.5-48-48-48L48 32zm224 0c-26.5 0-48 21.5-48 48l0 352c0 26.5 21.5 48 48 48l64 0c26.5 0 48-21.5 48-48l0-352c0-26.5-21.5-48-48-48l-64 0z"/></svg>`,
play: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Pro 7.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2025 Fonticons, Inc. --><path fill="currentColor" d="M91.2 36.9c-12.4-6.8-27.4-6.5-39.6 .7S32 57.9 32 72l0 368c0 14.1 7.5 27.2 19.6 34.4s27.2 7.5 39.6 .7l336-184c12.8-7 20.8-20.5 20.8-35.1s-8-28.1-20.8-35.1l-336-184z"/></svg>`,
star: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!--! Font Awesome Pro 7.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2025 Fonticons, Inc. --><path fill="currentColor" d="M309.5-18.9c-4.1-8-12.4-13.1-21.4-13.1s-17.3 5.1-21.4 13.1L193.1 125.3 33.2 150.7c-8.9 1.4-16.3 7.7-19.1 16.3s-.5 18 5.8 24.4l114.4 114.5-25.2 159.9c-1.4 8.9 2.3 17.9 9.6 23.2s16.9 6.1 25 2L288.1 417.6 432.4 491c8 4.1 17.7 3.3 25-2s11-14.2 9.6-23.2L441.7 305.9 556.1 191.4c6.4-6.4 8.6-15.8 5.8-24.4s-10.1-14.9-19.1-16.3L383 125.3 309.5-18.9z"/></svg>`,
user: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Pro 7.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2025 Fonticons, Inc. --><path fill="currentColor" d="M224 248a120 120 0 1 0 0-240 120 120 0 1 0 0 240zm-29.7 56C95.8 304 16 383.8 16 482.3 16 498.7 29.3 512 45.7 512l356.6 0c16.4 0 29.7-13.3 29.7-29.7 0-98.5-79.8-178.3-178.3-178.3l-59.4 0z"/></svg>`,
xmark: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!--! Font Awesome Pro 7.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2025 Fonticons, Inc. --><path fill="currentColor" d="M55.1 73.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L147.2 256 9.9 393.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L192.5 301.3 329.9 438.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L237.8 256 375.1 118.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L192.5 210.7 55.1 73.4z"/></svg>`,
},
//
// Regular variant
//
regular: {
'circle-question': `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--!Font Awesome Free v7.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M528 320C528 205.1 434.9 112 320 112C205.1 112 112 205.1 112 320C112 434.9 205.1 528 320 528C434.9 528 528 434.9 528 320zM64 320C64 178.6 178.6 64 320 64C461.4 64 576 178.6 576 320C576 461.4 461.4 576 320 576C178.6 576 64 461.4 64 320zM320 240C302.3 240 288 254.3 288 272C288 285.3 277.3 296 264 296C250.7 296 240 285.3 240 272C240 227.8 275.8 192 320 192C364.2 192 400 227.8 400 272C400 319.2 364 339.2 344 346.5L344 350.3C344 363.6 333.3 374.3 320 374.3C306.7 374.3 296 363.6 296 350.3L296 342.2C296 321.7 310.8 307 326.1 302C332.5 299.9 339.3 296.5 344.3 291.7C348.6 287.5 352 281.7 352 272.1C352 254.4 337.7 240.1 320 240.1zM288 432C288 414.3 302.3 400 320 400C337.7 400 352 414.3 352 432C352 449.7 337.7 464 320 464C302.3 464 288 449.7 288 432z"/></svg>`,
'circle-xmark': `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--!Font Awesome Free v7.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M320 112C434.9 112 528 205.1 528 320C528 434.9 434.9 528 320 528C205.1 528 112 434.9 112 320C112 205.1 205.1 112 320 112zM320 576C461.4 576 576 461.4 576 320C576 178.6 461.4 64 320 64C178.6 64 64 178.6 64 320C64 461.4 178.6 576 320 576zM231 231C221.6 240.4 221.6 255.6 231 264.9L286 319.9L231 374.9C221.6 384.3 221.6 399.5 231 408.8C240.4 418.1 255.6 418.2 264.9 408.8L319.9 353.8L374.9 408.8C384.3 418.2 399.5 418.2 408.8 408.8C418.1 399.4 418.2 384.2 408.8 374.9L353.8 319.9L408.8 264.9C418.2 255.5 418.2 240.3 408.8 231C399.4 221.7 384.2 221.6 374.9 231L319.9 286L264.9 231C255.5 221.6 240.3 221.6 231 231z"/></svg>`,
copy: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--!Font Awesome Free v7.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M480 400L288 400C279.2 400 272 392.8 272 384L272 128C272 119.2 279.2 112 288 112L421.5 112C425.7 112 429.8 113.7 432.8 116.7L491.3 175.2C494.3 178.2 496 182.3 496 186.5L496 384C496 392.8 488.8 400 480 400zM288 448L480 448C515.3 448 544 419.3 544 384L544 186.5C544 169.5 537.3 153.2 525.3 141.2L466.7 82.7C454.7 70.7 438.5 64 421.5 64L288 64C252.7 64 224 92.7 224 128L224 384C224 419.3 252.7 448 288 448zM160 192C124.7 192 96 220.7 96 256L96 512C96 547.3 124.7 576 160 576L352 576C387.3 576 416 547.3 416 512L416 496L368 496L368 512C368 520.8 360.8 528 352 528L160 528C151.2 528 144 520.8 144 512L144 256C144 247.2 151.2 240 160 240L176 240L176 192L160 192z"/></svg>`,
eye: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--!Font Awesome Free v7.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M320 144C254.8 144 201.2 173.6 160.1 211.7C121.6 247.5 95 290 81.4 320C95 350 121.6 392.5 160.1 428.3C201.2 466.4 254.8 496 320 496C385.2 496 438.8 466.4 479.9 428.3C518.4 392.5 545 350 558.6 320C545 290 518.4 247.5 479.9 211.7C438.8 173.6 385.2 144 320 144zM127.4 176.6C174.5 132.8 239.2 96 320 96C400.8 96 465.5 132.8 512.6 176.6C559.4 220.1 590.7 272 605.6 307.7C608.9 315.6 608.9 324.4 605.6 332.3C590.7 368 559.4 420 512.6 463.4C465.5 507.1 400.8 544 320 544C239.2 544 174.5 507.2 127.4 463.4C80.6 419.9 49.3 368 34.4 332.3C31.1 324.4 31.1 315.6 34.4 307.7C49.3 272 80.6 220 127.4 176.6zM320 400C364.2 400 400 364.2 400 320C400 290.4 383.9 264.5 360 250.7C358.6 310.4 310.4 358.6 250.7 360C264.5 383.9 290.4 400 320 400zM240.4 311.6C242.9 311.9 245.4 312 248 312C283.3 312 312 283.3 312 248C312 245.4 311.8 242.9 311.6 240.4C274.2 244.3 244.4 274.1 240.5 311.5zM286 196.6C296.8 193.6 308.2 192.1 319.9 192.1C328.7 192.1 337.4 193 345.7 194.7C346 194.8 346.2 194.8 346.5 194.9C404.4 207.1 447.9 258.6 447.9 320.1C447.9 390.8 390.6 448.1 319.9 448.1C258.3 448.1 206.9 404.6 194.7 346.7C192.9 338.1 191.9 329.2 191.9 320.1C191.9 309.1 193.3 298.3 195.9 288.1C196.1 287.4 196.2 286.8 196.4 286.2C208.3 242.8 242.5 208.6 285.9 196.7z"/></svg>`,
'eye-slash': `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--!Font Awesome Free v7.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M73 39.1C63.6 29.7 48.4 29.7 39.1 39.1C29.8 48.5 29.7 63.7 39 73.1L567 601.1C576.4 610.5 591.6 610.5 600.9 601.1C610.2 591.7 610.3 576.5 600.9 567.2L504.5 470.8C507.2 468.4 509.9 466 512.5 463.6C559.3 420.1 590.6 368.2 605.5 332.5C608.8 324.6 608.8 315.8 605.5 307.9C590.6 272.2 559.3 220.2 512.5 176.8C465.4 133.1 400.7 96.2 319.9 96.2C263.1 96.2 214.3 114.4 173.9 140.4L73 39.1zM208.9 175.1C241 156.2 278.1 144 320 144C385.2 144 438.8 173.6 479.9 211.7C518.4 247.4 545 290 558.5 320C544.9 350 518.3 392.5 479.9 428.3C476.8 431.1 473.7 433.9 470.5 436.7L425.8 392C439.8 371.5 448 346.7 448 320C448 249.3 390.7 192 320 192C293.3 192 268.5 200.2 248 214.2L208.9 175.1zM390.9 357.1L282.9 249.1C294 243.3 306.6 240 320 240C364.2 240 400 275.8 400 320C400 333.4 396.7 346 390.9 357.1zM135.4 237.2L101.4 203.2C68.8 240 46.4 279 34.5 307.7C31.2 315.6 31.2 324.4 34.5 332.3C49.4 368 80.7 420 127.5 463.4C174.6 507.1 239.3 544 320.1 544C357.4 544 391.3 536.1 421.6 523.4L384.2 486C364.2 492.4 342.8 496 320 496C254.8 496 201.2 466.4 160.1 428.3C121.6 392.6 95 350 81.5 320C91.9 296.9 110.1 266.4 135.5 237.2z"/></svg>`,
star: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--!Font Awesome Free v7.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M320.1 32C329.1 32 337.4 37.1 341.5 45.1L415 189.3L574.9 214.7C583.8 216.1 591.2 222.4 594 231C596.8 239.6 594.5 249 588.2 255.4L473.7 369.9L499 529.8C500.4 538.7 496.7 547.7 489.4 553C482.1 558.3 472.4 559.1 464.4 555L320.1 481.6L175.8 555C167.8 559.1 158.1 558.3 150.8 553C143.5 547.7 139.8 538.8 141.2 529.8L166.4 369.9L52 255.4C45.6 249 43.4 239.6 46.2 231C49 222.4 56.3 216.1 65.3 214.7L225.2 189.3L298.8 45.1C302.9 37.1 311.2 32 320.2 32zM320.1 108.8L262.3 222C258.8 228.8 252.3 233.6 244.7 234.8L119.2 254.8L209 344.7C214.4 350.1 216.9 357.8 215.7 365.4L195.9 490.9L309.2 433.3C316 429.8 324.1 429.8 331 433.3L444.3 490.9L424.5 365.4C423.3 357.8 425.8 350.1 431.2 344.7L521 254.8L395.5 234.8C387.9 233.6 381.4 228.8 377.9 222L320.1 108.8z"/></svg>`,
'circle-question': `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 7.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2025 Fonticons, Inc. --><path fill="currentColor" d="M464 256a208 208 0 1 0 -416 0 208 208 0 1 0 416 0zM0 256a256 256 0 1 1 512 0 256 256 0 1 1 -512 0zm256-80c-17.7 0-32 14.3-32 32 0 13.3-10.7 24-24 24s-24-10.7-24-24c0-44.2 35.8-80 80-80s80 35.8 80 80c0 47.2-36 67.2-56 74.5l0 3.8c0 13.3-10.7 24-24 24s-24-10.7-24-24l0-8.1c0-20.5 14.8-35.2 30.1-40.2 6.4-2.1 13.2-5.5 18.2-10.3 4.3-4.2 7.7-10 7.7-19.6 0-17.7-14.3-32-32-32zM224 368a32 32 0 1 1 64 0 32 32 0 1 1 -64 0z"/></svg>`,
'circle-xmark': `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 7.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2025 Fonticons, Inc. --><path fill="currentColor" d="M256 48a208 208 0 1 1 0 416 208 208 0 1 1 0-416zm0 464a256 256 0 1 0 0-512 256 256 0 1 0 0 512zM167 167c-9.4 9.4-9.4 24.6 0 33.9l55 55-55 55c-9.4 9.4-9.4 24.6 0 33.9s24.6 9.4 33.9 0l55-55 55 55c9.4 9.4 24.6 9.4 33.9 0s9.4-24.6 0-33.9l-55-55 55-55c9.4-9.4 9.4-24.6 0-33.9s-24.6-9.4-33.9 0l-55 55-55-55c-9.4-9.4-24.6-9.4-33.9 0z"/></svg>`,
copy: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Pro 7.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2025 Fonticons, Inc. --><path fill="currentColor" d="M384 336l-192 0c-8.8 0-16-7.2-16-16l0-256c0-8.8 7.2-16 16-16l133.5 0c4.2 0 8.3 1.7 11.3 4.7l58.5 58.5c3 3 4.7 7.1 4.7 11.3L400 320c0 8.8-7.2 16-16 16zM192 384l192 0c35.3 0 64-28.7 64-64l0-197.5c0-17-6.7-33.3-18.7-45.3L370.7 18.7C358.7 6.7 342.5 0 325.5 0L192 0c-35.3 0-64 28.7-64 64l0 256c0 35.3 28.7 64 64 64zM64 128c-35.3 0-64 28.7-64 64L0 448c0 35.3 28.7 64 64 64l192 0c35.3 0 64-28.7 64-64l0-16-48 0 0 16c0 8.8-7.2 16-16 16L64 464c-8.8 0-16-7.2-16-16l0-256c0-8.8 7.2-16 16-16l16 0 0-48-16 0z"/></svg>`,
eye: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!--! Font Awesome Pro 7.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2025 Fonticons, Inc. --><path fill="currentColor" d="M288 80C222.8 80 169.2 109.6 128.1 147.7 89.6 183.5 63 226 49.4 256 63 286 89.6 328.5 128.1 364.3 169.2 402.4 222.8 432 288 432s118.8-29.6 159.9-67.7C486.4 328.5 513 286 526.6 256 513 226 486.4 183.5 447.9 147.7 406.8 109.6 353.2 80 288 80zM95.4 112.6C142.5 68.8 207.2 32 288 32s145.5 36.8 192.6 80.6c46.8 43.5 78.1 95.4 93 131.1 3.3 7.9 3.3 16.7 0 24.6-14.9 35.7-46.2 87.7-93 131.1-47.1 43.7-111.8 80.6-192.6 80.6S142.5 443.2 95.4 399.4c-46.8-43.5-78.1-95.4-93-131.1-3.3-7.9-3.3-16.7 0-24.6 14.9-35.7 46.2-87.7 93-131.1zM288 336c44.2 0 80-35.8 80-80 0-29.6-16.1-55.5-40-69.3-1.4 59.7-49.6 107.9-109.3 109.3 13.8 23.9 39.7 40 69.3 40zm-79.6-88.4c2.5 .3 5 .4 7.6 .4 35.3 0 64-28.7 64-64 0-2.6-.2-5.1-.4-7.6-37.4 3.9-67.2 33.7-71.1 71.1zm45.6-115c10.8-3 22.2-4.5 33.9-4.5 8.8 0 17.5 .9 25.8 2.6 .3 .1 .5 .1 .8 .2 57.9 12.2 101.4 63.7 101.4 125.2 0 70.7-57.3 128-128 128-61.6 0-113-43.5-125.2-101.4-1.8-8.6-2.8-17.5-2.8-26.6 0-11 1.4-21.8 4-32 .2-.7 .3-1.3 .5-1.9 11.9-43.4 46.1-77.6 89.5-89.5z"/></svg>`,
'eye-slash': `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!--! Font Awesome Pro 7.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2025 Fonticons, Inc. --><path fill="currentColor" d="M41-24.9c-9.4-9.4-24.6-9.4-33.9 0S-2.3-.3 7 9.1l528 528c9.4 9.4 24.6 9.4 33.9 0s9.4-24.6 0-33.9l-96.4-96.4c2.7-2.4 5.4-4.8 8-7.2 46.8-43.5 78.1-95.4 93-131.1 3.3-7.9 3.3-16.7 0-24.6-14.9-35.7-46.2-87.7-93-131.1-47.1-43.7-111.8-80.6-192.6-80.6-56.8 0-105.6 18.2-146 44.2L41-24.9zM176.9 111.1c32.1-18.9 69.2-31.1 111.1-31.1 65.2 0 118.8 29.6 159.9 67.7 38.5 35.7 65.1 78.3 78.6 108.3-13.6 30-40.2 72.5-78.6 108.3-3.1 2.8-6.2 5.6-9.4 8.4L393.8 328c14-20.5 22.2-45.3 22.2-72 0-70.7-57.3-128-128-128-26.7 0-51.5 8.2-72 22.2l-39.1-39.1zm182 182l-108-108c11.1-5.8 23.7-9.1 37.1-9.1 44.2 0 80 35.8 80 80 0 13.4-3.3 26-9.1 37.1zM103.4 173.2l-34-34c-32.6 36.8-55 75.8-66.9 104.5-3.3 7.9-3.3 16.7 0 24.6 14.9 35.7 46.2 87.7 93 131.1 47.1 43.7 111.8 80.6 192.6 80.6 37.3 0 71.2-7.9 101.5-20.6L352.2 422c-20 6.4-41.4 10-64.2 10-65.2 0-118.8-29.6-159.9-67.7-38.5-35.7-65.1-78.3-78.6-108.3 10.4-23.1 28.6-53.6 54-82.8z"/></svg>`,
star: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!--! Font Awesome Pro 7.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2025 Fonticons, Inc. --><path fill="currentColor" d="M288.1-32c9 0 17.3 5.1 21.4 13.1L383 125.3 542.9 150.7c8.9 1.4 16.3 7.7 19.1 16.3s.5 18-5.8 24.4L441.7 305.9 467 465.8c1.4 8.9-2.3 17.9-9.6 23.2s-17 6.1-25 2L288.1 417.6 143.8 491c-8 4.1-17.7 3.3-25-2s-11-14.2-9.6-23.2L134.4 305.9 20 191.4c-6.4-6.4-8.6-15.8-5.8-24.4s10.1-14.9 19.1-16.3l159.9-25.4 73.6-144.2c4.1-8 12.4-13.1 21.4-13.1zm0 76.8L230.3 158c-3.5 6.8-10 11.6-17.6 12.8l-125.5 20 89.8 89.9c5.4 5.4 7.9 13.1 6.7 20.7l-19.8 125.5 113.3-57.6c6.8-3.5 14.9-3.5 21.8 0l113.3 57.6-19.8-125.5c-1.2-7.6 1.3-15.3 6.7-20.7l89.8-89.9-125.5-20c-7.6-1.2-14.1-6-17.6-12.8L288.1 44.8z"/></svg>`,
},
};

View File

@@ -174,6 +174,7 @@ textarea {
padding: 0;
transition: var(--wa-transition-normal) color;
cursor: pointer;
margin-inline-start: var(--wa-form-control-padding-inline);
@media (hover: hover) {
&:hover {

View File

@@ -372,7 +372,7 @@ export default class WaInput extends WebAwesomeFormAssociatedElement {
min=${ifDefined(this.min)}
max=${ifDefined(this.max)}
step=${ifDefined(this.step as number)}
.value=${live(this.value || '')}
.value=${live(this.value ?? '')}
autocapitalize=${ifDefined(this.autocapitalize)}
autocomplete=${ifDefined(this.autocomplete)}
autocorrect=${ifDefined(this.autocorrect)}

View File

@@ -0,0 +1,3 @@
:host {
display: contents;
}

View File

@@ -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;
});
});

View File

@@ -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;
}
}

View File

@@ -1,250 +0,0 @@
:host {
display: block;
background-color: var(--wa-color-surface-default);
box-sizing: border-box;
height: 100%;
--menu-width: auto;
--main-width: 1fr;
--aside-width: auto;
--banner-height: 0px;
--header-height: 0px;
--subheader-height: 0px;
--scroll-margin-top: calc(var(--header-height, 0px) + var(--subheader-height, 0px) + 0.5em);
}
slot[name]:not([name='skip-to-content'], [name='navigation-toggle'])::slotted(*) {
display: flex;
background-color: var(--wa-color-surface-default);
}
::slotted([slot='banner']) {
align-items: center;
justify-content: center;
gap: var(--wa-space-m);
padding: var(--wa-space-xs) var(--wa-space-m);
}
::slotted([slot='header']) {
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
gap: var(--wa-space-m);
padding: var(--wa-space-m);
flex: auto;
}
::slotted([slot='subheader']) {
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
gap: var(--wa-space-m);
padding: var(--wa-space-xs) var(--wa-space-m);
}
::slotted([slot*='navigation']),
::slotted([slot='menu']),
::slotted([slot='aside']) {
flex-direction: column;
gap: var(--wa-space-m);
padding: var(--wa-space-m);
}
::slotted([slot='main-header']) {
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
gap: var(--wa-space-m);
padding: var(--wa-space-m) var(--wa-space-3xl);
}
slot:not([name]) {
/* See #331 */
&::slotted(main),
&::slotted(section) {
padding: var(--wa-space-3xl);
}
}
::slotted([slot='main-footer']),
::slotted([slot='footer']) {
align-items: start;
justify-content: space-between;
flex-wrap: wrap;
gap: var(--wa-space-m);
padding: var(--wa-space-3xl);
}
:host([disable-sticky~='banner']) :is([part~='header'], [part~='subheader']) {
--banner-height: 0px !important;
}
:host([disable-sticky~='header']) [part~='subheader'] {
--header-height: 0px !important;
}
/* Nothing else depends on subheader-height. */
:host([disable-sticky~='subheader']) {
}
:host([disable-sticky~='aside']) [part~='aside'],
:host([disable-sticky~='menu']) [part~='menu'] {
height: unset;
max-height: unset;
}
:host([disable-sticky~='banner']) [part~='banner'],
:host([disable-sticky~='header']) [part~='header'],
:host([disable-sticky~='subheader']) [part~='subheader'],
:host([disable-sticky~='aside']) [part~='aside'],
:host([disable-sticky~='menu']) [part~='menu'] {
position: static;
overflow: unset;
z-index: unset;
}
:host([disable-sticky~='aside']) [part~='aside'],
:host([disable-sticky~='menu']) [part~='menu'] {
height: auto;
max-height: auto;
}
[part~='base'] {
min-height: 100%;
display: grid;
grid-template-rows: repeat(3, minmax(0, auto)) minmax(0, 1fr) minmax(0, auto);
grid-template-columns: 100%;
width: 100%;
grid-template-areas:
'banner'
'header'
'subheader'
'body'
'footer';
}
/* Grid areas */
[part~='banner'] {
grid-area: banner;
}
[part~='header'] {
grid-area: header;
}
[part~='subheader'] {
grid-area: subheader;
}
[part~='menu'] {
grid-area: menu;
}
[part~='body'] {
grid-area: body;
}
[part~='main'] {
grid-area: main;
}
[part~='aside'] {
grid-area: aside;
}
[part~='footer'] {
grid-area: footer;
}
/* Z-indexes */
[part~='banner'],
[part~='header'],
[part~='subheader'] {
position: sticky;
z-index: 5;
}
[part~='banner'] {
top: 0px;
}
[part~='header'] {
top: var(--banner-height);
/** Make the header flex so that you don't unexpectedly have the default toggle button appearing above a slotted div because block elements are fun. */
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
}
[part~='subheader'] {
top: calc(var(--header-height) + var(--banner-height));
}
[part~='body'] {
display: grid;
height: 100%;
align-items: flex-start;
grid-template-columns: minmax(0, var(--menu-width)) minmax(0, var(--main-width)) minmax(0, var(--aside-width));
grid-template-rows: minmax(0, 1fr);
grid-template-areas: 'menu main aside';
}
[part~='main'] {
display: grid;
min-height: 100%;
grid-template-columns: minmax(0, 1fr);
grid-template-rows: minmax(0, auto) minmax(0, 1fr) minmax(0, auto);
grid-template-areas:
'main-header'
'main-content'
'main-footer';
}
[part~='main-header'] {
grid-area: main-header;
}
[part~='main-content'] {
grid-area: main-content;
}
[part~='main-footer'] {
grid-area: main-footer;
}
.skip-to-content {
position: absolute;
top: var(--wa-space-m);
left: var(--wa-space-m);
z-index: 6;
border-radius: var(--wa-corners-1x);
background-color: var(--wa-color-surface-default);
color: var(--wa-color-text-link);
text-decoration: none;
padding: var(--wa-space-s) var(--wa-space-m);
box-shadow: var(--wa-shadow-l);
outline: var(--wa-focus-ring);
outline-offset: var(--wa-focus-ring-offset);
}
[part~='menu'],
[part~='aside'] {
position: sticky;
top: calc(var(--banner-height) + var(--header-height) + var(--subheader-height));
z-index: 4;
height: calc(100dvh - var(--header-height) - var(--banner-height) - var(--subheader-height));
max-height: calc(100dvh - var(--header-height) - var(--banner-height) - var(--subheader-height));
overflow: auto;
}
[part~='navigation'] {
height: 100%;
display: grid;
grid-template-columns: minmax(0, 1fr);
grid-template-rows: minmax(0, auto) minmax(0, 1fr) minmax(0, auto);
}
[part~='drawer']::part(dialog) {
background-color: var(--wa-color-surface-default);
}
/* Set these on the slot because we don't always control the navigation-toggle since that may be slotted. */
slot[name~='navigation-toggle'],
:host([disable-navigation-toggle]) slot[name~='navigation-toggle'] {
display: none;
}
/* Sometimes the media query in the viewport is stubborn in iframes. This is an extra check to make it behave properly. */
:host(:not([disable-navigation-toggle])[view='mobile']) slot[name~='navigation-toggle'] {
display: contents;
}
[part~='navigation-toggle'] {
/* Use only a margin-inline-start because the slotted header is expected to have default padding
so it looks really awkward if this sets a margin-inline-end and the slotted header has a padding-inline-start. */
margin-inline-start: var(--wa-space-m);
}

View File

@@ -1,11 +0,0 @@
export default (breakpoint: string = '768px') => `
@media screen and (width < ${breakpoint}) {
[part~='navigation'] {
display: none;
}
:host(:not([disable-navigation-toggle])) slot[name~='navigation-toggle'] {
display: contents;
}
}
`;

View File

@@ -1,15 +0,0 @@
import { expect } from '@open-wc/testing';
import { html } from 'lit';
import { fixtures } from '../../internal/test/fixture.js';
describe('<wa-page>', () => {
for (const fixture of fixtures) {
describe(`with "${fixture.type}" rendering`, () => {
it('should render a component', async () => {
const el = await fixture(html` <wa-page></wa-page> `);
expect(el).to.exist;
});
});
}
});

View File

@@ -1,455 +0,0 @@
import type { PropertyValues } from 'lit';
import { html, isServer } from 'lit';
import { customElement, property, query } from 'lit/decorators.js';
import { live } from 'lit/directives/live.js';
import { unsafeHTML } from 'lit/directives/unsafe-html.js';
import WebAwesomeElement from '../../internal/webawesome-element.js';
import visuallyHidden from '../../styles/utilities/visually-hidden.css';
import '../button/button.js';
import '../drawer/drawer.js';
import type WaDrawer from '../drawer/drawer.js';
import '../icon/icon.js';
import styles from './page.css';
import mobileStyles from './page.mobile.styles.js';
if (typeof ResizeObserver === 'undefined') {
globalThis.ResizeObserver = class {
// eslint-disable-next-line
constructor(..._args: ConstructorParameters<typeof ResizeObserver>) {}
// eslint-disable-next-line
observe(..._args: Parameters<ResizeObserver['observe']>) {}
// eslint-disable-next-line
unobserve(..._args: Parameters<ResizeObserver['unobserve']>) {}
// eslint-disable-next-line
disconnect(..._args: Parameters<ResizeObserver['disconnect']>) {}
};
}
//
// TODO - the toPx and toLength functions aren't used anywhere else, and they're not named or documented well enough to
// abstract into a utility as-is.
//
/** Converts a non-pixel value to a pixel value. */
function toPx(value: string | number, element: HTMLElement | SVGElement = document.documentElement): number {
if (!Number.isNaN(Number(value))) {
return Number(value);
}
// If CSS.registerProperty isn't supported, try to parse as-is
if (!window.CSS || !CSS.registerProperty) {
if (typeof value === 'string' && value.endsWith('px')) {
return parseFloat(value);
}
return Number(value) || 0;
}
const resolver = '--wa-length-resolver';
// Register the property if not already done
if (!CSS.registerProperty.toString().includes(resolver)) {
try {
CSS.registerProperty({
name: resolver,
syntax: '<length>',
inherits: false,
initialValue: '0px',
});
} catch (e) {
// Property might already be registered
}
}
const previousValue = element.style.getPropertyValue(resolver);
element.style.setProperty(resolver, value as string);
const computedValue = getComputedStyle(element)?.getPropertyValue(resolver);
element.style.setProperty(resolver, previousValue);
if (computedValue?.endsWith('px')) {
return parseFloat(computedValue);
}
return Number(computedValue) || 0;
}
/** Converts a number or string to a CSS px value. Not used anywhere else, so consolidated here for the time being. */
function toLength(px: number | string): string {
return Number.isNaN(Number(px)) ? (px as string) : `${px}px`;
}
/**
* @summary Pages offer an easy way to scaffold entire page layouts using minimal markup.
* @documentation https://webawesome.com/docs/components/page
* @status experimental
* @since 3.0
*
* @slot - The page's main content.
* @slot banner - The banner that gets display above the header. The banner will not be shown if no content is provided.
* @slot header - The header to display at the top of the page. If a banner is present, the header will appear below the banner. The header will not be shown if there is no content.
* @slot subheader - A subheader to display below the `header`. This is a good place to put things like breadcrumbs.
* @slot menu - The left side of the page. If you slot an element in here, you will override the default `navigation` slot and will be handling navigation on your own. This also will not disable the fallback behavior of the navigation button. This section "sticks" to the top as the page scrolls.
* @slot navigation-header - The header for a navigation area. On mobile this will be the header for `<wa-drawer>`.
* @slot navigation - The main content to display in the navigation area. This is displayed on the left side of the page, if `menu` is not used. This section "sticks" to the top as the page scrolls.
* @slot navigation-footer - The footer for a navigation area. On mobile this will be the footer for `<wa-drawer>`.
* @slot navigation-toggle - Use this slot to slot in your own button + icon for toggling the navigation drawer. By default it is a `<wa-button>` + a 3 bars `<wa-icon>`
* @slot navigation-toggle-icon - Use this to slot in your own icon for toggling the navigation drawer. By default it is 3 bars `<wa-icon>`.
* @slot main-header - Header to display inline above the main content.
* @slot main-footer - Footer to display inline below the main content.
* @slot aside - Content to be shown on the right side of the page. Typically contains a table of contents, ads, etc. This section "sticks" to the top as the page scrolls.
* @slot skip-to-content - The "skip to content" slot. You can override this If you would like to override the `Skip to content` button and add additional "Skip to X", they can be inserted here.
* @slot footer - The content to display in the footer. This is always displayed underneath the viewport so will always make the page "scrollable".
*
* @csspart base - The component's base wrapper.
* @csspart banner - The banner to show above header.
* @csspart header - The header, usually for top level navigation / branding.
* @csspart subheader - Shown below the header, usually intended for things like breadcrumbs and other page level navigation.
* @csspart body - The wrapper around menu, main, and aside.
* @csspart menu - The left hand side of the page. Generally intended for navigation.
* @csspart navigation - The `<nav>` that wraps the navigation slots on desktop viewports.
* @csspart navigation-header - The header for a navigation area. On mobile this will be the header for `<wa-drawer>`.
* @csspart navigation-footer - The footer for a navigation area. On mobile this will be the footer for `<wa-drawer>`.
* @csspart navigation-toggle - The default `<wa-button>` that will toggle the `<wa-drawer>` for mobile viewports.
* @csspart navigation-toggle-icon - The default `<wa-icon>` displayed inside of the navigation-toggle button.
* @csspart main-header - The header above main content.
* @csspart main-content - The main content.
* @csspart main-footer - The footer below main content.
* @csspart aside - The right hand side of the page. Used for things like table of contents, ads, etc.
* @csspart skip-links - Wrapper around skip-link
* @csspart skip-link - The "skip to main content" link
* @csspart footer - The footer of the page. This is always below the initial viewport size.
* @csspart dialog-wrapper - A wrapper around elements such as dialogs or other modal-like elements.
*
* @cssproperty [--menu-width=auto] - The width of the page's "menu" section.
* @cssproperty [--main-width=1fr] - The width of the page's "main" section.
* @cssproperty [--aside-width=auto] - The wide of the page's "aside" section.
* @cssproperty [--banner-height=0px] - The height of the banner. This gets calculated when the page initializes. If the height is known, you can set it here to prevent shifting when the page loads.
* @cssproperty [--header-height=0px] - The height of the header. This gets calculated when the page initializes. If the height is known, you can set it here to prevent shifting when the page loads.
* @cssproperty [--subheader-height=0px] - The height of the subheader. This gets calculated when the page initializes. If the height is known, you can set it here to prevent shifting when the page loads.
*/
@customElement('wa-page')
export default class WaPage extends WebAwesomeElement {
static css = [visuallyHidden, styles];
private headerResizeObserver = this.slotResizeObserver('header');
private subheaderResizeObserver = this.slotResizeObserver('subheader');
private bannerResizeObserver = this.slotResizeObserver('banner');
private footerResizeObserver = this.slotResizeObserver('footer');
private slotResizeObserver(slot: string) {
return new ResizeObserver(entries => {
for (const entry of entries) {
if (entry.contentBoxSize) {
const contentBoxSize = entry.borderBoxSize[0];
this.style.setProperty(`--${slot}-height`, `${contentBoxSize.blockSize}px`);
}
}
});
}
private handleNavigationToggle = (e: Event) => {
// Don't toggle the nav when we're in desktop mode
if (this.view === 'desktop') {
// Just in case, try to hide the navigation.
this.hideNavigation();
return;
}
const path = e.composedPath();
const navigationToggleSlot = this.navigationToggleSlot;
if (
path.find((el: Element) => {
return (
el.hasAttribute?.('data-toggle-nav') ||
el.assignedSlot === navigationToggleSlot ||
el === navigationToggleSlot
);
})
) {
e.preventDefault();
this.toggleNavigation();
}
};
@query("[part~='header']") header: HTMLElement;
@query("[part~='subheader']") subheader: HTMLElement;
@query("[part~='footer']") footer: HTMLElement;
@query("[part~='banner']") banner: HTMLElement;
@query("[part~='drawer']") navigationDrawer: WaDrawer;
@query("slot[name~='navigation-toggle']") navigationToggleSlot: HTMLSlotElement;
/**
* The view is a reflection of the "mobileBreakpoint", when the page is larger than the `mobile-breakpoint` (768px by
* default), it is considered to be a "desktop" view. The view is merely a way to distinguish when to show/hide the
* navigation. You can use additional media queries to make other adjustments to content as necessary.
* The default is "desktop" because the "mobile navigation drawer" isn't accessible via SSR due to drawer requiring JS.
*/
@property({ attribute: 'view', reflect: true }) view: 'mobile' | 'desktop' = 'desktop';
/**
* Whether or not the navigation drawer is open. Note, the navigation drawer is only "open" on mobile views.
*/
@property({ attribute: 'nav-open', reflect: true, type: Boolean }) navOpen = false;
/**
* At what page width to hide the "navigation" slot and collapse into a hamburger button.
* Accepts both numbers (interpreted as px) and CSS lengths (e.g. `50em`), which are resolved based on the root element.
*/
@property({ attribute: 'mobile-breakpoint', type: String })
mobileBreakpoint = '768px';
/**
* Where to place the navigation when in the mobile viewport.
*/
@property({ attribute: 'navigation-placement', reflect: true }) navigationPlacement: 'start' | 'end' = 'start';
/**
* Determines whether or not to hide the default hamburger button.
* This will automatically flip to "true" if you add an element with `data-toggle-nav` anywhere in the element light DOM.
* Generally this will be set for you and you don't need to do anything, unless you're using SSR, in which case you should set this manually for initial page loads.
*/
@property({ attribute: 'disable-navigation-toggle', reflect: true, type: Boolean }) disableNavigationToggle: boolean =
false;
pageResizeObserver = new ResizeObserver(entries => {
for (const entry of entries) {
if (entry.contentBoxSize) {
const contentBoxSize = entry.borderBoxSize[0];
const pageWidth = contentBoxSize.inlineSize;
const oldView = this.view;
if (pageWidth >= toPx(this.mobileBreakpoint)) {
this.view = 'desktop';
} else {
this.view = 'mobile';
}
this.requestUpdate('view', oldView);
}
}
});
protected update(changedProperties: PropertyValues<this>): void {
if (changedProperties.has('view')) {
this.hideNavigation();
}
super.update(changedProperties);
}
constructor() {
super();
if (!isServer) {
this.addEventListener('click', this.handleNavigationToggle);
}
}
connectedCallback() {
super.connectedCallback();
this.pageResizeObserver.observe(this);
const navQuery = ":not([slot='toggle-navigation']) [data-toggle-nav]";
// check once on initial connect
// eslint-disable-next-line
this.disableNavigationToggle = Boolean(this.querySelector(navQuery));
setTimeout(() => {
this.headerResizeObserver.observe(this.header);
this.subheaderResizeObserver.observe(this.subheader);
this.bannerResizeObserver.observe(this.banner);
this.footerResizeObserver.observe(this.footer);
// Check again when the element updates
// eslint-disable-next-line
this.disableNavigationToggle = Boolean(this.querySelector(navQuery));
});
}
firstUpdated() {
// If the user provides a #main-content id, it should be present in the default slot and the "skip to
// content" link will point to it. If not, we'll prepend an empty element for them so things just work.
if (!document.getElementById('main-content')) {
const div = document.createElement('div');
div.id = 'main-content';
div.slot = 'skip-to-content-target';
this.prepend(div);
}
}
disconnectedCallback() {
super.disconnectedCallback();
this.pageResizeObserver.unobserve(this);
this.headerResizeObserver.unobserve(this.header);
this.subheaderResizeObserver.unobserve(this.subheader);
this.footerResizeObserver.unobserve(this.footer);
this.bannerResizeObserver.unobserve(this.banner);
}
/**
* Shows the mobile navigation drawer
*/
showNavigation() {
this.navOpen = true;
}
/**
* Hides the mobile navigation drawer
*/
hideNavigation() {
this.navOpen = false;
}
/**
* Toggles the mobile navigation drawer
*/
toggleNavigation() {
this.navOpen = !this.navOpen;
}
render() {
return html`
<a href="#main-content" part="skip-to-content" class="wa-visually-hidden">
<slot name="skip-to-content">Skip to content</slot>
</a>
<!-- unsafeHTML needed for SSR until this is solved: https://github.com/lit/lit/issues/4696 -->
${unsafeHTML(`
<style id="mobile-styles">
${mobileStyles(toLength(this.mobileBreakpoint))}
</style>
`)}
<div class="base" part="base">
<div class="banner" part="banner">
<slot name="banner"></slot>
</div>
<div class="header" part="header">
<slot name="navigation-toggle">
<wa-button part="navigation-toggle" size="small" appearance="plain" variant="neutral">
<slot name="navigation-toggle-icon">
<wa-icon name="bars" part="navigation-toggle-icon" label="Toggle navigation drawer"></wa-icon>
</slot>
</wa-button>
</slot>
<slot name="header"></slot>
</div>
<div class="subheader" part="subheader">
<slot name="subheader"></slot>
</div>
<div class="body" part="body">
<div class="menu" part="menu">
<slot name="menu">
<nav name="navigation" class="navigation" part="navigation navigation-desktop">
<!-- Add fallback divs so that CSS grid works properly. -->
<slot name="desktop-navigation-header">
<slot name=${this.view === 'desktop' ? 'navigation-header' : '___'}><div></div></slot>
</slot>
<slot name="desktop-navigation">
<slot name=${this.view === 'desktop' ? 'navigation' : '____'}><div></div></slot>
</slot>
<slot name="desktop-navigation-footer">
<slot name=${this.view === 'desktop' ? 'navigation-footer' : '___'}><div></div></slot>
</slot>
</nav>
</slot>
</div>
<div class="main" part="main">
<div class="main-header" part="main-header">
<slot name="main-header"></slot>
</div>
<div class="main-content" part="main-content">
<slot name="skip-to-content-target"></slot>
<slot></slot>
</div>
<div class="main-footer" part="main-footer">
<slot name="main-footer"></slot>
</div>
</div>
<div class="aside" part="aside">
<slot name="aside"></slot>
</div>
</div>
<div class="footer" part="footer">
<slot name="footer"></slot>
</div>
</div>
<wa-drawer
part="drawer"
placement=${this.navigationPlacement}
light-dismiss
?open=${live(this.navOpen)}
@wa-after-show=${() => (this.navOpen = this.navigationDrawer.open)}
@wa-after-hide=${() => (this.navOpen = this.navigationDrawer.open)}
exportparts="
dialog:drawer__dialog,
overlay:drawer__overlay,
panel:drawer__panel,
header:drawer__header,
header-actions:drawer__header-actions,
title:drawer__title,
close-button:drawer__close-button,
close-button__base:drawer__close-button__base,
body:drawer__body,
footer:drawer__footer
"
class="navigation-drawer"
>
<slot slot="label" part="navigation-header" name="mobile-navigation-header">
<slot name=${this.view === 'mobile' ? 'navigation-header' : '___'}></slot>
</slot>
<slot name="mobile-navigation">
<slot name=${this.view === 'mobile' ? 'navigation' : '____'}></slot>
</slot>
<slot name="mobile-navigation-footer">
<slot
part="navigation-footer"
slot="footer"
name=${this.view === 'mobile' ? 'navigation-footer' : '___'}
></slot>
</slot>
</wa-drawer>
`;
}
}
declare global {
interface HTMLElementTagNameMap {
'wa-page': WaPage;
}
}
//
// Append a supporting light DOM styles for <wa-page>
//
const stylesheet = new CSSStyleSheet();
stylesheet.replaceSync(`
:is(html, body):has(wa-page) {
min-height: 100%;
height: 100%;
padding: 0;
margin: 0;
}
/**
Because headers are sticky, this is needed to make sure page fragment anchors scroll down past the headers / subheaders and are visible.
IE: \`<a href="#id-for-h2">\` anchors.
*/
wa-page :is(*, *:after, *:before) {
scroll-margin-top: var(--scroll-margin-top);
}
wa-page[view='desktop'] [data-toggle-nav] {
display: none;
}
wa-page[view='mobile'] .wa-desktop-only, wa-page[view='desktop'] .wa-mobile-only {
display: none !important;
}
`);
document.adoptedStyleSheets = [...document.adoptedStyleSheets, stylesheet];

View File

@@ -18,31 +18,19 @@
margin-inline-start: var(--wa-form-control-required-content-offset);
}
.button-group {
display: flex;
}
[part~='form-control-input'] {
display: flex;
flex-direction: column;
flex-wrap: wrap;
gap: 0.75em;
gap: 0; /* Radios handle their own spacing */
}
/* Horizontal */
:host([orientation='horizontal']) [part~='form-control-input'] {
flex-direction: row;
gap: 1em;
}
/* Help text */
[part~='hint'] {
margin-block-start: 0.5em;
}
/* Radios have the "button" appearance */
:host fieldset.has-radio-buttons {
[part~='form-control-input'] {
gap: 0;
}
}

View File

@@ -57,8 +57,6 @@ export default class WaRadioGroup extends WebAwesomeFormAssociatedElement {
private readonly hasSlotController = new HasSlotController(this, 'hint', 'label');
@state() hasRadioButtons = false;
@query('slot:not([name])') defaultSlot: HTMLSlotElement;
/**
@@ -197,11 +195,9 @@ export default class WaRadioGroup extends WebAwesomeFormAssociatedElement {
private async syncRadioElements() {
const radios = this.getAllRadios();
let hasRadioButtons = false;
// Add data attributes to support styling
// Set positioning data attributes and properties
radios.forEach((radio, index) => {
if (radio.appearance === 'button') hasRadioButtons = true;
radio.setAttribute('size', this.size);
radio.toggleAttribute('data-wa-radio-horizontal', this.orientation !== 'vertical');
radio.toggleAttribute('data-wa-radio-vertical', this.orientation === 'vertical');
@@ -213,9 +209,6 @@ export default class WaRadioGroup extends WebAwesomeFormAssociatedElement {
(radio as WaRadio).forceDisabled = this.disabled;
});
// If at least one radio button exists, we assume it's a radio button group
this.hasRadioButtons = hasRadioButtons;
await Promise.all(
radios.map(async radio => {
await radio.updateComplete;
@@ -349,7 +342,6 @@ export default class WaRadioGroup extends WebAwesomeFormAssociatedElement {
'form-control': true,
'form-control-radio-group': true,
'form-control-has-label': hasLabel,
'has-radio-buttons': this.hasRadioButtons,
})}
role="radiogroup"
aria-labelledby="label"

View File

@@ -31,6 +31,41 @@
margin-block-start: 0.5em;
}
/* Default spacing for default appearance radios */
:host([appearance='default']) {
margin-block: 0.375em; /* Half of the original 0.75em gap on each side */
}
:host([appearance='default'][data-wa-radio-horizontal]) {
margin-block: 0;
margin-inline: 0.5em; /* Half of the original 1em gap on each side */
}
/* Remove margin from first/last items to prevent extra space */
:host([appearance='default'][data-wa-radio-first]) {
margin-block-start: 0;
margin-inline-start: 0;
}
:host([appearance='default'][data-wa-radio-last]) {
margin-block-end: 0;
margin-inline-end: 0;
}
/* Button appearance have no spacing, they get handled by the overlap margins below */
:host([appearance='button']) {
margin: 0;
align-items: center;
min-height: var(--wa-form-control-height);
background-color: var(--wa-color-surface-default);
border: var(--wa-form-control-border-width) var(--wa-form-control-border-style) var(--wa-form-control-border-color);
border-radius: var(--wa-border-radius-m);
padding: 0 var(--wa-form-control-padding-inline);
transition:
background-color var(--wa-transition-fast),
border-color var(--wa-transition-fast);
}
/* Default appearance */
:host([appearance='default']) {
.control {
@@ -66,6 +101,13 @@
}
}
/* Button appearance */
:host([appearance='button']) {
.control {
display: none;
}
}
/* Checked */
:host(:state(checked)) .control {
color: var(--checked-icon-color);
@@ -85,23 +127,6 @@
cursor: not-allowed;
}
/* Button appearance */
:host([appearance='button']) {
align-items: center;
min-height: var(--wa-form-control-height);
background-color: var(--wa-color-surface-default);
border: var(--wa-form-control-border-width) var(--wa-form-control-border-style) var(--wa-form-control-border-color);
border-radius: var(--wa-border-radius-m);
padding: 0 var(--wa-form-control-padding-inline);
transition:
background-color var(--wa-transition-fast),
border-color var(--wa-transition-fast);
.control {
display: none;
}
}
/* Horizontal grouping - remove inner border radius */
:host([appearance='button'][data-wa-radio-horizontal][data-wa-radio-inner]) {
border-radius: 0;
@@ -153,7 +178,7 @@
outline-offset: var(--wa-focus-ring-offset);
}
/* Remove inner borders and handle overlap */
/* Button overlap margins */
:host([appearance='button'][data-wa-radio-horizontal]:not([data-wa-radio-first])) {
margin-inline-start: calc(-1 * var(--wa-form-control-border-width));
}

View File

@@ -111,7 +111,6 @@ export default class WaRadio extends WebAwesomeFormAssociatedElement {
// We override `setValue` because we don't want to set form values from here. We want to do that in "RadioGroup" itself.
}
// Update the handleClick method (around line 75)
private handleClick = () => {
if (!this.disabled && !this.forceDisabled) {
this.checked = true;

View File

@@ -99,7 +99,8 @@ export default class WaRelativeTime extends WebAwesomeElement {
this.updateTimeout = setTimeout(() => this.requestUpdate(), nextInterval);
}
return html` <time datetime=${this.isoTime}>${this.relativeTime}</time> `;
// No whitespace before or after
return html`<time datetime=${this.isoTime}>${this.relativeTime}</time>`;
}
}

View File

@@ -493,6 +493,10 @@ export default class WaSelect extends WebAwesomeFormAssociatedElement {
this.displayInput.focus();
}
private handleComboboxClick(event: MouseEvent) {
event.preventDefault();
}
private handleComboboxMouseDown(event: MouseEvent) {
const path = event.composedPath();
const isButton = path.some(el => el instanceof Element && el.tagName.toLowerCase() === 'wa-button');
@@ -944,6 +948,7 @@ export default class WaSelect extends WebAwesomeFormAssociatedElement {
slot="anchor"
@keydown=${this.handleComboboxKeyDown}
@mousedown=${this.handleComboboxMouseDown}
@click=${this.handleComboboxClick}
>
<slot part="start" name="start" class="start"></slot>

View File

@@ -64,7 +64,7 @@ function syncCheckboxes(changedTreeItem: WaTreeItem, initialSync = false) {
*
* @csspart base - The component's base wrapper.
*
* @cssproperty [--indent-size=var(--wa-spacing-m)] - The size of the indentation for nested items.
* @cssproperty [--indent-size=var(--wa-space-m)] - The size of the indentation for nested items.
* @cssproperty [--indent-guide-color=var(--wa-color-surface-border)] - The color of the indentation line.
* @cssproperty [--indent-guide-offset=0] - The amount of vertical spacing to leave between the top and bottom of the
* indentation line's starting position.

View File

@@ -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';

View 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;
}
}

View File

@@ -11,17 +11,3 @@ export function waitForEvent(el: HTMLElement, eventName: string) {
el.addEventListener(eventName, done);
});
}
export function getTargetElement(event: Event) {
if (event.target instanceof Node) {
switch (event.target.nodeType) {
case Node.TEXT_NODE:
case Node.COMMENT_NODE:
return event.target.parentNode as Element;
case Node.ELEMENT_NODE:
return event.target as Element;
}
}
return null;
}

View File

@@ -454,7 +454,7 @@
&::after {
content: '';
background-color: var(--wa-color-text-quiet);
mask: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" height="16" width="10" viewBox="0 0 320 512"><path d="M310.6 233.4c12.5 12.5 12.5 32.8 0 45.3l-192 192c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3L242.7 256 73.4 86.6c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0l192 192z"/></svg>')
mask: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" height="16" width="10" viewBox="0 0 320 512"><!--! Font Awesome Pro 7.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2025 Fonticons, Inc. --><path fill="currentColor" d="M311.1 233.4c12.5 12.5 12.5 32.8 0 45.3l-192 192c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3L243.2 256 73.9 86.6c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0l192 192z"/></svg>')
center no-repeat;
width: 1rem;
height: 1rem;
@@ -916,8 +916,8 @@
&:indeterminate::after {
position: absolute;
width: 100%;
height: 100%;
width: round(calc(100% - var(--wa-form-control-border-width) * 2), 1px);
height: round(calc(100% - var(--wa-form-control-border-width) * 2), 1px);
content: '';
@@ -927,7 +927,7 @@
}
&:checked::after {
mask: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path d="M438.6 105.4c12.5 12.5 12.5 32.8 0 45.3l-256 256c-12.5 12.5-32.8 12.5-45.3 0l-128-128c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0L160 338.7 393.4 105.4c12.5-12.5 32.8-12.5 45.3 0z"/></svg>')
mask: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Pro 7.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2025 Fonticons, Inc. --><path fill="currentColor" d="M434.8 70.1c14.3 10.4 17.5 30.4 7.1 44.7l-256 352c-5.5 7.6-14 12.3-23.4 13.1s-18.5-2.7-25.1-9.3l-128-128c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0l101.5 101.5 234-321.7c10.4-14.3 30.4-17.5 44.7-7.1z"/></svg>')
center no-repeat;
}
@@ -940,7 +940,7 @@
/* Radio */
input[type='radio'] {
--checked-icon-color: var(--wa-form-control-activated-color);
--checked-icon-scale: 0.75;
--checked-icon-scale: 0.7;
color: transparent;
@@ -1059,6 +1059,8 @@
select {
--icon-caret: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16" viewBox="0 0 512 512"><path fill="rgb(180 180 200)" d="M233.4 406.6c12.5 12.5 32.8 12.5 45.3 0l192-192c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L256 338.7 86.6 169.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3l192 192z"/></svg>');
--icon-caret: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16" viewBox="0 0 448 512"><!--! Font Awesome Pro 7.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2025 Fonticons, Inc. --><path fill="rgb(180 180 200)" d="M201.4 406.6c12.5 12.5 32.8 12.5 45.3 0l192-192c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L224 338.7 54.6 169.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3l192 192z"/></svg>');
appearance: none;
position: relative;

View File

@@ -1,4 +1,4 @@
@import url('https://fonts.googleapis.com/css2?family=Crimson+Pro:ital,wght@0,200..900;1,200..900&family=Quicksand:wght@300..700&display=swap');
@import url('https://fonts.bunny.net/css2?family=Crimson+Pro:ital,wght@0,200..900;1,200..900&family=Quicksand:wght@300..700&display=swap');
@layer wa-theme {
.wa-theme-awesome,

View File

@@ -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