mirror of
https://github.com/shoelace-style/webawesome.git
synced 2026-01-13 04:29:14 +00:00
Compare commits
131 Commits
hints
...
button-typ
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e019dcbeb0 | ||
|
|
f398899991 | ||
|
|
8fc0ee89ff | ||
|
|
ed17964f10 | ||
|
|
64ce424c42 | ||
|
|
51253650e1 | ||
|
|
0b45173192 | ||
|
|
0eb8eaea00 | ||
|
|
334e3361b4 | ||
|
|
5e482739a9 | ||
|
|
c0b18f6580 | ||
|
|
c18df17429 | ||
|
|
2ec957ff76 | ||
|
|
1fd68dfb3c | ||
|
|
0b5689de62 | ||
|
|
8ffd9991db | ||
|
|
566f98359c | ||
|
|
6387dffe75 | ||
|
|
bb747d50dd | ||
|
|
2704ef7197 | ||
|
|
336a3acdd0 | ||
|
|
e68640b8ca | ||
|
|
487b629995 | ||
|
|
255647d2a9 | ||
|
|
53f1b3615d | ||
|
|
d00d8bac4e | ||
|
|
87864924e1 | ||
|
|
ce3496a621 | ||
|
|
cf80b57a79 | ||
|
|
694a9eccb9 | ||
|
|
502df6ee9c | ||
|
|
44142fb56d | ||
|
|
7e00d2b02e | ||
|
|
fcf37f83a1 | ||
|
|
0beceff73f | ||
|
|
afe60eae69 | ||
|
|
b4f45f4ff1 | ||
|
|
fd4cded708 | ||
|
|
4b2b72e822 | ||
|
|
56657ebcfc | ||
|
|
844015df7b | ||
|
|
d5da2e2db5 | ||
|
|
efbc404524 | ||
|
|
b6afa148ae | ||
|
|
7c3795897c | ||
|
|
986e52f977 | ||
|
|
6dce88429a | ||
|
|
04497cfd13 | ||
|
|
75116a5b0c | ||
|
|
5e192023b4 | ||
|
|
26b29189db | ||
|
|
ac20e495d9 | ||
|
|
4ac31e06f3 | ||
|
|
4993b1034f | ||
|
|
b3b93091f7 | ||
|
|
8cf20d9938 | ||
|
|
63296b7ed5 | ||
|
|
0e56ed0cbb | ||
|
|
e70cb5c66d | ||
|
|
913abd0db1 | ||
|
|
d2798a96da | ||
|
|
42729153db | ||
|
|
c2df5ca1ea | ||
|
|
c49813c7c1 | ||
|
|
f63dfbfff0 | ||
|
|
9a7947debd | ||
|
|
d2e1653519 | ||
|
|
5b7004e72d | ||
|
|
7ccfed9e93 | ||
|
|
4f1de3918b | ||
|
|
adaea2fa5f | ||
|
|
e016e4bd48 | ||
|
|
5e0b5f3fe6 | ||
|
|
493e1c06b9 | ||
|
|
8b763d0392 | ||
|
|
9d82749abb | ||
|
|
3e3e5276a6 | ||
|
|
a0a47b1bdf | ||
|
|
fa51cff947 | ||
|
|
fd08d4f227 | ||
|
|
7008c0cef7 | ||
|
|
e9ce8659f6 | ||
|
|
325d6f211b | ||
|
|
bd1570ec76 | ||
|
|
406f3a0e81 | ||
|
|
08babbce6d | ||
|
|
293b86705a | ||
|
|
fe5ab48c06 | ||
|
|
35e9dfe88d | ||
|
|
44567ec967 | ||
|
|
bfdd0bd6d0 | ||
|
|
f3f5d40b7b | ||
|
|
3d75d6b59c | ||
|
|
f30801ab66 | ||
|
|
f92ef1f74e | ||
|
|
da27a8dc74 | ||
|
|
437e0d9aec | ||
|
|
583d9c0616 | ||
|
|
cb29033eaf | ||
|
|
c02ac306af | ||
|
|
56d678b504 | ||
|
|
b1864eb93d | ||
|
|
d2b5613e85 | ||
|
|
f7ca634822 | ||
|
|
35d33c89b9 | ||
|
|
094bd478ff | ||
|
|
0b619a99f1 | ||
|
|
89cf48c865 | ||
|
|
b7a6ebd228 | ||
|
|
6c8bbd51d1 | ||
|
|
6085b9698c | ||
|
|
17ee36175b | ||
|
|
3c7bb71a59 | ||
|
|
d66b552962 | ||
|
|
6f6e23c78c | ||
|
|
310f7a8c5d | ||
|
|
9a7b258108 | ||
|
|
e10aba0ed1 | ||
|
|
1ea5dae9ad | ||
|
|
21310bd367 | ||
|
|
c1478e5865 | ||
|
|
9b8433c996 | ||
|
|
57dac67aab | ||
|
|
441bfd7b72 | ||
|
|
7150c59334 | ||
|
|
bd2a3c3b64 | ||
|
|
11519625ed | ||
|
|
f19848c11e | ||
|
|
36b21b0be7 | ||
|
|
fe2c2ab7af | ||
|
|
b98b9baba4 |
28
README.md
28
README.md
@@ -21,7 +21,7 @@ Twitter: [@webawesomer](https://twitter.com/webawesomer)
|
||||
|
||||
## Developers ✨
|
||||
|
||||
Developers can use this documentation to learn how to build Web Awesome from source. You will need Node >= 14.17 to build and run the project locally.
|
||||
Developers can use this documentation to learn how to build Web Awesome from source. You will need Node.js 14.17 or later to build and run the project locally.
|
||||
|
||||
**You don't need to do any of this to use Web Awesome!** This page is for people who want to contribute to the project, tinker with the source, or create a custom build of Web Awesome.
|
||||
|
||||
@@ -29,31 +29,29 @@ If that's not what you're trying to do, the [documentation website](https://weba
|
||||
|
||||
### What are you using to build Web Awesome?
|
||||
|
||||
Components are built with [LitElement](https://lit-element.polymer-project.org/), a custom elements base class that provides an intuitive API and reactive data binding. The build is a custom script with bundling powered by [esbuild](https://esbuild.github.io/).
|
||||
Components are built with [Lit](https://lit.dev/), a custom elements base class that provides an intuitive API and reactive data binding. The build is a custom script with bundling powered by [esbuild](https://esbuild.github.io/).
|
||||
|
||||
### Understanding the Web Awesome monorepo
|
||||
|
||||
Web Awesome uses [NPM workspaces](https://docs.npmjs.com/cli/v11/using-npm/workspaces) for its monorepo structure and is fairly minimal in what it provides.
|
||||
Web Awesome uses [npm workspaces](https://docs.npmjs.com/cli/v11/using-npm/workspaces) for its monorepo structure and is fairly minimal in what it provides.
|
||||
|
||||
By using a NPM workspaces and a monorepo structure, we can get consistent builds, shared configurations, and reduced duplication across repositories which reduces regressions and forces consistency across `webawesome`, `webawesome-pro`, and `webawesome-app`.
|
||||
By using npm workspaces and a monorepo structure, we can get consistent builds, shared configurations, and reduced duplication across repositories which reduces regressions and forces consistency across `webawesome`, `webawesome-pro`, and `webawesome-app`.
|
||||
|
||||
Generally, if you plan to only work with the free version of `webawesome` it is easiest to go to `packages/webawesome` and run all commands from there.
|
||||
|
||||
### Where do NPM dependencies go?
|
||||
### Where do npm dependencies go?
|
||||
|
||||
Any dependencies intended to be used across all packages (IE: `prettier`, `eslint`) that are _NOT_ used at runtime should be in the root `devDependencies` of `package.json`.
|
||||
Any dependencies intended to be used across all packages (i.e., `prettier`, `eslint`) that are **not** used at runtime should be in the root `devDependencies` of `package.json`.
|
||||
|
||||
```bash
|
||||
npm install -D -w prettier
|
||||
```
|
||||
|
||||
Any dependencies that will be used at runtime by a package should be part of the specific package's `"dependencies"` such as `lit`. This is required because if that dependency is not in the `packages/*/package.json`, it will not be installed when used via NPM.
|
||||
Any dependencies that will be used at runtime by a package should be part of the specific package's `dependencies` such as `lit`. This is required because if that dependency is not in the `packages/*/package.json`, it will not be installed when used via npm.
|
||||
|
||||
Individual packages are also free to install devDependencies as needed as long as they are specific to that package only.
|
||||
Individual packages are also free to install `devDependencies` as needed as long as they are specific to that package only.
|
||||
|
||||
To do install a package specific to a package, change your working directory to that package's root
|
||||
|
||||
IE: `cd packages/webawesome && npm install <package-name>`
|
||||
To install a package specific to a Web Awesome package, change your working directory to that package's root (i.e., `cd packages/webawesome && npm install <package-name>`).
|
||||
|
||||
### Forking the Repo
|
||||
|
||||
@@ -67,14 +65,14 @@ npm install
|
||||
|
||||
### Developing
|
||||
|
||||
Once you've cloned the repo, run the following command from the respective directory within `packages/*`
|
||||
Once you've cloned the repo, run the following command from the respective directory within `packages/*`.
|
||||
|
||||
```bash
|
||||
cd packages/webawesome
|
||||
npm start
|
||||
```
|
||||
|
||||
This will spin up the dev server. After the initial build, a browser will open automatically. There is currently no hot module reloading (HMR), as browser's don't provide a way to reregister custom elements, but most changes to the source will reload the browser automatically.
|
||||
This will spin up the dev server. After the initial build, a browser will open automatically. There is currently no hot module reloading (HMR), as browsers don't provide a way to reregister custom elements, but most changes to the source will reload the browser automatically.
|
||||
|
||||
### Building
|
||||
|
||||
@@ -102,7 +100,7 @@ This will generate a source file, a stylesheet, and a docs page for you. When yo
|
||||
|
||||
Right now the only additional packages are in private repositories.
|
||||
|
||||
To add additional packages from other repositories, run: `git clone <url> packages/<package-name>` to clone your repo into `packages/`.
|
||||
To add additional packages from other repositories, run `git clone <url> packages/<package-name>` to clone your repo into `packages/`.
|
||||
|
||||
Make sure to run `npm install` at the root of the monorepo after adding your package!
|
||||
|
||||
@@ -112,4 +110,4 @@ Web Awesome is an open source project and contributions are encouraged! If you'r
|
||||
|
||||
## License
|
||||
|
||||
Web Awesome is available under the terms of the MIT license.
|
||||
Web Awesome is available under the terms of the [MIT License](LICENSE.md).
|
||||
|
||||
6
VERSIONS.txt
Normal file
6
VERSIONS.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
3.0.0-beta.1
|
||||
3.0.0-beta.2
|
||||
3.0.0-beta.3
|
||||
3.0.0-beta.4
|
||||
3.0.0-beta.5
|
||||
3.0.0-beta.6
|
||||
@@ -92,6 +92,7 @@
|
||||
"heroicons",
|
||||
"hexa",
|
||||
"Hotwire",
|
||||
"hrefs",
|
||||
"Iconoir",
|
||||
"Iframes",
|
||||
"iife",
|
||||
@@ -135,6 +136,7 @@
|
||||
"noopener",
|
||||
"noreferrer",
|
||||
"noscript",
|
||||
"Notdog",
|
||||
"novalidate",
|
||||
"nowrap",
|
||||
"Numberish",
|
||||
|
||||
83
package-lock.json
generated
83
package-lock.json
generated
@@ -16,7 +16,7 @@
|
||||
"@custom-elements-manifest/analyzer": "^0.10.4",
|
||||
"@lit-labs/eleventy-plugin-lit": "^1.0.3",
|
||||
"@lit-labs/testing": "^0.2.5",
|
||||
"@lit/react": "^1.0.6",
|
||||
"@lit/react": "^1.0.8",
|
||||
"@open-wc/testing": "^3.2.0",
|
||||
"@types/mocha": "^10.0.10",
|
||||
"@types/react": "^18.2.28",
|
||||
@@ -2029,10 +2029,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@lit/react": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@lit/react/-/react-1.0.7.tgz",
|
||||
"integrity": "sha512-cencnwwLXQKiKxjfFzSgZRngcWJzUDZi/04E0fSaF86wZgchMdvTyu+lE36DrUfvuus3bH8+xLPrhM1cTjwpzw==",
|
||||
"dev": true,
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@lit/react/-/react-1.0.8.tgz",
|
||||
"integrity": "sha512-p2+YcF+JE67SRX3mMlJ1TKCSTsgyOVdAwd/nxp3NuV1+Cb6MWALbN6nT7Ld4tpmYofcE5kcaSY1YBB9erY+6fw==",
|
||||
"peerDependencies": {
|
||||
"@types/react": "17 || 18 || 19"
|
||||
}
|
||||
@@ -2876,8 +2875,7 @@
|
||||
"node_modules/@types/prop-types": {
|
||||
"version": "15.7.14",
|
||||
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz",
|
||||
"integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==",
|
||||
"dev": true
|
||||
"integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ=="
|
||||
},
|
||||
"node_modules/@types/qs": {
|
||||
"version": "6.9.11",
|
||||
@@ -2895,7 +2893,6 @@
|
||||
"version": "18.3.23",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.23.tgz",
|
||||
"integrity": "sha512-/LDXMQh55EzZQ0uVAZmKKhfENivEvWz6E+EYzh+/MCjMhNsotd+ZHhBGIjFDTi6+fz0OhQQQLbTgdQIxxCsC0w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/prop-types": "*",
|
||||
"csstype": "^3.0.2"
|
||||
@@ -2986,6 +2983,12 @@
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@wc-toolkit/jsx-types": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@wc-toolkit/jsx-types/-/jsx-types-1.3.0.tgz",
|
||||
"integrity": "sha512-2rcRyPNEAdesFlokSSFBuCjpPPrMySk4NqyVJsqCs/WczcAUnIGwjnJk2fd/SNmzSjxGFRIFLAhXOgFOHLPvxQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@web/browser-logs": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@web/browser-logs/-/browser-logs-0.4.0.tgz",
|
||||
@@ -5799,8 +5802,7 @@
|
||||
"node_modules/csstype": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
||||
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
|
||||
"dev": true
|
||||
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
|
||||
},
|
||||
"node_modules/custom-element-jet-brains-integration": {
|
||||
"version": "1.7.0",
|
||||
@@ -6476,6 +6478,16 @@
|
||||
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/eleventy-plugin-git-commit-date": {
|
||||
"version": "0.1.3",
|
||||
"resolved": "https://registry.npmjs.org/eleventy-plugin-git-commit-date/-/eleventy-plugin-git-commit-date-0.1.3.tgz",
|
||||
"integrity": "sha512-dmdkGpMuRj8apWptC1QGqAsLHCguddFlfPjbjflGCqXdJ8cdhEjrzzvI/rL0XUvzCC4ETgGl9i/wDU6ujZ94gA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cross-spawn": "^7.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/emoji-regex": {
|
||||
"version": "10.4.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz",
|
||||
@@ -13006,12 +13018,6 @@
|
||||
"node": ">=0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/style-observer": {
|
||||
"version": "0.0.7",
|
||||
"resolved": "https://registry.npmjs.org/style-observer/-/style-observer-0.0.7.tgz",
|
||||
"integrity": "sha512-t75H3CRy+vd5q3yqyrf/De4tkz33hPQTiCcfh0NTesI5G7kJnZ227LEYTwqjKTtaFOCJvqZcYFHpJlF8bsk3bQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/supports-color": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
@@ -13975,43 +13981,66 @@
|
||||
},
|
||||
"packages/webawesome": {
|
||||
"name": "@awesome.me/webawesome",
|
||||
"version": "3.0.0-beta.2",
|
||||
"version": "3.0.0-beta.6",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@ctrl/tinycolor": "^4.1.0",
|
||||
"@ctrl/tinycolor": "4.1.0",
|
||||
"@floating-ui/dom": "^1.6.13",
|
||||
"@lit/react": "^1.0.8",
|
||||
"@shoelace-style/animations": "^1.2.0",
|
||||
"@shoelace-style/localize": "^3.2.1",
|
||||
"composed-offset-position": "^0.0.6",
|
||||
"lit": "^3.2.1",
|
||||
"nanoid": "^5.1.5",
|
||||
"qr-creator": "^1.0.0",
|
||||
"style-observer": "^0.0.7"
|
||||
"qr-creator": "^1.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@wc-toolkit/jsx-types": "^1.3.0",
|
||||
"eleventy-plugin-git-commit-date": "^0.1.3"
|
||||
},
|
||||
"devDependencies": {},
|
||||
"engines": {
|
||||
"node": ">=14.17.0"
|
||||
}
|
||||
},
|
||||
"packages/webawesome-pro": {
|
||||
"name": "@shoelace-style/webawesome-pro",
|
||||
"version": "3.0.0-beta.2",
|
||||
"license": "TODO",
|
||||
"version": "3.0.0-beta.6",
|
||||
"dependencies": {
|
||||
"@ctrl/tinycolor": "^4.1.0",
|
||||
"@ctrl/tinycolor": "4.1.0",
|
||||
"@floating-ui/dom": "^1.6.13",
|
||||
"@lit/react": "^1.0.8",
|
||||
"@shoelace-style/animations": "^1.2.0",
|
||||
"@shoelace-style/localize": "^3.2.1",
|
||||
"composed-offset-position": "^0.0.6",
|
||||
"lit": "^3.2.1",
|
||||
"qr-creator": "^1.0.0",
|
||||
"style-observer": "^0.0.7"
|
||||
"nanoid": "^5.1.5",
|
||||
"qr-creator": "^1.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@wc-toolkit/jsx-types": "^1.3.0",
|
||||
"eleventy-plugin-git-commit-date": "^0.1.3"
|
||||
},
|
||||
"devDependencies": {},
|
||||
"engines": {
|
||||
"node": ">=14.17.0"
|
||||
}
|
||||
},
|
||||
"packages/webawesome-pro/node_modules/nanoid": {
|
||||
"version": "5.1.5",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.5.tgz",
|
||||
"integrity": "sha512-Ir/+ZpE9fDsNH0hQ3C68uyThDXzYcim2EqcZ8zn8Chtt1iylPT9xXJB0kPCnqzgcEGikO9RxSrh63MsmVCU7Fw==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"bin": {
|
||||
"nanoid": "bin/nanoid.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18 || >=20"
|
||||
}
|
||||
},
|
||||
"packages/webawesome/node_modules/nanoid": {
|
||||
"version": "5.1.5",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.5.tgz",
|
||||
|
||||
@@ -18,13 +18,12 @@
|
||||
"engines": {
|
||||
"node": ">=14.17.0"
|
||||
},
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"@11ty/eleventy": "3.0.0",
|
||||
"@custom-elements-manifest/analyzer": "^0.10.4",
|
||||
"@lit-labs/eleventy-plugin-lit": "^1.0.3",
|
||||
"@lit-labs/testing": "^0.2.5",
|
||||
"@lit/react": "^1.0.6",
|
||||
"@lit/react": "^1.0.8",
|
||||
"@open-wc/testing": "^3.2.0",
|
||||
"@types/mocha": "^10.0.10",
|
||||
"@types/react": "^18.2.28",
|
||||
@@ -87,4 +86,3 @@
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { jsxTypesPlugin } from '@wc-toolkit/jsx-types';
|
||||
import { customElementJetBrainsPlugin } from 'custom-element-jet-brains-integration';
|
||||
import { customElementVsCodePlugin } from 'custom-element-vs-code-integration';
|
||||
// import { customElementVuejsPlugin } from 'custom-element-vuejs-integration';
|
||||
@@ -164,6 +165,7 @@ export default {
|
||||
],
|
||||
}),
|
||||
|
||||
// Generate custom JetBrains data
|
||||
customElementJetBrainsPlugin({
|
||||
outdir: './dist-cdn',
|
||||
excludeCss: true,
|
||||
@@ -176,6 +178,16 @@ export default {
|
||||
},
|
||||
}),
|
||||
|
||||
// Generate JSX types (see https://wc-toolkit.com/integrations/jsx/)
|
||||
jsxTypesPlugin({
|
||||
fileName: 'custom-elements-jsx.d.ts',
|
||||
outdir,
|
||||
defaultExport: true,
|
||||
componentTypePath: (_name, _tag, modulePath) => {
|
||||
return `./${modulePath}`;
|
||||
},
|
||||
}),
|
||||
|
||||
//
|
||||
// TODO - figure out why this broke when events were updated
|
||||
//
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { nanoid } from 'nanoid';
|
||||
import { parse as HTMLParse } from 'node-html-parser';
|
||||
import { execFileSync } from 'node:child_process';
|
||||
import * as fs from 'node:fs';
|
||||
import * as path from 'node:path';
|
||||
import { anchorHeadingsTransformer } from './_transformers/anchor-headings.js';
|
||||
@@ -19,6 +21,7 @@ import { replaceTextPlugin } from './_plugins/replace-text.js';
|
||||
import { searchPlugin } from './_plugins/search.js';
|
||||
const __dirname = url.fileURLToPath(new URL('.', import.meta.url));
|
||||
const isDev = process.argv.includes('--develop');
|
||||
const ignoreGit = process.env.ELEVENTY_IGNORE_GIT === 'true';
|
||||
const passThroughExtensions = ['js', 'css', 'png', 'svg', 'jpg', 'mp4'];
|
||||
|
||||
async function getPackageData() {
|
||||
@@ -57,6 +60,15 @@ export default async function (eleventyConfig) {
|
||||
if (updateComponentData) {
|
||||
allComponents = getComponents();
|
||||
}
|
||||
|
||||
// Invalidate last-modified cache for changed content files during watch
|
||||
if (Array.isArray(changedFiles)) {
|
||||
for (const file of changedFiles) {
|
||||
if (/\.(md|njk|html)$/i.test(file)) {
|
||||
lastModCache.delete(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -87,12 +99,97 @@ export default async function (eleventyConfig) {
|
||||
eleventyConfig.addFilter('markdown', content => markdown.render(content || ''));
|
||||
eleventyConfig.addFilter('stripExtension', string => path.parse(string + '').name);
|
||||
eleventyConfig.addFilter('stripPrefix', content => content.replace(/^wa-/, ''));
|
||||
eleventyConfig.addFilter('uniqueId', (_value, length = 8) => nanoid(length));
|
||||
// Returns last modified date as ISO 8601 (UTC, Z-suffixed)
|
||||
// Fallback order: front matter override -> Git last commit date -> filesystem mtime -> now
|
||||
// Caching: in-memory per inputPath during one build/dev session
|
||||
// Override: pass a Date or string: {{ page.inputPath | gitLastModifiedISO(lastUpdated) }}
|
||||
const lastModCache = new Map();
|
||||
let repoRoot = null; // lazily resolved; null => not resolved, undefined => failed
|
||||
|
||||
function getLastModifiedISO(inputPath, overrideDate) {
|
||||
if (overrideDate instanceof Date) {
|
||||
return overrideDate.toISOString();
|
||||
}
|
||||
if (typeof overrideDate === 'string' && overrideDate) {
|
||||
const parsed = new Date(overrideDate);
|
||||
if (!isNaN(parsed.getTime())) return parsed.toISOString();
|
||||
}
|
||||
if (!inputPath) return new Date().toISOString();
|
||||
if (lastModCache.has(inputPath)) return lastModCache.get(inputPath);
|
||||
|
||||
// Try Git (ISO via %cI). Use a repo-root-relative path for portability.
|
||||
if (!ignoreGit) {
|
||||
try {
|
||||
if (repoRoot === null) {
|
||||
try {
|
||||
repoRoot = execFileSync('git', ['rev-parse', '--show-toplevel'], {
|
||||
stdio: ['ignore', 'pipe', 'ignore'],
|
||||
cwd: __dirname,
|
||||
})
|
||||
.toString()
|
||||
.trim();
|
||||
} catch (_) {
|
||||
repoRoot = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
const gitPath = repoRoot ? path.relative(repoRoot, inputPath) : inputPath;
|
||||
const args = ['log', '-1', '--format=%cI', '--follow', '--', gitPath];
|
||||
const result = execFileSync('git', args, {
|
||||
stdio: ['ignore', 'pipe', 'ignore'],
|
||||
cwd: repoRoot || path.dirname(inputPath),
|
||||
})
|
||||
.toString()
|
||||
.trim();
|
||||
if (result) {
|
||||
const iso = new Date(result).toISOString();
|
||||
lastModCache.set(inputPath, iso);
|
||||
return iso;
|
||||
}
|
||||
} catch (_) {
|
||||
// continue to fs fallback
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to filesystem mtime
|
||||
try {
|
||||
const stats = fs.statSync(inputPath);
|
||||
const iso = new Date(stats.mtime).toISOString();
|
||||
lastModCache.set(inputPath, iso);
|
||||
return iso;
|
||||
} catch (_) {
|
||||
const now = new Date().toISOString();
|
||||
lastModCache.set(inputPath, now);
|
||||
return now;
|
||||
}
|
||||
}
|
||||
|
||||
eleventyConfig.addFilter('gitLastModifiedISO', function (inputPath, overrideDate) {
|
||||
return getLastModifiedISO(inputPath, overrideDate);
|
||||
});
|
||||
|
||||
// Attach lastUpdatedISO to page data so templates can use {{ lastUpdatedISO }} directly
|
||||
eleventyConfig.addGlobalData('eleventyComputed', {
|
||||
lastUpdatedISO: data => getLastModifiedISO(data.page?.inputPath, data.lastUpdated),
|
||||
});
|
||||
// Trims whitespace and pipes from the start and end of a string. Useful for CEM types, which can be pipe-delimited.
|
||||
// With Prettier 3, this means a leading pipe will exist be present when the line wraps.
|
||||
eleventyConfig.addFilter('trimPipes', content => {
|
||||
return typeof content === 'string' ? content.replace(/^(\s|\|)/g, '').replace(/(\s|\|)$/g, '') : content;
|
||||
});
|
||||
|
||||
/**
|
||||
* @example
|
||||
{% set foo = {foo: "bar"} %}
|
||||
{% set bar = {bar: "baz"} %}
|
||||
{% set merged = foo | merge(bar) %}
|
||||
{{ merged | dump }}
|
||||
*/
|
||||
eleventyConfig.addFilter('merge', function (obj1, obj2) {
|
||||
return Object.assign({}, obj1, obj2);
|
||||
});
|
||||
|
||||
// Custom filter to sort with a priority item first, e.g.
|
||||
// {{ collection | sortWithFirst('fileSlug', 'default') }} => the item with the fileSlug of 'default' will be first
|
||||
eleventyConfig.addFilter('sortWithFirst', function (collection, property, firstValue) {
|
||||
@@ -159,6 +256,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 } });
|
||||
@@ -229,9 +335,12 @@ export default async function (eleventyConfig) {
|
||||
// eleventyConfig.addPlugin(formatCodePlugin());
|
||||
// }
|
||||
|
||||
let assetsDir = path.join(process.env.BASE_DIR || 'docs', 'assets');
|
||||
const siteAssetsDir = path.join(eleventyConfig.directories.output, 'assets');
|
||||
fs.cpSync(assetsDir, siteAssetsDir, { recursive: true });
|
||||
// This needs to happen in "eleventy.after" otherwise incremental builds never update.
|
||||
eleventyConfig.on('eleventy.after', function () {
|
||||
let assetsDir = path.join(process.env.BASE_DIR || 'docs', 'assets');
|
||||
const siteAssetsDir = path.join(eleventyConfig.directories.output, 'assets');
|
||||
fs.cpSync(assetsDir, siteAssetsDir, { recursive: true });
|
||||
});
|
||||
|
||||
for (let glob of passThrough) {
|
||||
eleventyConfig.addPassthroughCopy(glob);
|
||||
@@ -261,13 +370,10 @@ export default async function (eleventyConfig) {
|
||||
// });
|
||||
// }
|
||||
|
||||
if (!isDev) {
|
||||
// For a server build, we expect a server to run the second transform.
|
||||
// For dev builds, we run the second transform in a middleware.
|
||||
if (!isDev && !serverBuild) {
|
||||
eleventyConfig.addTransform('simulate-webawesome-app', function (content) {
|
||||
// For a server build, we expect a server to run the second transform.
|
||||
if (serverBuild) {
|
||||
return content;
|
||||
}
|
||||
|
||||
// Only run the transform on files nunjucks would transform.
|
||||
if (!this.page.inputPath.match(/.(md|html|njk)$/)) {
|
||||
return content;
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" data-fa-kit-code="b10bfbde90" data-cdn-url="{% cdnUrl %}" class="wa-cloak">
|
||||
<html lang="en" data-fa-kit-code="38c11e3f20" data-cdn-url="{% cdnUrl %}" class="wa-cloak">
|
||||
<head>
|
||||
{% include 'head.njk' %}
|
||||
<meta name="theme-color" content="#f36944">
|
||||
@@ -15,10 +15,9 @@
|
||||
{% 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" />
|
||||
|
||||
{% block head %}{% endblock %}
|
||||
{% block head %}
|
||||
<link rel="stylesheet" href="/assets/styles/docs.css" />
|
||||
{% endblock %}
|
||||
|
||||
<script type="module">
|
||||
// Set the initial color scheme before the page renders to prevent flashing
|
||||
@@ -26,75 +25,60 @@
|
||||
const isDark = value === 'dark' || (value === 'auto' && matchMedia('(prefers-color-scheme: dark)').matches);
|
||||
document.documentElement.classList.toggle('wa-dark', isDark);
|
||||
</script>
|
||||
|
||||
<script type="module">
|
||||
// Set the initial theme before the page renders to prevent flashing
|
||||
const savedTheme = localStorage.getItem('theme') || 'default';
|
||||
|
||||
// Update the theme stylesheet link first
|
||||
const themeStylesheet = document.getElementById('theme-stylesheet');
|
||||
if (themeStylesheet) {
|
||||
themeStylesheet.href = `/dist/styles/themes/${savedTheme}.css`;
|
||||
}
|
||||
|
||||
if (savedTheme !== 'default') {
|
||||
document.documentElement.classList.add(`wa-theme-${savedTheme}`);
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body class="layout-{{ layout | stripExtension }}{{ ' page-wide' if wide }}">
|
||||
<!-- 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>
|
||||
<body class="layout-{{ layout | stripExtension }} page-{{ pageClass or page.fileSlug or 'home' }}{{ ' page-wide' if wide }}">
|
||||
{% set defaultWaPageAttributes = defaultWaPageAttributes or { view: 'desktop', 'disable-navigation-toggle': true, 'mobile-breakpoint': 1180 } %}
|
||||
{% set waPageAttributes = waPageAttributes or {} %}
|
||||
{% set mergedWaPageAttributes = defaultWaPageAttributes | merge(waPageAttributes) %}
|
||||
<wa-page
|
||||
{% for key, value in mergedWaPageAttributes %}
|
||||
{% if value != null and value != false %}
|
||||
{{ key }}="{{ value }}"
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
>
|
||||
{% block pageHeader %}
|
||||
<header slot="header" class="wa-split">
|
||||
{# Nav toggle #}
|
||||
<wa-button appearance="plain" size="small" data-toggle-nav>
|
||||
<wa-icon name="bars" label="Toggle navigation" class="icon-default icon-embiggen"></wa-icon>
|
||||
<wa-icon name="burger" aria-hidden="true" class="icon-hover icon-embiggen"></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>
|
||||
{# Logo - Desktop #}
|
||||
<a class="brand-logo wa-desktop-only" href="/" aria-label="Web Awesome">
|
||||
{% include "logo.njk" %}
|
||||
</a>
|
||||
|
||||
<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>
|
||||
{#- Logo - mobile branding -#}
|
||||
<a href="/" class="brand-logo wa-mobile-only" aria-label="Web Awesome">
|
||||
{# Logo - Mobile #}
|
||||
{% include "logo-simple.njk" %}
|
||||
</a>
|
||||
|
||||
<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>
|
||||
</header>
|
||||
<div id="docs-toolbar" class="wa-cluster gap-s">
|
||||
<div class="wa-desktop-only wa-cluster wa-gap-2xs">
|
||||
{% include "theme-selector.njk" %}
|
||||
{% include "color-scheme-selector.njk" %}
|
||||
{% include "github-icon-buttons.njk" %}
|
||||
</div>
|
||||
{#- Login -#}
|
||||
{% include "login-or-avatar.njk" ignore missing %}
|
||||
</div>
|
||||
</header>
|
||||
{% endblock %}
|
||||
|
||||
{# Sidebar #}
|
||||
{% if hasSidebar %}
|
||||
{# Mobile selectors #}
|
||||
<div class="wa-mobile-only" slot="navigation-header">
|
||||
<div class="wa-cluster wa-gap-xs" style="flex-wrap: nowrap;">
|
||||
{% include "theme-selector.njk" %}
|
||||
{% include "color-scheme-selector.njk" %}
|
||||
<div class="wa-cluster wa-gap-s">
|
||||
<a class="brand-logo" href="/" aria-label="Web Awesome">{% include "logo-simple.njk" %}</a>
|
||||
<div class="wa-cluster wa-gap-2xs" style="flex-wrap: nowrap;">
|
||||
{% include "theme-selector.njk" %}
|
||||
{% include "color-scheme-selector.njk" %}
|
||||
{% include "github-icon-buttons.njk" %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div slot="navigation" id="sidebar" class="docs-aside" data-remember-scroll>
|
||||
@@ -138,6 +122,9 @@
|
||||
</main>
|
||||
|
||||
{% include 'search.njk' %}
|
||||
|
||||
{# Footer #}
|
||||
{% block pageFooter %}{% endblock %}
|
||||
</wa-page>
|
||||
|
||||
</body>
|
||||
|
||||
@@ -1,25 +1,35 @@
|
||||
<wa-select class="color-scheme-selector" appearance="filled" size="small" value="auto" title="Press \ to toggle">
|
||||
<wa-icon class="only-light" slot="start" name="sun" variant="regular"></wa-icon>
|
||||
<wa-icon class="only-dark" slot="start" name="moon" variant="regular"></wa-icon>
|
||||
<wa-option value="light">
|
||||
<wa-icon slot="start" name="sun" variant="regular"></wa-icon>
|
||||
<wa-dropdown class="color-scheme-selector" title="Press \ to toggle">
|
||||
<wa-button slot="trigger" id="color-scheme-selector-trigger" appearance="plain">
|
||||
<wa-icon name="sun-bright" variant="regular" class="icon-embiggen only-light"></wa-icon>
|
||||
<wa-icon name="moon-stars" variant="regular" class="icon-embiggen only-dark"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-dropdown-item value="light">
|
||||
<wa-icon slot="icon" name="sun-bright" variant="regular" class="icon-embiggen"></wa-icon>
|
||||
Light
|
||||
</wa-option>
|
||||
<wa-option value="dark">
|
||||
<wa-icon slot="start" name="moon" variant="regular"></wa-icon>
|
||||
</wa-dropdown-item>
|
||||
<wa-dropdown-item value="dark">
|
||||
<wa-icon slot="icon" name="moon-stars" variant="regular" class="icon-embiggen"></wa-icon>
|
||||
Dark
|
||||
</wa-option>
|
||||
</wa-dropdown-item>
|
||||
<wa-divider></wa-divider>
|
||||
<wa-option value="auto">
|
||||
<wa-icon class="only-light" slot="start" name="sun" variant="regular"></wa-icon>
|
||||
<wa-icon class="only-dark" slot="start" name="moon" variant="regular"></wa-icon>
|
||||
<wa-dropdown-item value="auto">
|
||||
<wa-icon slot="icon" name="sun-bright" variant="regular" class="only-light icon-embiggen"></wa-icon>
|
||||
<wa-icon slot="icon" name="moon-stars" variant="regular" class="only-dark icon-embiggen"></wa-icon>
|
||||
System
|
||||
</wa-option>
|
||||
</wa-select>
|
||||
</wa-dropdown-item>
|
||||
</wa-dropdown>
|
||||
<wa-tooltip for="color-scheme-selector-trigger" id="color-scheme-tooltip">Select Color Scheme</wa-tooltip>
|
||||
|
||||
<script>
|
||||
// Immediately set the correct value from storage
|
||||
document.querySelectorAll('wa-select.color-scheme-selector').forEach(el => {
|
||||
el.setAttribute('value', localStorage.getItem('color-scheme') || 'auto');
|
||||
// Handle dropdown selection and trigger input event for color-scheme.js
|
||||
document.querySelectorAll('wa-dropdown.color-scheme-selector').forEach(el => {
|
||||
el.addEventListener('wa-select', (event) => {
|
||||
const selectedValue = event.detail.item.value;
|
||||
|
||||
// Trigger input event for color-scheme.js
|
||||
el.value = selectedValue;
|
||||
el.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
<wa-button id="github-repo-button" href="https://github.com/shoelace-style/webawesome" rel="noopener noreferrer" target="_blank" appearance="plain">
|
||||
<wa-icon family="brands" name="github" label="View Repository on GitHub" class="icon-embiggen"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-tooltip for="github-repo-button" distance="2">View on GitHub</wa-tooltip>
|
||||
<wa-button id="github-star-button" href="https://github.com/shoelace-style/webawesome/stargazers" rel="noopener noreferrer" target="_blank" appearance="plain">
|
||||
<wa-icon name="star" variant="regular" label="Star Repository on GitHub" class="icon-embiggen"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-tooltip for="github-star-button" distance="2">Star on GitHub</wa-tooltip>
|
||||
@@ -21,10 +21,20 @@
|
||||
<script type="module" src="/dist/webawesome.loader.js"></script>
|
||||
|
||||
<link id="theme-stylesheet" rel="stylesheet" href="/dist/styles/themes/default.css" render="blocking" fetchpriority="high" />
|
||||
{% for palette in themer.palettes %}
|
||||
<link rel="stylesheet" href="/dist/styles/color/palettes/{{palette.filename}}" />
|
||||
{% 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" %}
|
||||
|
||||
5
packages/webawesome/docs/_includes/planned-badge.njk
Normal file
5
packages/webawesome/docs/_includes/planned-badge.njk
Normal file
@@ -0,0 +1,5 @@
|
||||
{% macro plannedBadge(plannedBadgeDescription, plannedBadgeId) %}
|
||||
{% set badgeId = plannedBadgeId or ("planned-badge-" + ("" | uniqueId(8))) %}
|
||||
<wa-badge appearance="filled" variant="neutral" pill class="planned" id="{{ badgeId }}">Planned</wa-badge>
|
||||
<wa-tooltip for="{{ badgeId }}">{{ plannedBadgeDescription or "This is a planned feature" }}</wa-tooltip>
|
||||
{% endmacro %}
|
||||
6
packages/webawesome/docs/_includes/pro-badge.njk
Normal file
6
packages/webawesome/docs/_includes/pro-badge.njk
Normal file
@@ -0,0 +1,6 @@
|
||||
{% macro proBadge(params) %}
|
||||
{% set description = params.description or "This requires access to Web Awesome Pro" %}
|
||||
{% set badgeId = params.id or ("pro-badge-" + ("" | uniqueId(8))) %}
|
||||
<wa-badge appearance="accent" pill class="pro" id="{{ badgeId }}">Pro</wa-badge>
|
||||
<wa-tooltip for="{{ badgeId }}">{{ description }}</wa-tooltip>
|
||||
{% endmacro %}
|
||||
@@ -0,0 +1,5 @@
|
||||
<wa-button id="search-trigger" appearance="outlined" size="small" data-search>
|
||||
<wa-icon slot="start" name="magnifying-glass"></wa-icon>
|
||||
Search the Docs for…
|
||||
<kbd slot="end" class="wa-desktop-only">/</kbd>
|
||||
</wa-button>
|
||||
@@ -1,9 +1,7 @@
|
||||
{# Search #}
|
||||
<wa-button id="search-trigger" appearance="outlined" size="small" data-search>
|
||||
<wa-icon slot="start" name="magnifying-glass"></wa-icon>
|
||||
Search
|
||||
<kbd slot="end" class="wa-desktop-only">/</kbd>
|
||||
</wa-button>
|
||||
{% include "search-trigger-button.njk" %}
|
||||
{% from "pro-badge.njk" import proBadge %}
|
||||
{% from "planned-badge.njk" import plannedBadge %}
|
||||
|
||||
{# Getting started #}
|
||||
<h2>Getting Started</h2>
|
||||
@@ -25,22 +23,28 @@
|
||||
<li><a href="/docs/resources/browser-support">Browser Support</a></li>
|
||||
<li><a href="/docs/resources/contributing">Contributing</a></li>
|
||||
<li><a href="/docs/resources/changelog">Changelog</a></li>
|
||||
<li><a href="/license"><span class="wa-visually-hidden">Web Awesome </span>Free License</a></li>
|
||||
<li><a href="/license/pro"><span class="wa-visually-hidden">Web Awesome </span>Pro License</a></li>
|
||||
<li><a href="/tos">Terms of Service</a></li>
|
||||
<li><a href="/docs/resources/visual-tests">Visual Tests</a></li>
|
||||
</ul>
|
||||
|
||||
<!-- 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" class="icon-shrink"></wa-icon>
|
||||
</a>
|
||||
{{ proBadge() }}
|
||||
</span>
|
||||
</li>
|
||||
<li><a href="/docs/components/animated-image/">Animated Image</a></li>
|
||||
<li><a href="/docs/components/animation/">Animation</a></li>
|
||||
@@ -57,22 +61,32 @@
|
||||
<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" class="icon-shrink"></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" class="icon-shrink"></wa-icon>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><span class="is-planned wa-split">Charts <a href="https://github.com/shoelace-style/webawesome/issues/1073" target="_blank">{{ plannedBadge("A Web Awesome Kickstarter stretch goal!") }}</a></span></li>
|
||||
<li><a href="/docs/components/checkbox/">Checkbox</a></li>
|
||||
<li><a href="/docs/components/color-picker/">Color Picker</a></li>
|
||||
<li><span class="is-planned wa-split">Combobox <a href="https://github.com/shoelace-style/webawesome/issues/1074" target="_blank">{{ plannedBadge("A Web Awesome Kickstarter stretch goal!") }}</a></span></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" class="icon-shrink"></wa-icon>
|
||||
</a>
|
||||
</li>
|
||||
<li><span class="is-planned wa-split">Data Grid <a href="https://github.com/shoelace-style/webawesome/issues/1072" target="_blank">{{ plannedBadge("A Web Awesome Kickstarter stretch goal!") }}</a></span></li>
|
||||
<li><span class="is-planned wa-split">Datepicker <a href="https://github.com/shoelace-style/webawesome/issues/1075" target="_blank">{{ plannedBadge("A Web Awesome Kickstarter stretch goal!") }}</a></span></li>
|
||||
<li><a href="/docs/components/details/">Details</a></li>
|
||||
<li><a href="/docs/components/dialog/">Dialog</a></li>
|
||||
<li><a href="/docs/components/divider/">Divider</a></li>
|
||||
@@ -89,6 +103,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 +151,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 +169,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 +184,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" class="icon-shrink"></wa-icon>
|
||||
</a>
|
||||
{{ proBadge() }}
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</wa-details>
|
||||
@@ -179,15 +197,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>
|
||||
{{ proBadge() }}
|
||||
</span>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="/docs/patterns/app/action-panel/">Action Panel</a>
|
||||
@@ -218,6 +238,8 @@
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/patterns/app/pagination/">Pagination</a>
|
||||
</li><li>
|
||||
<a href="/docs/patterns/app/password/">Password</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/patterns/app/permissions/">Permissions</a>
|
||||
@@ -228,21 +250,35 @@
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/patterns/blog-news/">Blog & News</a>
|
||||
<wa-badge class="pro" appearance="accent" attention="none">PRO</wa-badge>
|
||||
<span class="wa-split">
|
||||
<a href="/docs/patterns/blog-news/">Blog & News</a>
|
||||
{{ proBadge() }}
|
||||
</span>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="/docs/patterns/blog-news/banners/">Banners</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/patterns/blog-news/call-to-action/">Call To Action</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/patterns/blog-news/category-list/">Category List</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/patterns/blog-news/contact/">Contact</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/patterns/blog-news/featured-post/">Featured Post</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/patterns/blog-news/footer/">Footer</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/patterns/blog-news/grid-section/">Grid Section</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/patterns/blog-news/header/">Header</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/patterns/blog-news/newsletter/">Newsletter</a>
|
||||
</li>
|
||||
@@ -261,14 +297,25 @@
|
||||
<li>
|
||||
<a href="/docs/patterns/blog-news/login/">Sign Up & Login</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/patterns/blog-news/numbers/">Numbers</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/patterns/blog-news/social-share/">Social Share</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/patterns/blog-news/teams/">Teams</a>
|
||||
</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>
|
||||
{{ proBadge() }}
|
||||
</span>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="/docs/patterns/ecommerce/category-filter/">Category Filter</a>
|
||||
@@ -306,8 +353,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>
|
||||
{{ proBadge() }}
|
||||
</span>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="/docs/patterns/layouts/ecommerce/">Ecommerce</a>
|
||||
@@ -329,17 +378,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>
|
||||
{{ proBadge({ description: "This requires an active Web Awesome Pro subscription", shrink: true }) }}
|
||||
</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>
|
||||
@@ -353,3 +404,37 @@
|
||||
<li><a href="/docs/tokens/component-groups/">Component Groups</a></li>
|
||||
</ul>
|
||||
</wa-details>
|
||||
|
||||
<wa-divider style="--spacing: var(--wa-space-xl);"></wa-divider>
|
||||
|
||||
<div class="wa-stack wa-gap-xl" id="colophon">
|
||||
<div class="wa-stack wa-gap-xs">
|
||||
{% include "logo-simple.njk" %}
|
||||
<div class="wa-cluster wa-gap-xs">
|
||||
<h2 class="wa-heading-xs">Web Awesome</h2>
|
||||
<wa-badge id="version-beta-badge" variant="orange" appearance="filled" style="font-size: var(--wa-font-size-2xs);">Beta</wa-badge>
|
||||
<wa-tooltip for="version-beta-badge" distance="2" style="font-size: var(--wa-font-size-xs);">Here be freshly made Awesome… and possible dragons</wa-tooltip>
|
||||
</div>
|
||||
<p class="wa-caption-s">Version {{ package.version }}</p>
|
||||
<p class="wa-caption-s">© Fonticons, Inc.</p>
|
||||
</div>
|
||||
|
||||
<div class="wa-cluster wa-gap-0 wa-caption-s the-socials">
|
||||
<h2 class="wa-visually-hidden">Web Awesome Elsewhere</h2>
|
||||
<a href="https://github.com/shoelace-style/webawesome" rel="noopener noreferrer" target="_blank" class="appearance-plain">
|
||||
<wa-icon family="brands" name="github" label="GitHub"></wa-icon>
|
||||
</a>
|
||||
<a href="https://bsky.app/profile/webawesome.com" rel="noopener noreferrer" target="_blank" class="appearance-plain">
|
||||
<wa-icon family="brands" name="bluesky" label="Bluesky"></wa-icon>
|
||||
</a>
|
||||
<a href="https://mastodon.social/@webawesome" rel="noopener noreferrer" target="_blank" class="appearance-plain">
|
||||
<wa-icon family="brands" name="mastodon" label="Mastodon"></wa-icon>
|
||||
</a>
|
||||
<a href="https://x.com/webawesomer" rel="noopener noreferrer" target="_blank" class="appearance-plain">
|
||||
<wa-icon family="brands" name="x-twitter" label="Twitter (X)"></wa-icon>
|
||||
</a>
|
||||
<a href="https://www.threads.com/@web.awesome" rel="noopener noreferrer" target="_blank" class="appearance-plain">
|
||||
<wa-icon family="brands" name="threads" label="Threads"></wa-icon>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
<wa-select appearance="filled" size="small" value="default" class="theme-selector">
|
||||
<wa-icon slot="start" name="paintbrush" variant="regular"></wa-icon>
|
||||
<wa-dropdown class="theme-selector" title="Select Theme">
|
||||
<wa-button slot="trigger" id="theme-selector-trigger" appearance="plain">
|
||||
<wa-icon name="paintbrush" variant="regular" class="icon-embiggen"></wa-icon>
|
||||
</wa-button>
|
||||
|
||||
{# Free themes #}
|
||||
{% for theme in themer.themes %}
|
||||
{% if not theme.isPro %}
|
||||
<wa-option
|
||||
<wa-dropdown-item
|
||||
value="{{ theme.filename | stripExtension }}"
|
||||
data-brand="{{ theme.colorBrand.color }}"
|
||||
data-palette="{{ theme.palette.filename | stripExtension }}"
|
||||
>
|
||||
{{ theme.name }}
|
||||
</wa-option>
|
||||
</wa-dropdown-item>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
@@ -18,24 +20,27 @@
|
||||
{% for theme in themer.themes %}
|
||||
{% if loop.first %}<wa-divider></wa-divider>{% endif %}
|
||||
{% if theme.isPro %}
|
||||
<wa-option
|
||||
<wa-dropdown-item
|
||||
value="{{ theme.filename | stripExtension }}"
|
||||
data-brand="{{ theme.colorBrand.color }}"
|
||||
data-palette="{{ theme.palette.filename | stripExtension }}"
|
||||
>
|
||||
{{ theme.name }}
|
||||
</wa-option>
|
||||
</wa-dropdown-item>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</wa-select>
|
||||
</wa-dropdown>
|
||||
<wa-tooltip for="theme-selector-trigger" id="theme-tooltip">Select Theme</wa-tooltip>
|
||||
|
||||
<script>
|
||||
// Immediately set the correct values from storage
|
||||
document.querySelectorAll('wa-select.theme-selector').forEach(el => {
|
||||
el.setAttribute('value', localStorage.getItem('theme') || 'default');
|
||||
});
|
||||
// Handle dropdown selection and trigger input event for theme.js
|
||||
document.querySelectorAll('wa-dropdown.theme-selector').forEach(el => {
|
||||
el.addEventListener('wa-select', (event) => {
|
||||
const selectedValue = event.detail.item.value;
|
||||
|
||||
// Apply saved brand and palette classes
|
||||
document.documentElement.classList.add(`wa-brand-${localStorage.getItem('brand') || 'blue'}`);
|
||||
document.documentElement.classList.add(`wa-palette-${localStorage.getItem('palette') || 'default'}`);
|
||||
// Trigger input event for theme.js
|
||||
el.value = selectedValue;
|
||||
el.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -12,22 +12,18 @@
|
||||
<th><code>brand</code></th>
|
||||
<td>
|
||||
<div class="wa-cluster wa-gap-2xs">
|
||||
<wa-badge variant="brand" appearance="accent outlined">A+O</wa-badge>
|
||||
<wa-badge variant="brand" appearance="accent">Accent</wa-badge>
|
||||
<wa-badge variant="brand" appearance="filled outlined">F+O</wa-badge>
|
||||
<wa-badge variant="brand" appearance="filled outlined">Filled + Outlined</wa-badge>
|
||||
<wa-badge variant="brand" appearance="filled">Filled</wa-badge>
|
||||
<wa-badge variant="brand" appearance="outlined">Outlined</wa-badge>
|
||||
<wa-badge variant="brand" appearance="plain">Plain</wa-badge>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="wa-cluster wa-gap-2xs">
|
||||
<wa-badge class="wa-brand" appearance="accent outlined">A+O</wa-badge>
|
||||
<wa-badge class="wa-brand" appearance="accent">Accent</wa-badge>
|
||||
<wa-badge class="wa-brand" appearance="filled outlined">F+O</wa-badge>
|
||||
<wa-badge class="wa-brand" appearance="filled outlined">Filled + Outlined</wa-badge>
|
||||
<wa-badge class="wa-brand" appearance="filled">Filled</wa-badge>
|
||||
<wa-badge class="wa-brand" appearance="outlined">Outlined</wa-badge>
|
||||
<wa-badge class="wa-brand" appearance="plain">Plain</wa-badge>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -35,22 +31,18 @@
|
||||
<th><code>neutral</code></th>
|
||||
<td>
|
||||
<div class="wa-cluster wa-gap-2xs">
|
||||
<wa-badge variant="neutral" appearance="accent outlined">A+O</wa-badge>
|
||||
<wa-badge variant="neutral" appearance="accent">Accent</wa-badge>
|
||||
<wa-badge variant="neutral" appearance="filled outlined">F+O</wa-badge>
|
||||
<wa-badge variant="neutral" appearance="filled outlined">Filled + Outlined</wa-badge>
|
||||
<wa-badge variant="neutral" appearance="filled">Filled</wa-badge>
|
||||
<wa-badge variant="neutral" appearance="outlined">Outlined</wa-badge>
|
||||
<wa-badge variant="neutral" appearance="plain">Plain</wa-badge>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="wa-cluster wa-gap-2xs">
|
||||
<wa-badge class="wa-neutral" appearance="accent outlined">A+O</wa-badge>
|
||||
<wa-badge class="wa-neutral" appearance="accent">Accent</wa-badge>
|
||||
<wa-badge class="wa-neutral" appearance="filled outlined">F+O</wa-badge>
|
||||
<wa-badge class="wa-neutral" appearance="filled outlined">Filled + Outlined</wa-badge>
|
||||
<wa-badge class="wa-neutral" appearance="filled">Filled</wa-badge>
|
||||
<wa-badge class="wa-neutral" appearance="outlined">Outlined</wa-badge>
|
||||
<wa-badge class="wa-neutral" appearance="plain">Plain</wa-badge>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -58,22 +50,18 @@
|
||||
<th><code>success</code></th>
|
||||
<td>
|
||||
<div class="wa-cluster wa-gap-2xs">
|
||||
<wa-badge variant="success" appearance="accent outlined">A+O</wa-badge>
|
||||
<wa-badge variant="success" appearance="accent">Accent</wa-badge>
|
||||
<wa-badge variant="success" appearance="filled outlined">F+O</wa-badge>
|
||||
<wa-badge variant="success" appearance="filled outlined">Filled + Outlined</wa-badge>
|
||||
<wa-badge variant="success" appearance="filled">Filled</wa-badge>
|
||||
<wa-badge variant="success" appearance="outlined">Outlined</wa-badge>
|
||||
<wa-badge variant="success" appearance="plain">Plain</wa-badge>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="wa-cluster wa-gap-2xs">
|
||||
<wa-badge class="wa-success" appearance="accent outlined">A+O</wa-badge>
|
||||
<wa-badge class="wa-success" appearance="accent">Accent</wa-badge>
|
||||
<wa-badge class="wa-success" appearance="filled outlined">F+O</wa-badge>
|
||||
<wa-badge class="wa-success" appearance="filled outlined">Filled + Outlined</wa-badge>
|
||||
<wa-badge class="wa-success" appearance="filled">Filled</wa-badge>
|
||||
<wa-badge class="wa-success" appearance="outlined">Outlined</wa-badge>
|
||||
<wa-badge class="wa-success" appearance="plain">Plain</wa-badge>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -81,22 +69,18 @@
|
||||
<th><code>warning</code></th>
|
||||
<td>
|
||||
<div class="wa-cluster wa-gap-2xs">
|
||||
<wa-badge variant="warning" appearance="accent outlined">A+O</wa-badge>
|
||||
<wa-badge variant="warning" appearance="accent">Accent</wa-badge>
|
||||
<wa-badge variant="warning" appearance="filled outlined">F+O</wa-badge>
|
||||
<wa-badge variant="warning" appearance="filled outlined">Filled + Outlined</wa-badge>
|
||||
<wa-badge variant="warning" appearance="filled">Filled</wa-badge>
|
||||
<wa-badge variant="warning" appearance="outlined">Outlined</wa-badge>
|
||||
<wa-badge variant="warning" appearance="plain">Plain</wa-badge>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="wa-cluster wa-gap-2xs">
|
||||
<wa-badge class="wa-warning" appearance="accent outlined">A+O</wa-badge>
|
||||
<wa-badge class="wa-warning" appearance="accent">Accent</wa-badge>
|
||||
<wa-badge class="wa-warning" appearance="filled outlined">F+O</wa-badge>
|
||||
<wa-badge class="wa-warning" appearance="filled outlined">Filled + Outlined</wa-badge>
|
||||
<wa-badge class="wa-warning" appearance="filled">Filled</wa-badge>
|
||||
<wa-badge class="wa-warning" appearance="outlined">Outlined</wa-badge>
|
||||
<wa-badge class="wa-warning" appearance="plain">Plain</wa-badge>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -104,22 +88,18 @@
|
||||
<th><code>danger</code></th>
|
||||
<td>
|
||||
<div class="wa-cluster wa-gap-2xs">
|
||||
<wa-badge variant="danger" appearance="accent outlined">A+O</wa-badge>
|
||||
<wa-badge variant="danger" appearance="accent">Accent</wa-badge>
|
||||
<wa-badge variant="danger" appearance="filled outlined">F+O</wa-badge>
|
||||
<wa-badge variant="danger" appearance="filled outlined">Filled + Outlined</wa-badge>
|
||||
<wa-badge variant="danger" appearance="filled">Filled</wa-badge>
|
||||
<wa-badge variant="danger" appearance="outlined">Outlined</wa-badge>
|
||||
<wa-badge variant="danger" appearance="plain">Plain</wa-badge>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="wa-cluster wa-gap-2xs">
|
||||
<wa-badge class="wa-danger" appearance="accent outlined">A+O</wa-badge>
|
||||
<wa-badge class="wa-danger" appearance="accent">Accent</wa-badge>
|
||||
<wa-badge class="wa-danger" appearance="filled outlined">F+O</wa-badge>
|
||||
<wa-badge class="wa-danger" appearance="filled outlined">Filled + Outlined</wa-badge>
|
||||
<wa-badge class="wa-danger" appearance="filled">Filled</wa-badge>
|
||||
<wa-badge class="wa-danger" appearance="outlined">Outlined</wa-badge>
|
||||
<wa-badge class="wa-danger" appearance="plain">Plain</wa-badge>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -142,9 +122,8 @@
|
||||
<th><code>brand</code></th>
|
||||
<td>
|
||||
<div class="wa-cluster wa-gap-2xs">
|
||||
<wa-button variant="brand" appearance="accent outlined">A+O</wa-button>
|
||||
<wa-button variant="brand" appearance="accent">Accent</wa-button>
|
||||
<wa-button variant="brand" appearance="filled outlined">F+O</wa-button>
|
||||
<wa-button variant="brand" appearance="filled outlined">Filled + Outlined</wa-button>
|
||||
<wa-button variant="brand" appearance="filled">Filled</wa-button>
|
||||
<wa-button variant="brand" appearance="outlined">Outlined</wa-button>
|
||||
<wa-button variant="brand" appearance="plain">Plain</wa-button>
|
||||
@@ -152,9 +131,8 @@
|
||||
</td>
|
||||
<td>
|
||||
<div class="wa-cluster wa-gap-2xs">
|
||||
<wa-button class="wa-brand" appearance="accent outlined">A+O</wa-button>
|
||||
<wa-button class="wa-brand" appearance="accent">Accent</wa-button>
|
||||
<wa-button class="wa-brand" appearance="filled outlined">F+O</wa-button>
|
||||
<wa-button class="wa-brand" appearance="filled outlined">Filled + Outlined</wa-button>
|
||||
<wa-button class="wa-brand" appearance="filled">Filled</wa-button>
|
||||
<wa-button class="wa-brand" appearance="outlined">Outlined</wa-button>
|
||||
<wa-button class="wa-brand" appearance="plain">Plain</wa-button>
|
||||
@@ -165,9 +143,8 @@
|
||||
<th><code>neutral</code></th>
|
||||
<td>
|
||||
<div class="wa-cluster wa-gap-2xs">
|
||||
<wa-button variant="neutral" appearance="accent outlined">A+O</wa-button>
|
||||
<wa-button variant="neutral" appearance="accent">Accent</wa-button>
|
||||
<wa-button variant="neutral" appearance="filled outlined">F+O</wa-button>
|
||||
<wa-button variant="neutral" appearance="filled outlined">Filled + Outlined</wa-button>
|
||||
<wa-button variant="neutral" appearance="filled">Filled</wa-button>
|
||||
<wa-button variant="neutral" appearance="outlined">Outlined</wa-button>
|
||||
<wa-button variant="neutral" appearance="plain">Plain</wa-button>
|
||||
@@ -175,9 +152,8 @@
|
||||
</td>
|
||||
<td>
|
||||
<div class="wa-cluster wa-gap-2xs">
|
||||
<wa-button class="wa-neutral" appearance="accent outlined">A+O</wa-button>
|
||||
<wa-button class="wa-neutral" appearance="accent">Accent</wa-button>
|
||||
<wa-button class="wa-neutral" appearance="filled outlined">F+O</wa-button>
|
||||
<wa-button class="wa-neutral" appearance="filled outlined">Filled + Outlined</wa-button>
|
||||
<wa-button class="wa-neutral" appearance="filled">Filled</wa-button>
|
||||
<wa-button class="wa-neutral" appearance="outlined">Outlined</wa-button>
|
||||
<wa-button class="wa-neutral" appearance="plain">Plain</wa-button>
|
||||
@@ -188,9 +164,8 @@
|
||||
<th><code>success</code></th>
|
||||
<td>
|
||||
<div class="wa-cluster wa-gap-2xs">
|
||||
<wa-button variant="success" appearance="accent outlined">A+O</wa-button>
|
||||
<wa-button variant="success" appearance="accent">Accent</wa-button>
|
||||
<wa-button variant="success" appearance="filled outlined">F+O</wa-button>
|
||||
<wa-button variant="success" appearance="filled outlined">Filled + Outlined</wa-button>
|
||||
<wa-button variant="success" appearance="filled">Filled</wa-button>
|
||||
<wa-button variant="success" appearance="outlined">Outlined</wa-button>
|
||||
<wa-button variant="success" appearance="plain">Plain</wa-button>
|
||||
@@ -198,9 +173,8 @@
|
||||
</td>
|
||||
<td>
|
||||
<div class="wa-cluster wa-gap-2xs">
|
||||
<wa-button class="wa-success" appearance="accent outlined">A+O</wa-button>
|
||||
<wa-button class="wa-success" appearance="accent">Accent</wa-button>
|
||||
<wa-button class="wa-success" appearance="filled outlined">F+O</wa-button>
|
||||
<wa-button class="wa-success" appearance="filled outlined">Filled + Outlined</wa-button>
|
||||
<wa-button class="wa-success" appearance="filled">Filled</wa-button>
|
||||
<wa-button class="wa-success" appearance="outlined">Outlined</wa-button>
|
||||
<wa-button class="wa-success" appearance="plain">Plain</wa-button>
|
||||
@@ -211,9 +185,8 @@
|
||||
<th><code>warning</code></th>
|
||||
<td>
|
||||
<div class="wa-cluster wa-gap-2xs">
|
||||
<wa-button variant="warning" appearance="accent outlined">A+O</wa-button>
|
||||
<wa-button variant="warning" appearance="accent">Accent</wa-button>
|
||||
<wa-button variant="warning" appearance="filled outlined">F+O</wa-button>
|
||||
<wa-button variant="warning" appearance="filled outlined">Filled + Outlined</wa-button>
|
||||
<wa-button variant="warning" appearance="filled">Filled</wa-button>
|
||||
<wa-button variant="warning" appearance="outlined">Outlined</wa-button>
|
||||
<wa-button variant="warning" appearance="plain">Plain</wa-button>
|
||||
@@ -221,9 +194,8 @@
|
||||
</td>
|
||||
<td>
|
||||
<div class="wa-cluster wa-gap-2xs">
|
||||
<wa-button class="wa-warning" appearance="accent outlined">A+O</wa-button>
|
||||
<wa-button class="wa-warning" appearance="accent">Accent</wa-button>
|
||||
<wa-button class="wa-warning" appearance="filled outlined">F+O</wa-button>
|
||||
<wa-button class="wa-warning" appearance="filled outlined">Filled + Outlined</wa-button>
|
||||
<wa-button class="wa-warning" appearance="filled">Filled</wa-button>
|
||||
<wa-button class="wa-warning" appearance="outlined">Outlined</wa-button>
|
||||
<wa-button class="wa-warning" appearance="plain">Plain</wa-button>
|
||||
@@ -234,9 +206,8 @@
|
||||
<th><code>danger</code></th>
|
||||
<td>
|
||||
<div class="wa-cluster wa-gap-2xs">
|
||||
<wa-button variant="danger" appearance="accent outlined">A+O</wa-button>
|
||||
<wa-button variant="danger" appearance="accent">Accent</wa-button>
|
||||
<wa-button variant="danger" appearance="filled outlined">F+O</wa-button>
|
||||
<wa-button variant="danger" appearance="filled outlined">Filled + Outlined</wa-button>
|
||||
<wa-button variant="danger" appearance="filled">Filled</wa-button>
|
||||
<wa-button variant="danger" appearance="outlined">Outlined</wa-button>
|
||||
<wa-button variant="danger" appearance="plain">Plain</wa-button>
|
||||
@@ -244,9 +215,8 @@
|
||||
</td>
|
||||
<td>
|
||||
<div class="wa-cluster wa-gap-2xs">
|
||||
<wa-button class="wa-danger" appearance="accent outlined">A+O</wa-button>
|
||||
<wa-button class="wa-danger" appearance="accent">Accent</wa-button>
|
||||
<wa-button class="wa-danger" appearance="filled outlined">F+O</wa-button>
|
||||
<wa-button class="wa-danger" appearance="filled outlined">Filled + Outlined</wa-button>
|
||||
<wa-button class="wa-danger" appearance="filled">Filled</wa-button>
|
||||
<wa-button class="wa-danger" appearance="outlined">Outlined</wa-button>
|
||||
<wa-button class="wa-danger" appearance="plain">Plain</wa-button>
|
||||
@@ -272,17 +242,13 @@
|
||||
<th><code>brand</code></th>
|
||||
<td>
|
||||
<div class="wa-grid wa-gap-2xs">
|
||||
<wa-callout variant="brand" appearance="accent outlined">
|
||||
<wa-icon slot="icon" name="circle-star"></wa-icon>
|
||||
A+O
|
||||
</wa-callout>
|
||||
<wa-callout variant="brand" appearance="accent">
|
||||
<wa-icon slot="icon" name="circle-star"></wa-icon>
|
||||
Accent
|
||||
</wa-callout>
|
||||
<wa-callout variant="brand" appearance="filled outlined">
|
||||
<wa-icon slot="icon" name="circle-star"></wa-icon>
|
||||
F+O
|
||||
Filled + Outlined
|
||||
</wa-callout>
|
||||
<wa-callout variant="brand" appearance="filled">
|
||||
<wa-icon slot="icon" name="circle-star"></wa-icon>
|
||||
@@ -300,17 +266,13 @@
|
||||
</td>
|
||||
<td>
|
||||
<div class="wa-grid wa-gap-2xs">
|
||||
<wa-callout class="wa-brand" appearance="accent outlined">
|
||||
<wa-icon slot="icon" name="circle-star"></wa-icon>
|
||||
A+O
|
||||
</wa-callout>
|
||||
<wa-callout class="wa-brand" appearance="accent">
|
||||
<wa-icon slot="icon" name="circle-star"></wa-icon>
|
||||
Accent
|
||||
</wa-callout>
|
||||
<wa-callout class="wa-brand" appearance="filled outlined">
|
||||
<wa-icon slot="icon" name="circle-star"></wa-icon>
|
||||
F+O
|
||||
Filled + Outlined
|
||||
</wa-callout>
|
||||
<wa-callout class="wa-brand" appearance="filled">
|
||||
<wa-icon slot="icon" name="circle-star"></wa-icon>
|
||||
@@ -331,17 +293,13 @@
|
||||
<th><code>neutral</code></th>
|
||||
<td>
|
||||
<div class="wa-grid wa-gap-2xs">
|
||||
<wa-callout variant="neutral" appearance="accent outlined">
|
||||
<wa-icon slot="icon" name="circle-info"></wa-icon>
|
||||
A+O
|
||||
</wa-callout>
|
||||
<wa-callout variant="neutral" appearance="accent">
|
||||
<wa-icon slot="icon" name="circle-info"></wa-icon>
|
||||
Accent
|
||||
</wa-callout>
|
||||
<wa-callout variant="neutral" appearance="filled outlined">
|
||||
<wa-icon slot="icon" name="circle-info"></wa-icon>
|
||||
F+O
|
||||
Filled + Outlined
|
||||
</wa-callout>
|
||||
<wa-callout variant="neutral" appearance="filled">
|
||||
<wa-icon slot="icon" name="circle-info"></wa-icon>
|
||||
@@ -359,17 +317,13 @@
|
||||
</td>
|
||||
<td>
|
||||
<div class="wa-grid wa-gap-2xs">
|
||||
<wa-callout class="wa-neutral" appearance="accent outlined">
|
||||
<wa-icon slot="icon" name="circle-info"></wa-icon>
|
||||
A+O
|
||||
</wa-callout>
|
||||
<wa-callout class="wa-neutral" appearance="accent">
|
||||
<wa-icon slot="icon" name="circle-info"></wa-icon>
|
||||
Accent
|
||||
</wa-callout>
|
||||
<wa-callout class="wa-neutral" appearance="filled outlined">
|
||||
<wa-icon slot="icon" name="circle-info"></wa-icon>
|
||||
F+O
|
||||
Filled + Outlined
|
||||
</wa-callout>
|
||||
<wa-callout class="wa-neutral" appearance="filled">
|
||||
<wa-icon slot="icon" name="circle-info"></wa-icon>
|
||||
@@ -390,17 +344,13 @@
|
||||
<th><code>success</code></th>
|
||||
<td>
|
||||
<div class="wa-grid wa-gap-2xs">
|
||||
<wa-callout variant="success" appearance="accent outlined">
|
||||
<wa-icon slot="icon" name="circle-check"></wa-icon>
|
||||
A+O
|
||||
</wa-callout>
|
||||
<wa-callout variant="success" appearance="accent">
|
||||
<wa-icon slot="icon" name="circle-check"></wa-icon>
|
||||
Accent
|
||||
</wa-callout>
|
||||
<wa-callout variant="success" appearance="filled outlined">
|
||||
<wa-icon slot="icon" name="circle-check"></wa-icon>
|
||||
F+O
|
||||
Filled + Outlined
|
||||
</wa-callout>
|
||||
<wa-callout variant="success" appearance="filled">
|
||||
<wa-icon slot="icon" name="circle-check"></wa-icon>
|
||||
@@ -418,17 +368,13 @@
|
||||
</td>
|
||||
<td>
|
||||
<div class="wa-grid wa-gap-2xs">
|
||||
<wa-callout class="wa-success" appearance="accent outlined">
|
||||
<wa-icon slot="icon" name="circle-check"></wa-icon>
|
||||
A+O
|
||||
</wa-callout>
|
||||
<wa-callout class="wa-success" appearance="accent">
|
||||
<wa-icon slot="icon" name="circle-check"></wa-icon>
|
||||
Accent
|
||||
</wa-callout>
|
||||
<wa-callout class="wa-success" appearance="filled outlined">
|
||||
<wa-icon slot="icon" name="circle-check"></wa-icon>
|
||||
F+O
|
||||
Filled + Outlined
|
||||
</wa-callout>
|
||||
<wa-callout class="wa-success" appearance="filled">
|
||||
<wa-icon slot="icon" name="circle-check"></wa-icon>
|
||||
@@ -449,17 +395,13 @@
|
||||
<th><code>warning</code></th>
|
||||
<td>
|
||||
<div class="wa-grid wa-gap-2xs">
|
||||
<wa-callout variant="warning" appearance="accent outlined">
|
||||
<wa-icon slot="icon" name="circle-exclamation"></wa-icon>
|
||||
A+O
|
||||
</wa-callout>
|
||||
<wa-callout variant="warning" appearance="accent">
|
||||
<wa-icon slot="icon" name="circle-exclamation"></wa-icon>
|
||||
Accent
|
||||
</wa-callout>
|
||||
<wa-callout variant="warning" appearance="filled outlined">
|
||||
<wa-icon slot="icon" name="circle-exclamation"></wa-icon>
|
||||
F+O
|
||||
Filled + Outlined
|
||||
</wa-callout>
|
||||
<wa-callout variant="warning" appearance="filled">
|
||||
<wa-icon slot="icon" name="circle-exclamation"></wa-icon>
|
||||
@@ -477,17 +419,13 @@
|
||||
</td>
|
||||
<td>
|
||||
<div class="wa-grid wa-gap-2xs">
|
||||
<wa-callout class="wa-warning" appearance="accent outlined">
|
||||
<wa-icon slot="icon" name="circle-exclamation"></wa-icon>
|
||||
A+O
|
||||
</wa-callout>
|
||||
<wa-callout class="wa-warning" appearance="accent">
|
||||
<wa-icon slot="icon" name="circle-exclamation"></wa-icon>
|
||||
Accent
|
||||
</wa-callout>
|
||||
<wa-callout class="wa-warning" appearance="filled outlined">
|
||||
<wa-icon slot="icon" name="circle-exclamation"></wa-icon>
|
||||
F+O
|
||||
Filled + Outlined
|
||||
</wa-callout>
|
||||
<wa-callout class="wa-warning" appearance="filled">
|
||||
<wa-icon slot="icon" name="circle-exclamation"></wa-icon>
|
||||
@@ -508,17 +446,13 @@
|
||||
<th><code>danger</code></th>
|
||||
<td>
|
||||
<div class="wa-grid wa-gap-2xs">
|
||||
<wa-callout variant="danger" appearance="accent outlined">
|
||||
<wa-icon slot="icon" name="circle-xmark"></wa-icon>
|
||||
A+O
|
||||
</wa-callout>
|
||||
<wa-callout variant="danger" appearance="accent">
|
||||
<wa-icon slot="icon" name="circle-xmark"></wa-icon>
|
||||
Accent
|
||||
</wa-callout>
|
||||
<wa-callout variant="danger" appearance="filled outlined">
|
||||
<wa-icon slot="icon" name="circle-xmark"></wa-icon>
|
||||
F+O
|
||||
Filled + Outlined
|
||||
</wa-callout>
|
||||
<wa-callout variant="danger" appearance="filled">
|
||||
<wa-icon slot="icon" name="circle-xmark"></wa-icon>
|
||||
@@ -536,17 +470,13 @@
|
||||
</td>
|
||||
<td>
|
||||
<div class="wa-grid wa-gap-2xs">
|
||||
<wa-callout class="wa-danger" appearance="accent outlined">
|
||||
<wa-icon slot="icon" name="circle-xmark"></wa-icon>
|
||||
A+O
|
||||
</wa-callout>
|
||||
<wa-callout class="wa-danger" appearance="accent">
|
||||
<wa-icon slot="icon" name="circle-xmark"></wa-icon>
|
||||
Accent
|
||||
</wa-callout>
|
||||
<wa-callout class="wa-danger" appearance="filled outlined">
|
||||
<wa-icon slot="icon" name="circle-xmark"></wa-icon>
|
||||
F+O
|
||||
Filled + Outlined
|
||||
</wa-callout>
|
||||
<wa-callout class="wa-danger" appearance="filled">
|
||||
<wa-icon slot="icon" name="circle-xmark"></wa-icon>
|
||||
@@ -582,22 +512,18 @@
|
||||
<th><code>brand</code></th>
|
||||
<td>
|
||||
<div class="wa-cluster wa-gap-2xs">
|
||||
<wa-tag variant="brand" appearance="accent outlined">A+O</wa-tag>
|
||||
<wa-tag variant="brand" appearance="accent">Accent</wa-tag>
|
||||
<wa-tag variant="brand" appearance="filled outlined">F+O</wa-tag>
|
||||
<wa-tag variant="brand" appearance="filled outlined">Filled + Outlined</wa-tag>
|
||||
<wa-tag variant="brand" appearance="filled">Filled</wa-tag>
|
||||
<wa-tag variant="brand" appearance="outlined">Outlined</wa-tag>
|
||||
<wa-tag variant="brand" appearance="plain">Plain</wa-tag>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="wa-cluster wa-gap-2xs">
|
||||
<wa-tag class="wa-brand" appearance="accent outlined">A+O</wa-tag>
|
||||
<wa-tag class="wa-brand" appearance="accent">Accent</wa-tag>
|
||||
<wa-tag class="wa-brand" appearance="filled outlined">F+O</wa-tag>
|
||||
<wa-tag class="wa-brand" appearance="filled outlined">Filled + Outlined</wa-tag>
|
||||
<wa-tag class="wa-brand" appearance="filled">Filled</wa-tag>
|
||||
<wa-tag class="wa-brand" appearance="outlined">Outlined</wa-tag>
|
||||
<wa-tag class="wa-brand" appearance="plain">Plain</wa-tag>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -605,22 +531,18 @@
|
||||
<th><code>neutral</code></th>
|
||||
<td>
|
||||
<div class="wa-cluster wa-gap-2xs">
|
||||
<wa-tag variant="neutral" appearance="accent outlined">A+O</wa-tag>
|
||||
<wa-tag variant="neutral" appearance="accent">Accent</wa-tag>
|
||||
<wa-tag variant="neutral" appearance="filled outlined">F+O</wa-tag>
|
||||
<wa-tag variant="neutral" appearance="filled outlined">Filled + Outlined</wa-tag>
|
||||
<wa-tag variant="neutral" appearance="filled">Filled</wa-tag>
|
||||
<wa-tag variant="neutral" appearance="outlined">Outlined</wa-tag>
|
||||
<wa-tag variant="neutral" appearance="plain">Plain</wa-tag>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="wa-cluster wa-gap-2xs">
|
||||
<wa-tag class="wa-neutral" appearance="accent outlined">A+O</wa-tag>
|
||||
<wa-tag class="wa-neutral" appearance="accent">Accent</wa-tag>
|
||||
<wa-tag class="wa-neutral" appearance="filled outlined">F+O</wa-tag>
|
||||
<wa-tag class="wa-neutral" appearance="filled outlined">Filled + Outlined</wa-tag>
|
||||
<wa-tag class="wa-neutral" appearance="filled">Filled</wa-tag>
|
||||
<wa-tag class="wa-neutral" appearance="outlined">Outlined</wa-tag>
|
||||
<wa-tag class="wa-neutral" appearance="plain">Plain</wa-tag>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -628,22 +550,18 @@
|
||||
<th><code>success</code></th>
|
||||
<td>
|
||||
<div class="wa-cluster wa-gap-2xs">
|
||||
<wa-tag variant="success" appearance="accent outlined">A+O</wa-tag>
|
||||
<wa-tag variant="success" appearance="accent">Accent</wa-tag>
|
||||
<wa-tag variant="success" appearance="filled outlined">F+O</wa-tag>
|
||||
<wa-tag variant="success" appearance="filled outlined">Filled + Outlined</wa-tag>
|
||||
<wa-tag variant="success" appearance="filled">Filled</wa-tag>
|
||||
<wa-tag variant="success" appearance="outlined">Outlined</wa-tag>
|
||||
<wa-tag variant="success" appearance="plain">Plain</wa-tag>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="wa-cluster wa-gap-2xs">
|
||||
<wa-tag class="wa-success" appearance="accent outlined">A+O</wa-tag>
|
||||
<wa-tag class="wa-success" appearance="accent">Accent</wa-tag>
|
||||
<wa-tag class="wa-success" appearance="filled outlined">F+O</wa-tag>
|
||||
<wa-tag class="wa-success" appearance="filled outlined">Filled + Outlined</wa-tag>
|
||||
<wa-tag class="wa-success" appearance="filled">Filled</wa-tag>
|
||||
<wa-tag class="wa-success" appearance="outlined">Outlined</wa-tag>
|
||||
<wa-tag class="wa-success" appearance="plain">Plain</wa-tag>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -651,22 +569,18 @@
|
||||
<th><code>warning</code></th>
|
||||
<td>
|
||||
<div class="wa-cluster wa-gap-2xs">
|
||||
<wa-tag variant="warning" appearance="accent outlined">A+O</wa-tag>
|
||||
<wa-tag variant="warning" appearance="accent">Accent</wa-tag>
|
||||
<wa-tag variant="warning" appearance="filled outlined">F+O</wa-tag>
|
||||
<wa-tag variant="warning" appearance="filled outlined">Filled + Outlined</wa-tag>
|
||||
<wa-tag variant="warning" appearance="filled">Filled</wa-tag>
|
||||
<wa-tag variant="warning" appearance="outlined">Outlined</wa-tag>
|
||||
<wa-tag variant="warning" appearance="plain">Plain</wa-tag>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="wa-cluster wa-gap-2xs">
|
||||
<wa-tag class="wa-warning" appearance="accent outlined">A+O</wa-tag>
|
||||
<wa-tag class="wa-warning" appearance="accent">Accent</wa-tag>
|
||||
<wa-tag class="wa-warning" appearance="filled outlined">F+O</wa-tag>
|
||||
<wa-tag class="wa-warning" appearance="filled outlined">Filled + Outlined</wa-tag>
|
||||
<wa-tag class="wa-warning" appearance="filled">Filled</wa-tag>
|
||||
<wa-tag class="wa-warning" appearance="outlined">Outlined</wa-tag>
|
||||
<wa-tag class="wa-warning" appearance="plain">Plain</wa-tag>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -674,25 +588,21 @@
|
||||
<th><code>danger</code></th>
|
||||
<td>
|
||||
<div class="wa-cluster wa-gap-2xs">
|
||||
<wa-tag variant="danger" appearance="accent outlined">A+O</wa-tag>
|
||||
<wa-tag variant="danger" appearance="accent">Accent</wa-tag>
|
||||
<wa-tag variant="danger" appearance="filled outlined">F+O</wa-tag>
|
||||
<wa-tag variant="danger" appearance="filled outlined">Filled + Outlined</wa-tag>
|
||||
<wa-tag variant="danger" appearance="filled">Filled</wa-tag>
|
||||
<wa-tag variant="danger" appearance="outlined">Outlined</wa-tag>
|
||||
<wa-tag variant="danger" appearance="plain">Plain</wa-tag>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="wa-cluster wa-gap-2xs">
|
||||
<wa-tag class="wa-danger" appearance="accent outlined">A+O</wa-tag>
|
||||
<wa-tag class="wa-danger" appearance="accent">Accent</wa-tag>
|
||||
<wa-tag class="wa-danger" appearance="filled outlined">F+O</wa-tag>
|
||||
<wa-tag class="wa-danger" appearance="filled outlined">Filled + Outlined</wa-tag>
|
||||
<wa-tag class="wa-danger" appearance="filled">Filled</wa-tag>
|
||||
<wa-tag class="wa-danger" appearance="outlined">Outlined</wa-tag>
|
||||
<wa-tag class="wa-danger" appearance="plain">Plain</wa-tag>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -40,66 +40,6 @@
|
||||
</div>
|
||||
<wa-divider></wa-divider>
|
||||
|
||||
<h3>Button Group</h3>
|
||||
|
||||
<div class="table-scroll">
|
||||
<table>
|
||||
<thead>
|
||||
<th></th>
|
||||
<th><code>size=""</code></th>
|
||||
<th><code>.wa-size-[s|m|l]</code></th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th><code>small</code>/<code>s</code></th>
|
||||
<td>
|
||||
<wa-button-group orientation="horizontal" size="small">
|
||||
<wa-button value="1">Button L</wa-button>
|
||||
<wa-button value="2">Button R</wa-button>
|
||||
</wa-button-group>
|
||||
</td>
|
||||
<td>
|
||||
<wa-button-group orientation="horizontal" class="wa-size-s">
|
||||
<wa-button value="1">Button L</wa-button>
|
||||
<wa-button value="2">Button R</wa-button>
|
||||
</wa-button-group>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><code>medium</code>/<code>m</code></th>
|
||||
<td>
|
||||
<wa-button-group orientation="horizontal" size="medium">
|
||||
<wa-button value="1">Button L</wa-button>
|
||||
<wa-button value="2">Button R</wa-button>
|
||||
</wa-button-group>
|
||||
</td>
|
||||
<td>
|
||||
<wa-button-group orientation="horizontal" class="wa-size-m">
|
||||
<wa-button value="1">Button L</wa-button>
|
||||
<wa-button value="2">Button R</wa-button>
|
||||
</wa-button-group>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><code>large</code>/<code>l</code></th>
|
||||
<td>
|
||||
<wa-button-group orientation="horizontal" size="large">
|
||||
<wa-button value="1">Button L</wa-button>
|
||||
<wa-button value="2">Button R</wa-button>
|
||||
</wa-button-group>
|
||||
</td>
|
||||
<td>
|
||||
<wa-button-group orientation="horizontal" class="wa-size-l">
|
||||
<wa-button value="1">Button L</wa-button>
|
||||
<wa-button value="2">Button R</wa-button>
|
||||
</wa-button-group>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<wa-divider></wa-divider>
|
||||
|
||||
<h3>Callout</h3>
|
||||
|
||||
<div class="table-scroll">
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" data-fa-kit-code="b10bfbde90" data-cdn-url="{% cdnUrl %}">
|
||||
<html lang="en" data-fa-kit-code="38c11e3f20" data-cdn-url="{% cdnUrl %}">
|
||||
<head>
|
||||
{% include 'head.njk' %}
|
||||
{% block head %}{% endblock %}
|
||||
</head>
|
||||
<body class="layout-{{ layout | stripExtension }}">
|
||||
<body class="layout-{{ layout | stripExtension }} page-{{ pageClass or page.fileSlug or 'home' }}">
|
||||
{% block content %}
|
||||
{{ content | safe }}
|
||||
{% endblock %}
|
||||
|
||||
8
packages/webawesome/docs/_layouts/pattern.njk
Normal file
8
packages/webawesome/docs/_layouts/pattern.njk
Normal file
@@ -0,0 +1,8 @@
|
||||
{% set hasSidebar = true %}
|
||||
{% set hasOutline = true %}
|
||||
{% set section = 'docs' %}
|
||||
|
||||
{% block beforeContent %}
|
||||
<p>{{ description }}</p>
|
||||
{% endblock %}
|
||||
{% extends "../_includes/base.njk" %}
|
||||
@@ -11,7 +11,6 @@ export function SimulateWebAwesomeApp(str) {
|
||||
},
|
||||
server: {
|
||||
head: '',
|
||||
loginOrAvatar: '',
|
||||
flashes: '',
|
||||
},
|
||||
});
|
||||
|
||||
@@ -56,7 +56,7 @@ document.addEventListener('click', event => {
|
||||
const code = codeExample.querySelector('code');
|
||||
const cdnUrl = document.documentElement.dataset.cdnUrl;
|
||||
const html =
|
||||
`<script data-fa-kit-code="b10bfbde90" type="module" src="${cdnUrl}webawesome.loader.js"></script>\n` +
|
||||
`<script data-fa-kit-code="38c11e3f20" type="module" src="${cdnUrl}webawesome.loader.js"></script>\n` +
|
||||
`<link rel="stylesheet" href="${cdnUrl}styles/webawesome.css">\n\n` +
|
||||
`${code.textContent}`;
|
||||
const css = 'html > body {\n padding: 2rem !important;\n}';
|
||||
|
||||
@@ -3,17 +3,30 @@ import { doViewTransition } from '../scripts/view-transitions.js';
|
||||
//
|
||||
// Updates the color scheme when a color scheme selector changes
|
||||
//
|
||||
function updateTheme(value) {
|
||||
async function updateTheme(value) {
|
||||
localStorage.setItem('color-scheme', value);
|
||||
|
||||
const isDark = value === 'dark' || (value === 'auto' && window.matchMedia('(prefers-color-scheme: dark)').matches);
|
||||
|
||||
doViewTransition(() => {
|
||||
// Disable tooltip during transition
|
||||
const tooltip = document.querySelector('#color-scheme-tooltip');
|
||||
if (tooltip) {
|
||||
tooltip.disabled = true;
|
||||
}
|
||||
|
||||
await doViewTransition(() => {
|
||||
document.documentElement.classList.toggle('wa-dark', isDark);
|
||||
});
|
||||
|
||||
// Sync all selectors
|
||||
// Sync all selectors and update tooltip
|
||||
document.querySelectorAll('.color-scheme-selector').forEach(el => (el.value = value));
|
||||
|
||||
// Update tooltip content and re-enable after transition completes
|
||||
if (tooltip) {
|
||||
const schemeText = value === 'light' ? 'Light' : value === 'dark' ? 'Dark' : 'System';
|
||||
tooltip.textContent = schemeText;
|
||||
tooltip.disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle changes
|
||||
|
||||
@@ -6,7 +6,20 @@ import { doViewTransition } from '../scripts/view-transitions.js';
|
||||
async function updateTheme(value, isInitialLoad = false) {
|
||||
const body = document.body;
|
||||
|
||||
// Get brand, palette, and theme name from the selected option
|
||||
const themeSelector = document.querySelector('.theme-selector');
|
||||
const selectedOption = themeSelector?.querySelector(`wa-dropdown-item[value="${value}"]`);
|
||||
let brand = selectedOption?.getAttribute('data-brand') || 'blue';
|
||||
let palette = selectedOption?.getAttribute('data-palette') || 'default';
|
||||
let themeName = selectedOption?.textContent.trim() || 'Unknown';
|
||||
|
||||
if (!isInitialLoad) {
|
||||
// Disable tooltip during theme transition
|
||||
const tooltip = document.querySelector('#theme-tooltip');
|
||||
if (tooltip) {
|
||||
tooltip.disabled = true;
|
||||
}
|
||||
|
||||
// Add fade-out class
|
||||
body.classList.add('theme-transitioning');
|
||||
|
||||
@@ -22,27 +35,29 @@ async function updateTheme(value, isInitialLoad = false) {
|
||||
});
|
||||
}
|
||||
|
||||
localStorage.setItem('theme', value);
|
||||
// Handle site theme vs regular theme
|
||||
let href = `/dist/styles/themes/${value}.css`;
|
||||
|
||||
// Get brand and palette from the selected option
|
||||
const themeSelector = document.querySelector('.theme-selector');
|
||||
const selectedOption = themeSelector?.querySelector(`wa-option[value="${value}"]`);
|
||||
const brand = selectedOption?.getAttribute('data-brand') || 'blue';
|
||||
const palette = selectedOption?.getAttribute('data-palette') || 'default';
|
||||
const htmlElement = document.documentElement;
|
||||
|
||||
localStorage.setItem('brand', brand);
|
||||
localStorage.setItem('palette', palette);
|
||||
if (document.querySelector('wa-page').dataset.pageType === 'site') {
|
||||
brand = 'orange';
|
||||
href = `/assets/styles/theme-site.css`;
|
||||
palette = 'default';
|
||||
value = 'site';
|
||||
} else {
|
||||
localStorage.setItem('brand', brand);
|
||||
localStorage.setItem('palette', palette);
|
||||
localStorage.setItem('theme', value);
|
||||
}
|
||||
|
||||
// Update theme classes
|
||||
const htmlElement = document.documentElement;
|
||||
const classesToRemove = Array.from(htmlElement.classList).filter(
|
||||
className =>
|
||||
className.startsWith('wa-theme-') || className.startsWith('wa-brand-') || className.startsWith('wa-palette-'),
|
||||
);
|
||||
const themeStylesheet = document.getElementById('theme-stylesheet');
|
||||
const href = `/dist/styles/themes/${value}.css`;
|
||||
|
||||
doViewTransition(() => {
|
||||
await doViewTransition(() => {
|
||||
// Update the theme
|
||||
if (themeStylesheet) {
|
||||
themeStylesheet.href = href;
|
||||
@@ -56,16 +71,26 @@ async function updateTheme(value, isInitialLoad = false) {
|
||||
htmlElement.classList.add(`wa-palette-${palette}`);
|
||||
|
||||
// Sync all theme selectors
|
||||
document.querySelectorAll('.theme-selector').forEach(el => (el.value = value));
|
||||
document.querySelectorAll('.theme-selector').forEach(el => {
|
||||
el.value = value;
|
||||
el.setAttribute('value', value);
|
||||
});
|
||||
|
||||
// Update tooltip content to reflect the new theme
|
||||
const tooltip = document.querySelector('#theme-tooltip');
|
||||
if (tooltip) {
|
||||
tooltip.textContent = `${themeName} Theme`;
|
||||
}
|
||||
});
|
||||
|
||||
if (!isInitialLoad) {
|
||||
// Waiting for the stylesheet and all it's imports to load is tricky. Preloading doesn't work for most themes
|
||||
// because applying the new stylesheet to the document, even without adding the `wa-theme-*` class, causes jank.
|
||||
// Suggestions welcome.
|
||||
setTimeout(() => {
|
||||
body.classList.remove('theme-transitioning');
|
||||
}, 500);
|
||||
// Remove transition class and re-enable tooltip after view transition completes
|
||||
body.classList.remove('theme-transitioning');
|
||||
|
||||
const tooltip = document.querySelector('#theme-tooltip');
|
||||
if (tooltip) {
|
||||
tooltip.disabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,5 +102,11 @@ document.addEventListener('input', event => {
|
||||
});
|
||||
|
||||
// Initialize
|
||||
const savedTheme = localStorage.getItem('theme') || 'default';
|
||||
updateTheme(savedTheme, true);
|
||||
function initializeTheme() {
|
||||
const savedTheme = localStorage.getItem('theme') || 'default';
|
||||
updateTheme(savedTheme, true);
|
||||
}
|
||||
|
||||
initializeTheme();
|
||||
|
||||
document.addEventListener('turbo:render', initializeTheme);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
@import 'search.css';
|
||||
@import 'cera-typeface.css';
|
||||
@import 'theme-icons.css';
|
||||
@import 'utils.css';
|
||||
|
||||
:root {
|
||||
--wa-brand-orange: #f36944;
|
||||
@@ -48,6 +49,16 @@ wa-page > header {
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.brand-logo {
|
||||
color: var(--wa-color-text-normal);
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.brand-logo svg {
|
||||
width: auto;
|
||||
height: 1.75rem;
|
||||
}
|
||||
|
||||
wa-button[data-toggle-nav] {
|
||||
--text-color: currentColor;
|
||||
font-size: 1rem;
|
||||
@@ -59,55 +70,6 @@ wa-page > header {
|
||||
padding-inline: 0.875rem;
|
||||
}
|
||||
}
|
||||
|
||||
svg {
|
||||
width: auto;
|
||||
height: 1.75rem;
|
||||
}
|
||||
|
||||
#docs-branding,
|
||||
#docs-toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--wa-space-s);
|
||||
|
||||
.color-scheme-selector,
|
||||
.theme-selector {
|
||||
max-inline-size: 20ch;
|
||||
}
|
||||
|
||||
wa-divider:last-child {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
#github-buttons {
|
||||
> wa-button {
|
||||
&::part(base) {
|
||||
color: var(--wa-color-on-quiet);
|
||||
background-color: var(--wa-color-fill-quiet);
|
||||
}
|
||||
> wa-icon {
|
||||
font-size: round(1.25em, 1px);
|
||||
}
|
||||
}
|
||||
|
||||
> wa-tooltip {
|
||||
--wa-tooltip-arrow-size: 0;
|
||||
font-size: var(--wa-font-size-xs);
|
||||
}
|
||||
}
|
||||
|
||||
#version-number {
|
||||
color: var(--wa-color-text-quiet);
|
||||
font-size: var(--wa-font-size-xs);
|
||||
line-height: 1;
|
||||
margin-block-start: var(--wa-space-2xs);
|
||||
}
|
||||
#version-number + wa-badge {
|
||||
font-size: var(--wa-font-size-2xs);
|
||||
text-transform: uppercase;
|
||||
}
|
||||
}
|
||||
|
||||
#sidebar,
|
||||
@@ -181,11 +143,6 @@ wa-page > header {
|
||||
|
||||
li wa-icon {
|
||||
color: var(--wa-color-text-quiet);
|
||||
vertical-align: middle;
|
||||
|
||||
&[name='flask'] {
|
||||
margin-inline: 0.125em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,37 +150,21 @@ wa-page > header {
|
||||
margin-block-end: var(--wa-space-m);
|
||||
}
|
||||
|
||||
/* Pro badges */
|
||||
wa-badge.pro {
|
||||
color: white;
|
||||
background-color: var(--wa-brand-orange);
|
||||
border-color: var(--wa-brand-orange);
|
||||
}
|
||||
|
||||
#sidebar {
|
||||
h2 {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -240,10 +181,30 @@ wa-badge.pro {
|
||||
}
|
||||
}
|
||||
|
||||
li wa-badge {
|
||||
/* adding 1 scale below --wa-font-size-2xs to handle badge proportions in sidebar nav */
|
||||
--font-size-3xs: round(calc(var(--wa-font-size-2xs) / 1.125), 1px); /* 10px */
|
||||
font-size: var(--font-size-3xs);
|
||||
}
|
||||
|
||||
wa-details {
|
||||
--show-duration: 0;
|
||||
--hide-duration: 0;
|
||||
}
|
||||
|
||||
.the-socials {
|
||||
/* nudge those linkies left */
|
||||
margin-inline-start: calc(var(--wa-space-xs) * -1);
|
||||
|
||||
a[target='_blank'] {
|
||||
color: var(--wa-color-text-quiet);
|
||||
padding-inline: var(--wa-space-xs);
|
||||
|
||||
&:hover {
|
||||
color: var(--wa-color-text-normal);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wa-button.delete {
|
||||
@@ -263,6 +224,11 @@ wa-button.delete {
|
||||
}
|
||||
}
|
||||
|
||||
/* planned sidebar item */
|
||||
.is-planned {
|
||||
color: var(--wa-color-text-quiet);
|
||||
}
|
||||
|
||||
#sidebar-close-button {
|
||||
display: none;
|
||||
}
|
||||
@@ -284,6 +250,20 @@ wa-page::part(menu) {
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
|
||||
/* smaller viewports-based navigation */
|
||||
wa-page > [slot='navigation-header'] {
|
||||
padding: 0;
|
||||
|
||||
.brand-logo svg {
|
||||
height: 1.25rem;
|
||||
}
|
||||
|
||||
/* resetting font-weight of dropdown items in .title */
|
||||
wa-dropdown {
|
||||
font-weight: var(--wa-font-weight-normal);
|
||||
}
|
||||
}
|
||||
|
||||
wa-page[view='mobile'] :is([slot='navigation-header'], [slot='navigation']) {
|
||||
padding: 0;
|
||||
|
||||
@@ -294,7 +274,7 @@ wa-page[view='mobile'] :is([slot='navigation-header'], [slot='navigation']) {
|
||||
|
||||
/* Main content */
|
||||
wa-page > main {
|
||||
max-width: 80ch;
|
||||
max-width: var(--content-width-s);
|
||||
padding: var(--wa-space-xl);
|
||||
margin-inline: auto;
|
||||
}
|
||||
@@ -400,25 +380,9 @@ h1.title {
|
||||
}
|
||||
}
|
||||
|
||||
/* Search trigger */
|
||||
wa-button#search-trigger {
|
||||
&::part(base) {
|
||||
background-color: var(--wa-form-control-background-color);
|
||||
border: var(--wa-form-control-border-width) var(--wa-form-control-border-style) var(--wa-form-control-border-color);
|
||||
box-shadow: none;
|
||||
}
|
||||
&::part(label) {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
#search-trigger kbd {
|
||||
font-size: var(--wa-font-size-2xs);
|
||||
line-height: var(--wa-form-control-value-line-height);
|
||||
}
|
||||
|
||||
/* Search list pages */
|
||||
wa-page > main:has(> .search-list) {
|
||||
max-width: 120ch;
|
||||
max-width: var(--content-width-l);
|
||||
margin-inline: auto;
|
||||
}
|
||||
|
||||
@@ -516,7 +480,6 @@ wa-card .page-name {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
--background-color-hover: transparent;
|
||||
font-family: var(--wa-font-family-code);
|
||||
|
||||
&::part(button) {
|
||||
@@ -526,6 +489,7 @@ wa-card .page-name {
|
||||
}
|
||||
|
||||
&::part(button):hover {
|
||||
background-color: transparent;
|
||||
cursor: copy;
|
||||
}
|
||||
|
||||
@@ -633,7 +597,7 @@ wa-scroller:has(.component-table) {
|
||||
|
||||
.table-arguments,
|
||||
.table-description {
|
||||
min-width: 40ch;
|
||||
min-width: var(--line-length-xs);
|
||||
}
|
||||
|
||||
.table-reflect {
|
||||
@@ -682,10 +646,10 @@ wa-scroller:has(.component-table) {
|
||||
|
||||
.page-wide {
|
||||
wa-page > main {
|
||||
max-width: 140ch;
|
||||
max-width: var(--content-width-l);
|
||||
|
||||
.max-line-length {
|
||||
max-width: 80ch;
|
||||
max-width: var(--line-length-l);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
164
packages/webawesome/docs/assets/styles/utils.css
Normal file
164
packages/webawesome/docs/assets/styles/utils.css
Normal file
@@ -0,0 +1,164 @@
|
||||
/* Monorepo Styles
|
||||
* These styles include elements that are shared across this repo as well as others included in the Web Awesome monorepo
|
||||
*/
|
||||
|
||||
@layer wa-utils {
|
||||
:root {
|
||||
/* max-width for fixed width app-based pages
|
||||
* We round() these to avoid obscure component rendering bugs caused by widths with fractional pixels */
|
||||
--content-width-l: round(up, 132ch, 1px);
|
||||
--content-width-m: round(up, 110ch, 1px);
|
||||
--content-width-s: round(up, 80ch, 1px);
|
||||
--content-width-xs: round(up, 50ch, 1px);
|
||||
--content-padding-inline: 4ch;
|
||||
--content-flow-spacing: 8ch;
|
||||
|
||||
--line-length-xs: 30ch;
|
||||
--line-length-s: 45ch;
|
||||
--line-length-m: 66ch;
|
||||
--line-length-l: 77ch;
|
||||
--line-length-xl: 90ch;
|
||||
--line-length-none: none;
|
||||
}
|
||||
|
||||
/* #region brand utilities */
|
||||
.logo-webawesome {
|
||||
color: var(--wa-brand-orange);
|
||||
}
|
||||
/* #endregion */
|
||||
|
||||
/* #region layout utilities */
|
||||
.content-container {
|
||||
margin-inline: auto;
|
||||
max-width: var(--max-width, var(--content-width-l));
|
||||
padding-inline: var(--content-padding-inline);
|
||||
}
|
||||
/* #endregion */
|
||||
|
||||
/* #region Text utilities */
|
||||
.text-truncate {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* line-length */
|
||||
.line-length {
|
||||
max-width: var(--line-length, none);
|
||||
}
|
||||
|
||||
.line-length-xs {
|
||||
--line-length: var(--line-length-xs);
|
||||
}
|
||||
.line-length-s {
|
||||
--line-length: var(--line-length-s);
|
||||
}
|
||||
.line-length-m {
|
||||
--line-length: var(--line-length-m);
|
||||
}
|
||||
.line-length-l {
|
||||
--line-length: var(--line-length-l);
|
||||
}
|
||||
.line-length-xl {
|
||||
--line-length: var(--line-length-xl);
|
||||
}
|
||||
.line-length-none {
|
||||
--line-length: none;
|
||||
}
|
||||
/* #endregion */
|
||||
|
||||
/* #region shared UI */
|
||||
/* pro badge */
|
||||
wa-badge.pro {
|
||||
color: white;
|
||||
background-color: var(--wa-brand-orange);
|
||||
border-color: var(--wa-brand-orange);
|
||||
|
||||
+ wa-tooltip {
|
||||
font-size: var(--wa-font-size-xs);
|
||||
--max-width: unset;
|
||||
}
|
||||
}
|
||||
|
||||
/* planned badge */
|
||||
wa-badge.planned {
|
||||
background-color: var(--wa-color-neutral-fill-quiet);
|
||||
color: var(--wa-color-neutral-on-quiet);
|
||||
|
||||
+ wa-tooltip {
|
||||
font-size: var(--wa-font-size-xs);
|
||||
--max-width: unset;
|
||||
}
|
||||
}
|
||||
|
||||
/* search trigger */
|
||||
wa-button#search-trigger {
|
||||
&::part(base) {
|
||||
background-color: var(--wa-form-control-background-color);
|
||||
border: var(--wa-form-control-border-width) var(--wa-form-control-border-style)
|
||||
var(--wa-form-control-border-color);
|
||||
box-shadow: none;
|
||||
}
|
||||
&::part(label) {
|
||||
text-align: start;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
#search-trigger kbd {
|
||||
font-size: var(--wa-font-size-2xs);
|
||||
line-height: var(--wa-form-control-value-line-height);
|
||||
padding-inline: 0.33em;
|
||||
}
|
||||
/* #endregion */
|
||||
|
||||
/* #region funsies */
|
||||
/* buttons with icon toggle on hover */
|
||||
wa-button .icon-hover {
|
||||
display: none;
|
||||
}
|
||||
wa-button:hover .icon-default {
|
||||
display: none;
|
||||
}
|
||||
wa-button:hover .icon-hover {
|
||||
display: inline-flex;
|
||||
}
|
||||
/* #endregion */
|
||||
|
||||
/* #region resets */
|
||||
code.appearance-plain {
|
||||
background: transparent;
|
||||
border: none;
|
||||
}
|
||||
|
||||
a.appearance-plain {
|
||||
--wa-color-text-link: var(--wa-color-text-normal);
|
||||
text-decoration: none;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
ul,
|
||||
ol {
|
||||
&.appearance-plain {
|
||||
list-style: none;
|
||||
padding-inline-start: 0;
|
||||
|
||||
li {
|
||||
padding-inline-start: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* increasing visual size of icons in certain contexts (such as in plain buttons) */
|
||||
wa-icon.icon-embiggen {
|
||||
font-size: round(1.125em, 1px);
|
||||
}
|
||||
|
||||
/* decreasing visual size of icons in certain contexts (such as in sidebar nav) */
|
||||
wa-icon.icon-shrink {
|
||||
font-size: round(0.875em, 1px);
|
||||
}
|
||||
/* #endregion */
|
||||
}
|
||||
@@ -70,7 +70,7 @@ layout: page
|
||||
{% for palette in themer.palettes %}
|
||||
<div class="palette-instructions" data-palette="{{ palette.name | lower }}" {% if not loop.first %}hidden{% endif %}>
|
||||
<p>
|
||||
To import this palette, set <code><html class="wa-theme-{{ palette.name | lower }}"></code> and import the following stylesheet:
|
||||
To import this palette, set <code><html class="wa-palette-{{ palette.name | lower }}"></code> and import the following stylesheet:
|
||||
</p>
|
||||
<pre><code class="language-html"><link rel="stylesheet" href="{% cdnUrl %}styles/color/palettes/{{ palette.filename }}" /></code></pre>
|
||||
</div>
|
||||
@@ -81,7 +81,7 @@ layout: page
|
||||
const paletteContainer = document.getElementById('color-palettes');
|
||||
const palettePicker = document.getElementById('palette-picker');
|
||||
|
||||
// Set first radio as checked and add initial theme class
|
||||
// Set first radio as checked and add initial palette class
|
||||
const firstRadio = palettePicker.querySelector('wa-radio');
|
||||
if (firstRadio) {
|
||||
firstRadio.checked = true;
|
||||
|
||||
@@ -15,41 +15,6 @@ category: Actions
|
||||
|
||||
## Examples
|
||||
|
||||
### Button Sizes
|
||||
|
||||
Unless otherwise specified,
|
||||
the size of [buttons](/docs/components/button) will be determined by the Button Group's `size` attribute.
|
||||
|
||||
```html {.example}
|
||||
<wa-button-group size="small" label="Alignment">
|
||||
<wa-button>Left</wa-button>
|
||||
<wa-button>Center</wa-button>
|
||||
<wa-button>Right</wa-button>
|
||||
</wa-button-group>
|
||||
|
||||
<br /><br />
|
||||
|
||||
<wa-button-group size="medium" label="Alignment">
|
||||
<wa-button>Left</wa-button>
|
||||
<wa-button>Center</wa-button>
|
||||
<wa-button>Right</wa-button>
|
||||
</wa-button-group>
|
||||
|
||||
<br /><br />
|
||||
|
||||
<wa-button-group size="large" label="Alignment">
|
||||
<wa-button>Left</wa-button>
|
||||
<wa-button>Center</wa-button>
|
||||
<wa-button>Right</wa-button>
|
||||
</wa-button-group>
|
||||
```
|
||||
|
||||
:::info
|
||||
While you can still set the size of [buttons](/docs/components/button) individually,
|
||||
and it will override the inherited size,
|
||||
it is rarely a good idea to mix sizes within the same button group.
|
||||
:::
|
||||
|
||||
### Vertical Button Groups
|
||||
|
||||
Set the `orientation` attribute to `vertical` to make a vertical button group.
|
||||
|
||||
@@ -183,6 +183,9 @@ Use the `start` and `end` slots to add presentational elements like `<wa-icon>`
|
||||
Use the `with-caret` attribute to add a dropdown indicator when a button will trigger a dropdown, menu, or popover.
|
||||
|
||||
```html {.example}
|
||||
<wa-button size="small" with-caret>
|
||||
<wa-icon name="gear" label="Settings"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-button size="small" with-caret>Small</wa-button>
|
||||
<wa-button size="medium" with-caret>Medium</wa-button>
|
||||
<wa-button size="large" with-caret>Large</wa-button>
|
||||
@@ -251,4 +254,4 @@ This example demonstrates how to style buttons using a custom class. This is the
|
||||
outline-offset: 4px;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
```
|
||||
|
||||
@@ -17,10 +17,8 @@ category: Organization
|
||||
This kitten is as cute as he is playful. Bring him home today!<br />
|
||||
<small class="wa-caption-m">6 weeks old</small>
|
||||
|
||||
<div slot="footer" class="wa-split">
|
||||
<wa-button variant="brand" pill>More Info</wa-button>
|
||||
<wa-rating label="Rating"></wa-rating>
|
||||
</div>
|
||||
<wa-button slot="footer" variant="brand" pill>More Info</wa-button>
|
||||
<wa-rating slot="footer-actions" label="Rating"></wa-rating>
|
||||
</wa-card>
|
||||
|
||||
<style>
|
||||
@@ -55,14 +53,11 @@ If using SSR, you need to also use the `with-header` attribute to add a header t
|
||||
|
||||
```html {.example}
|
||||
<wa-card class="card-header">
|
||||
<div slot="header" class="wa-split">
|
||||
Header Title
|
||||
<wa-button appearance="plain">
|
||||
<wa-icon name="gear" variant="solid" label="Settings"></wa-icon>
|
||||
</wa-button>
|
||||
</div>
|
||||
|
||||
<h3 slot="header">Header Title</h3>
|
||||
This card has a header. You can put all sorts of things in it!
|
||||
<wa-button appearance="plain" slot="header-actions">
|
||||
<wa-icon name="gear" variant="solid" label="Settings"></wa-icon>
|
||||
</wa-button>
|
||||
</wa-card>
|
||||
|
||||
<style>
|
||||
@@ -85,10 +80,9 @@ If using SSR, you need to also use the `with-footer` attribute to add a footer t
|
||||
<wa-card class="card-footer">
|
||||
This card has a footer. You can put all sorts of things in it!
|
||||
|
||||
<div slot="footer" class="wa-split">
|
||||
<wa-rating></wa-rating>
|
||||
<wa-button variant="brand">Preview</wa-button>
|
||||
</div>
|
||||
<wa-rating slot="footer"></wa-rating>
|
||||
|
||||
<wa-button slot="footer-actions" variant="brand">Preview</wa-button>
|
||||
</wa-card>
|
||||
|
||||
<style>
|
||||
@@ -155,3 +149,35 @@ Use the `appearance` attribute to change the card's visual appearance.
|
||||
{%- endfor %}
|
||||
</div>
|
||||
```
|
||||
|
||||
### Orientation
|
||||
|
||||
Set the `orientation` attribute to `horizontal` to create a card with a horizontal, side-by-side layout. Make sure to set a width or maximum width for the media slot. Horizontal cards do not currently contain the header and footer slots.
|
||||
<wa-callout>
|
||||
<wa-icon slot="icon" name="circle-info" variant="regular"></wa-icon>
|
||||
The `actions` slot is only available for the horizontal orientation
|
||||
</wa-callout>
|
||||
|
||||
```html {.example}
|
||||
<div class="wa-grid">
|
||||
<wa-card orientation="horizontal" class="horizontal-card">
|
||||
<img
|
||||
slot="media"
|
||||
src="https://images.unsplash.com/photo-1559209172-0ff8f6d49ff7?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=80"
|
||||
alt="A kitten sits patiently between a terracotta pot and decorative grasses."
|
||||
/>
|
||||
This is a kitten, but not just any kitten. This kitten likes walking along pallets.
|
||||
<wa-button slot="actions" variant="neutral" appearance="plain"
|
||||
><wa-icon name="ellipsis" label="actions"></wa-icon
|
||||
></wa-button>
|
||||
</wa-card>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.horizontal-card {
|
||||
img[slot='media'] {
|
||||
max-width: 300px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
@@ -344,7 +344,7 @@ The content of the carousel can be changed by adding or removing carousel items.
|
||||
|
||||
### Vertical Scrolling
|
||||
|
||||
Setting the `orientation` attribute to `vertical` will render the carousel in a vertical layout. If the content of your slides vary in height, you will need to set amn explicit `height` or `max-height` on the carousel using CSS.
|
||||
Setting the `orientation` attribute to `vertical` will render the carousel in a vertical layout. If the content of your slides vary in height, you will need to set an explicit `height` or `max-height` on the carousel using CSS.
|
||||
|
||||
```html {.example}
|
||||
<wa-carousel class="vertical" pagination orientation="vertical">
|
||||
|
||||
@@ -48,6 +48,23 @@ Use the `expand-icon` and `collapse-icon` slots to change the expand and collaps
|
||||
</style>
|
||||
```
|
||||
|
||||
### Icon Position
|
||||
|
||||
The default position for the expand and collapse icons is at the end of the summary. Set the `icon-placement` attribute to `start` to place the icon at the start of the summary.
|
||||
|
||||
```html {.example}
|
||||
<div class="wa-stack">
|
||||
<wa-details summary="Start" icon-placement="start">
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna
|
||||
aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
|
||||
</wa-details>
|
||||
<wa-details summary="End" icon-placement="end">
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna
|
||||
aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
|
||||
</wa-details>
|
||||
</div>
|
||||
```
|
||||
|
||||
### HTML in Summary
|
||||
|
||||
To use HTML in the summary, use the `summary` slot.
|
||||
@@ -68,12 +85,17 @@ Links and other interactive elements will still retain their behavior:
|
||||
|
||||
### Right-to-Left Languages
|
||||
|
||||
The details component automatically adapts to right-to-left languages:
|
||||
The details component, including its `icon-placement`, automatically adapts to right-to-left languages:
|
||||
|
||||
```html {.example}
|
||||
<wa-details summary="تبديلني" lang="ar" dir="rtl">
|
||||
استخدام طريقة لوريم إيبسوم لأنها تعطي توزيعاَ طبيعياَ -إلى حد ما- للأحرف عوضاً عن
|
||||
</wa-details>
|
||||
<div class="wa-stack">
|
||||
<wa-details summary="تبديلني" lang="ar" dir="rtl">
|
||||
استخدام طريقة لوريم إيبسوم لأنها تعطي توزيعاَ طبيعياَ -إلى حد ما- للأحرف عوضاً عن
|
||||
</wa-details>
|
||||
<wa-details summary="تبديلني" lang="ar" dir="rtl" icon-placement="start">
|
||||
استخدام طريقة لوريم إيبسوم لأنها تعطي توزيعاَ طبيعياَ -إلى حد ما- للأحرف عوضاً عن
|
||||
</wa-details>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Appearance
|
||||
|
||||
@@ -96,7 +96,7 @@ Use the `icon` slot to add icons to [dropdown items](/docs/components/dropdown-i
|
||||
Paste
|
||||
</wa-dropdown-item>
|
||||
|
||||
<wa-dropdown-item value="delete">
|
||||
<wa-dropdown-item value="delete" variant="danger">
|
||||
<wa-icon slot="icon" name="trash"></wa-icon>
|
||||
Delete
|
||||
</wa-dropdown-item>
|
||||
@@ -273,10 +273,10 @@ The distance from the panel to the trigger can be customized using the `distance
|
||||
|
||||
### Offset
|
||||
|
||||
The offset of the panel along the trigger can be customized using the `offset` attribute. This value is specified in pixels.
|
||||
The offset of the panel along the trigger can be customized using the `skidding` attribute. This value is specified in pixels.
|
||||
|
||||
```html {.example}
|
||||
<wa-dropdown offset="30">
|
||||
<wa-dropdown skidding="30">
|
||||
<wa-button slot="trigger" with-caret>Edit</wa-button>
|
||||
|
||||
<wa-dropdown-item>Cut</wa-dropdown-item>
|
||||
|
||||
@@ -7,6 +7,10 @@ category: Imagery
|
||||
|
||||
Web Awesome comes bundled with over 2,000 free icons courtesy of [Font Awesome](https://fontawesome.com/). These icons are part of the `default` icon library. Font Awesome Pro users can unlock additional icon families. Or, if you prefer, you can register your own [custom icon library](#icon-library).
|
||||
|
||||
```html {.example}
|
||||
<wa-icon name="face-awesome" variant="light" label="Awesome" style="font-size: 2em;"></wa-icon>
|
||||
```
|
||||
|
||||
:::info
|
||||
Not sure which icon to use? [Find the perfect icon over at Font Awesome!](https://fontawesome.com/search?o=r&m=free&f=brands%2Cclassic)
|
||||
:::
|
||||
@@ -17,73 +21,58 @@ Not sure which icon to use? [Find the perfect icon over at Font Awesome!](https:
|
||||
|
||||
The default icon library is Font Awesome Free, which comes with two icon families: `classic` and `brands`. Use the `family` attribute to set the icon family.
|
||||
|
||||
Many Font Awesome Pro icon families have variants such as `thin`, `light`, `regular`, and `solid`. Font Awesome Pro users can [provide their kit code](/docs/#using-font-awesome-kit-codes) to unlock additional families, including `sharp`, `duotone`, and `sharp-duotone`. For these icon families, use the `variant` attribute to set the variant.
|
||||
Many Font Awesome Pro icon families have variants such as `thin`, `light`, `regular`, and `solid`. Font Awesome Pro users can [provide their kit code](/docs/#using-font-awesome-kit-codes) to unlock additional premium icon families, including `sharp`, `duotone`, `sharp-duotone`, and additional Pro+ icon packs.
|
||||
|
||||
For supportive icon families, use the `variant` attribute to set the variant.
|
||||
|
||||
```html {.example}
|
||||
<wa-icon name="eyedropper"></wa-icon>
|
||||
<wa-icon name="grip-vertical"></wa-icon>
|
||||
<wa-icon name="play"></wa-icon>
|
||||
<wa-icon name="star"></wa-icon>
|
||||
<wa-icon name="user"></wa-icon>
|
||||
```
|
||||
|
||||
### Colors
|
||||
|
||||
Icons inherit their color from the current text color. Thus, you can set the `color` property on the `<wa-icon>` element or an ancestor to change the color.
|
||||
|
||||
```html {.example}
|
||||
<div style="color: #4a90e2;">
|
||||
<wa-icon name="exclamation-triangle"></wa-icon>
|
||||
<wa-icon name="archive"></wa-icon>
|
||||
<wa-icon name="battery-three-quarters"></wa-icon>
|
||||
<wa-icon name="bell"></wa-icon>
|
||||
<div class="wa-stack wa-gap-xl">
|
||||
<div class="wa-flank" style="--flank-size: 12ch;">
|
||||
<span>Classic</span>
|
||||
<div class="wa-cluster" style="font-size: 1.5em;">
|
||||
<wa-icon name="eyedropper"></wa-icon>
|
||||
<wa-icon variant="regular" name="grip-vertical"></wa-icon>
|
||||
<wa-icon variant="light" name="play"></wa-icon>
|
||||
<wa-icon variant="thin" name="star"></wa-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="wa-flank" style="--flank-size: 12ch;">
|
||||
<span>Duotone</span>
|
||||
<div class="wa-cluster" style="font-size: 1.5em;">
|
||||
<wa-icon family="duotone" name="eyedropper"></wa-icon>
|
||||
<wa-icon family="duotone" variant="regular" name="grip-vertical"></wa-icon>
|
||||
<wa-icon family="duotone" variant="light" name="play"></wa-icon>
|
||||
<wa-icon family="duotone" variant="thin" name="star"></wa-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="wa-flank" style="--flank-size: 12ch;">
|
||||
<span>Sharp</span>
|
||||
<div class="wa-cluster" style="font-size: 1.5em;">
|
||||
<wa-icon family="sharp" name="eyedropper"></wa-icon>
|
||||
<wa-icon family="sharp" variant="regular" name="grip-vertical"></wa-icon>
|
||||
<wa-icon family="sharp" variant="light" name="play"></wa-icon>
|
||||
<wa-icon family="sharp" variant="thin" name="star"></wa-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="wa-flank" style="--flank-size: 12ch;">
|
||||
<span>Sharp Duotone</span>
|
||||
<div class="wa-cluster" style="font-size: 1.5em;">
|
||||
<wa-icon family="sharp-duotone" name="eyedropper"></wa-icon>
|
||||
<wa-icon family="sharp-duotone" variant="regular" name="grip-vertical"></wa-icon>
|
||||
<wa-icon family="sharp-duotone" variant="light" name="play"></wa-icon>
|
||||
<wa-icon family="sharp-duotone" variant="thin" name="star"></wa-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="wa-flank" style="--flank-size: 12ch;">
|
||||
<span>Brands</span>
|
||||
<div class="wa-cluster" style="font-size: 1.5em;">
|
||||
<wa-icon family="brands" name="font-awesome"></wa-icon>
|
||||
<wa-icon family="brands" name="web-awesome"></wa-icon>
|
||||
<wa-icon family="brands" name="github"></wa-icon>
|
||||
<wa-icon family="brands" name="discord"></wa-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="color: #9013fe;">
|
||||
<wa-icon name="clock"></wa-icon>
|
||||
<wa-icon name="cloud"></wa-icon>
|
||||
<wa-icon name="download"></wa-icon>
|
||||
<wa-icon name="file"></wa-icon>
|
||||
</div>
|
||||
<div style="color: #417505;">
|
||||
<wa-icon name="flag"></wa-icon>
|
||||
<wa-icon name="heart"></wa-icon>
|
||||
<wa-icon name="image"></wa-icon>
|
||||
<wa-icon name="bolt-lightning"></wa-icon>
|
||||
</div>
|
||||
<div style="color: #f5a623;">
|
||||
<wa-icon name="microphone"></wa-icon>
|
||||
<wa-icon name="search"></wa-icon>
|
||||
<wa-icon name="star"></wa-icon>
|
||||
<wa-icon name="trash"></wa-icon>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Sizing
|
||||
|
||||
Icons are sized relative to the current font size. To change their size, set the `font-size` property on the icon itself or on a parent element as shown below.
|
||||
|
||||
```html {.example}
|
||||
<div style="font-size: 32px;">
|
||||
<wa-icon name="bell"></wa-icon>
|
||||
<wa-icon name="heart"></wa-icon>
|
||||
<wa-icon name="image"></wa-icon>
|
||||
<wa-icon name="microphone"></wa-icon>
|
||||
<wa-icon name="search"></wa-icon>
|
||||
<wa-icon name="star"></wa-icon>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Fixed Width Icons
|
||||
|
||||
By default, icons have a 1em height and a variable width. Use the `fixed-width` attribute to render the host element in a 1em by 1em box.
|
||||
|
||||
```html {.example}
|
||||
<wa-icon fixed-width name="cloud"></wa-icon>
|
||||
<wa-icon fixed-width name="user"></wa-icon>
|
||||
<wa-icon fixed-width name="truck"></wa-icon>
|
||||
<wa-icon fixed-width name="file"></wa-icon>
|
||||
<wa-icon fixed-width name="skating"></wa-icon>
|
||||
<wa-icon fixed-width name="snowplow"></wa-icon>
|
||||
```
|
||||
|
||||
### Labels
|
||||
@@ -91,9 +80,335 @@ By default, icons have a 1em height and a variable width. Use the `fixed-width`
|
||||
For non-decorative icons, use the `label` attribute to announce it to assistive devices.
|
||||
|
||||
```html {.example}
|
||||
<wa-icon name="star" label="Add to favorites"></wa-icon>
|
||||
<wa-icon name="star" label="Favorite" style="font-size: 1.5em;"></wa-icon>
|
||||
```
|
||||
|
||||
### Sizing
|
||||
|
||||
Icons are sized relative to the current font size. To change their size, set the `font-size` property on the icon itself or on a parent element as shown below.
|
||||
|
||||
```html {.example}
|
||||
<div class="wa-cluster" style="font-size: 44px;">
|
||||
<wa-icon name="bell"></wa-icon>
|
||||
<wa-icon name="heart"></wa-icon>
|
||||
<wa-icon name="image"></wa-icon>
|
||||
<wa-icon name="microphone"></wa-icon>
|
||||
<wa-icon name="search"></wa-icon>
|
||||
<wa-icon name="star"></wa-icon>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Auto Width
|
||||
|
||||
By default, icons have a `1em` height and a fixed `1.25em` width. Use the `auto-width` attribute to allow the icon to use its natural variable width.
|
||||
|
||||
```html {.example}
|
||||
Without auto-width<br />
|
||||
<div style="font-size: 1.5em; color: #193154;">
|
||||
<wa-icon family="solid" name="exclamation" style="background: lightsalmon;"></wa-icon>
|
||||
<wa-icon family="solid" name="circle-check" style="background: lightsalmon;"></wa-icon>
|
||||
<wa-icon family="solid" name="input-numeric" style="background: lightsalmon;"></wa-icon>
|
||||
<wa-icon family="solid" name="ruler-vertical" style="background: lightsalmon;"></wa-icon>
|
||||
<wa-icon family="solid" name="ruler-horizontal" style="background: lightsalmon;"></wa-icon>
|
||||
<wa-icon family="solid" name="airplay" style="background: lightsalmon;"></wa-icon>
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
With auto-width<br />
|
||||
<div style="font-size: 1.5em; color: #193154;">
|
||||
<wa-icon auto-width family="solid" name="exclamation" style="background: lightsalmon;"></wa-icon>
|
||||
<wa-icon auto-width family="solid" name="circle-check" style="background: lightsalmon;"></wa-icon>
|
||||
<wa-icon auto-width family="solid" name="input-numeric" style="background: lightsalmon;"></wa-icon>
|
||||
<wa-icon auto-width family="solid" name="ruler-vertical" style="background: lightsalmon;"></wa-icon>
|
||||
<wa-icon auto-width family="solid" name="ruler-horizontal" style="background: lightsalmon;"></wa-icon>
|
||||
<wa-icon auto-width family="solid" name="airplay" style="background: lightsalmon;"></wa-icon>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Colors
|
||||
|
||||
Icons inherit their color from the current text color. Thus, you can set the `color` property on the `<wa-icon>` element or an ancestor to change the color.
|
||||
|
||||
```html {.example}
|
||||
<div class="wa-cluster" style="font-size: 1.5em;">
|
||||
<wa-icon name="strawberry" style="color: salmon;"></wa-icon>
|
||||
<wa-icon name="crab" style="color: coral;"></wa-icon>
|
||||
<wa-icon name="sun" style="color: gold;"></wa-icon>
|
||||
<wa-icon name="leaf" style="color: mediumseagreen;"></wa-icon>
|
||||
<wa-icon name="cloud-showers-heavy" style="color: steelblue;"></wa-icon>
|
||||
<wa-icon name="cat-space" style="color: mediumpurple;"></wa-icon>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Duotone
|
||||
|
||||
Font Awesome's [Duotone icons](https://docs.fontawesome.com/web/style/duotone) change with the `color` property as well, but you can customize the primary and secondary colors independently using the `--primary-color` and `--secondary-color` custom properties. To change the opacity of either, use `--primary-opacity` and `--secondary-opacity`.
|
||||
|
||||
Note that these custom properties will not inherit and _must be applied directly to the icon_.
|
||||
|
||||
```html {.example}
|
||||
<div class="wa-stack">
|
||||
<div class="wa-cluster" style="font-size: 1.5em;">
|
||||
<wa-icon
|
||||
family="duotone"
|
||||
name="crow"
|
||||
style="--primary-color: dodgerblue; --secondary-color: gold; --secondary-opacity: 1.0;"
|
||||
></wa-icon>
|
||||
<wa-icon
|
||||
family="duotone"
|
||||
name="campfire"
|
||||
style="--primary-color: sienna; --secondary-color: red; --secondary-opacity: 1.0;"
|
||||
></wa-icon>
|
||||
<wa-icon
|
||||
family="duotone"
|
||||
name="birthday-cake"
|
||||
style="--primary-color: pink; --secondary-color: palevioletred; --secondary-opacity: 1.0;"
|
||||
></wa-icon>
|
||||
<wa-icon
|
||||
family="duotone"
|
||||
name="ear"
|
||||
style="--primary-color: sandybrown; --secondary-color: bisque; --secondary-opacity: 1.0;"
|
||||
></wa-icon>
|
||||
<wa-icon
|
||||
family="duotone"
|
||||
name="corn"
|
||||
style="--primary-color: mediumseagreen; --secondary-color: gold; --secondary-opacity: 1.0;"
|
||||
></wa-icon>
|
||||
<wa-icon
|
||||
family="duotone"
|
||||
name="cookie-bite"
|
||||
style="--primary-color: saddlebrown; --secondary-color: burlywood; --secondary-opacity: 1.0;"
|
||||
></wa-icon>
|
||||
</div>
|
||||
|
||||
<div class="wa-cluster" style="font-size: 1.5em;">
|
||||
<wa-icon
|
||||
family="duotone"
|
||||
variant="regular"
|
||||
name="crow"
|
||||
style="--primary-color: dodgerblue; --secondary-color: gold; --secondary-opacity: 1.0;"
|
||||
></wa-icon>
|
||||
<wa-icon
|
||||
family="duotone"
|
||||
variant="regular"
|
||||
name="campfire"
|
||||
style="--primary-color: sienna; --secondary-color: red; --secondary-opacity: 1.0;"
|
||||
></wa-icon>
|
||||
<wa-icon
|
||||
family="duotone"
|
||||
variant="regular"
|
||||
name="birthday-cake"
|
||||
style="--primary-color: pink; --secondary-color: palevioletred; --secondary-opacity: 1.0;"
|
||||
></wa-icon>
|
||||
<wa-icon
|
||||
family="duotone"
|
||||
variant="regular"
|
||||
name="ear"
|
||||
style="--primary-color: sandybrown; --secondary-color: bisque; --secondary-opacity: 1.0;"
|
||||
></wa-icon>
|
||||
<wa-icon
|
||||
family="duotone"
|
||||
variant="regular"
|
||||
name="corn"
|
||||
style="--primary-color: mediumseagreen; --secondary-color: gold; --secondary-opacity: 1.0;"
|
||||
></wa-icon>
|
||||
<wa-icon
|
||||
family="duotone"
|
||||
variant="regular"
|
||||
name="cookie-bite"
|
||||
style="--primary-color: saddlebrown; --secondary-color: burlywood; --secondary-opacity: 1.0;"
|
||||
></wa-icon>
|
||||
</div>
|
||||
|
||||
<div class="wa-cluster" style="font-size: 1.5em;">
|
||||
<wa-icon
|
||||
family="duotone"
|
||||
variant="light"
|
||||
name="crow"
|
||||
style="--primary-color: dodgerblue; --secondary-color: gold; --secondary-opacity: 1.0;"
|
||||
></wa-icon>
|
||||
<wa-icon
|
||||
family="duotone"
|
||||
variant="light"
|
||||
name="campfire"
|
||||
style="--primary-color: sienna; --secondary-color: red; --secondary-opacity: 1.0;"
|
||||
></wa-icon>
|
||||
<wa-icon
|
||||
family="duotone"
|
||||
variant="light"
|
||||
name="birthday-cake"
|
||||
style="--primary-color: pink; --secondary-color: palevioletred; --secondary-opacity: 1.0;"
|
||||
></wa-icon>
|
||||
<wa-icon
|
||||
family="duotone"
|
||||
variant="light"
|
||||
name="ear"
|
||||
style="--primary-color: sandybrown; --secondary-color: bisque; --secondary-opacity: 1.0;"
|
||||
></wa-icon>
|
||||
<wa-icon
|
||||
family="duotone"
|
||||
variant="light"
|
||||
name="corn"
|
||||
style="--primary-color: mediumseagreen; --secondary-color: gold; --secondary-opacity: 1.0;"
|
||||
></wa-icon>
|
||||
<wa-icon
|
||||
family="duotone"
|
||||
variant="light"
|
||||
name="cookie-bite"
|
||||
style="--primary-color: saddlebrown; --secondary-color: burlywood; --secondary-opacity: 1.0;"
|
||||
></wa-icon>
|
||||
</div>
|
||||
|
||||
<div class="wa-cluster" style="font-size: 1.5em;">
|
||||
<wa-icon
|
||||
family="duotone"
|
||||
variant="thin"
|
||||
name="crow"
|
||||
style="--primary-color: dodgerblue; --secondary-color: gold; --secondary-opacity: 1.0;"
|
||||
></wa-icon>
|
||||
<wa-icon
|
||||
family="duotone"
|
||||
variant="thin"
|
||||
name="campfire"
|
||||
style="--primary-color: sienna; --secondary-color: red; --secondary-opacity: 1.0;"
|
||||
></wa-icon>
|
||||
<wa-icon
|
||||
family="duotone"
|
||||
variant="thin"
|
||||
name="birthday-cake"
|
||||
style="--primary-color: pink; --secondary-color: palevioletred; --secondary-opacity: 1.0;"
|
||||
></wa-icon>
|
||||
<wa-icon
|
||||
family="duotone"
|
||||
variant="thin"
|
||||
name="ear"
|
||||
style="--primary-color: sandybrown; --secondary-color: bisque; --secondary-opacity: 1.0;"
|
||||
></wa-icon>
|
||||
<wa-icon
|
||||
family="duotone"
|
||||
variant="thin"
|
||||
name="corn"
|
||||
style="--primary-color: mediumseagreen; --secondary-color: gold; --secondary-opacity: 1.0;"
|
||||
></wa-icon>
|
||||
<wa-icon
|
||||
family="duotone"
|
||||
variant="thin"
|
||||
name="cookie-bite"
|
||||
style="--primary-color: saddlebrown; --secondary-color: burlywood; --secondary-opacity: 1.0;"
|
||||
></wa-icon>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
:::info
|
||||
Duotone icons can be unlocked by [providing a valid Font Awesome kit code](/docs/#using-font-awesome-kit-codes).
|
||||
:::
|
||||
|
||||
### Swap Duotone Opacity
|
||||
|
||||
For duotone icons, you can swap the primary and secondary opacity values using the `swap-opacity` attribute. This is useful when you want to emphasize the secondary layer of the icon.
|
||||
|
||||
```html {.example}
|
||||
Normal duotone<br />
|
||||
<div class="wa-cluster" style="font-size: 1.5em;">
|
||||
<wa-icon family="duotone" name="home"></wa-icon>
|
||||
<wa-icon family="duotone" name="user"></wa-icon>
|
||||
<wa-icon family="duotone" name="envelope"></wa-icon>
|
||||
<wa-icon family="duotone" name="calendar"></wa-icon>
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
Swapped duotone<br />
|
||||
<div class="wa-cluster" style="font-size: 1.5em;">
|
||||
<wa-icon family="duotone" name="home" swap-opacity></wa-icon>
|
||||
<wa-icon family="duotone" name="user" swap-opacity></wa-icon>
|
||||
<wa-icon family="duotone" name="envelope" swap-opacity></wa-icon>
|
||||
<wa-icon family="duotone" name="calendar" swap-opacity></wa-icon>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Font Awesome Pro+
|
||||
|
||||
If you're a [Font Awesome Pro+ customer](https://fontawesome.com/), you have access to even more premium icons! Just set the appropriate `family`, `variant`, and `name` on the icon.
|
||||
|
||||
```html {.example}
|
||||
<div class="wa-stack wa-gap-xl">
|
||||
<div class="wa-flank" style="--flank-size: 10ch;">
|
||||
<a href="https://fontawesome.com/icons/packs/notdog" target="_blank">Notdog</a>
|
||||
<div class="wa-cluster" style="font-size: 1.5em;">
|
||||
<wa-icon family="notdog" variant="solid" name="house"></wa-icon>
|
||||
<wa-icon
|
||||
family="notdog"
|
||||
variant="duo-solid"
|
||||
name="house"
|
||||
style="--secondary-color: skyblue; --secondary-opacity: 0.8;"
|
||||
></wa-icon>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="wa-flank" style="--flank-size: 10ch;">
|
||||
<a href="https://fontawesome.com/icons/packs/chisel" target="_blank">Chisel</a>
|
||||
<div class="wa-cluster" style="font-size: 1.5em;">
|
||||
<wa-icon family="chisel" variant="regular" name="house"></wa-icon>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="wa-flank" style="--flank-size: 10ch;">
|
||||
<a href="https://fontawesome.com/icons/packs/etch" target="_blank">Etch</a>
|
||||
<div class="wa-cluster" style="font-size: 1.5em;">
|
||||
<wa-icon family="etch" variant="solid" name="house"></wa-icon>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="wa-flank" style="--flank-size: 10ch;">
|
||||
<a href="https://fontawesome.com/icons/packs/jelly" target="_blank">Jelly</a>
|
||||
<div class="wa-cluster" style="font-size: 1.5em;">
|
||||
<wa-icon family="jelly" variant="regular" name="house"></wa-icon>
|
||||
<wa-icon
|
||||
family="jelly"
|
||||
variant="duo-regular"
|
||||
name="house"
|
||||
style="--secondary-color: skyblue; --secondary-opacity: 0.8;"
|
||||
></wa-icon>
|
||||
<wa-icon family="jelly" variant="fill-regular" name="house"></wa-icon>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="wa-flank" style="--flank-size: 10ch;">
|
||||
<a href="https://fontawesome.com/icons/packs/slab" target="_blank">Slab</a>
|
||||
<div class="wa-cluster" style="font-size: 1.5em;">
|
||||
<wa-icon family="slab" variant="regular" name="house"></wa-icon>
|
||||
<wa-icon family="slab" variant="press-regular" name="house"></wa-icon>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="wa-flank" style="--flank-size: 10ch;">
|
||||
<a href="https://fontawesome.com/icons/packs/thumbprint" target="_blank">Thumbprint</a>
|
||||
<div class="wa-cluster" style="font-size: 1.5em;">
|
||||
<wa-icon
|
||||
family="thumbprint"
|
||||
variant="light"
|
||||
name="house"
|
||||
style="--secondary-color: skyblue; --secondary-opacity: 0.8;"
|
||||
></wa-icon>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="wa-flank" style="--flank-size: 10ch;">
|
||||
<a href="https://fontawesome.com/icons/packs/whiteboard" target="_blank">Whiteboard</a>
|
||||
<div class="wa-cluster" style="font-size: 32px;">
|
||||
<wa-icon family="whiteboard" variant="semibold" name="house"></wa-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
:::info
|
||||
Pro+ icons can be unlocked by [providing a valid Font Awesome kit code](/docs/#using-font-awesome-kit-codes).
|
||||
:::
|
||||
|
||||
### Custom Icons
|
||||
|
||||
Custom icons can be loaded individually with the `src` attribute. Only SVGs on a local or CORS-enabled endpoint are supported. If you're using more than one custom icon, it might make sense to register a [custom icon library](#icon-libraries).
|
||||
@@ -287,7 +602,9 @@ Icons in this library are licensed under the [MIT License](https://github.com/lu
|
||||
import { registerIconLibrary } from '/dist/webawesome.js';
|
||||
|
||||
registerIconLibrary('iconoir', {
|
||||
resolver: name => `https://cdn.jsdelivr.net/gh/lucaburgio/iconoir@latest/icons/${name}.svg`,
|
||||
resolver: (name, family) => {
|
||||
return `https://cdn.jsdelivr.net/npm/iconoir@7.11.0/icons/regular/${name}.svg`;
|
||||
},
|
||||
mutator: svg =>
|
||||
svg.querySelectorAll('path').forEach(path => {
|
||||
path.setAttribute('fill', 'none');
|
||||
@@ -297,7 +614,7 @@ Icons in this library are licensed under the [MIT License](https://github.com/lu
|
||||
</script>
|
||||
|
||||
<div style="font-size: 24px;">
|
||||
<wa-icon library="iconoir" name="check-circled-outline"></wa-icon>
|
||||
<wa-icon library="iconoir" name="check-circle"></wa-icon>
|
||||
<wa-icon library="iconoir" name="drawer"></wa-icon>
|
||||
<wa-icon library="iconoir" name="keyframes"></wa-icon>
|
||||
<wa-icon library="iconoir" name="headset-help"></wa-icon>
|
||||
|
||||
@@ -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>
|
||||
```
|
||||
@@ -91,7 +91,7 @@ Use the `distance` attribute to control how far the popover appears from its anc
|
||||
|
||||
### Arrow Size
|
||||
|
||||
Use the `--arrow-size` custom property to change the size of the popover's arrow. Set it to `0` to remove the arrow entirely.
|
||||
Use the `--arrow-size` custom property to change the size of the popover's arrow. To remove it, use the `without-arrow` attribute.
|
||||
|
||||
```html {.example}
|
||||
<div style="display: flex; gap: 1rem; align-items: center;">
|
||||
@@ -99,7 +99,7 @@ Use the `--arrow-size` custom property to change the size of the popover's arrow
|
||||
<wa-popover for="popover__big-arrow" style="--arrow-size: 8px;">I have a big arrow</wa-popover>
|
||||
|
||||
<wa-button id="popover__no-arrow">No arrow</wa-button>
|
||||
<wa-popover for="popover__no-arrow" style="--arrow-size: 0;">I don't have an arrow</wa-popover>
|
||||
<wa-popover for="popover__no-arrow" without-arrow>I don't have an arrow</wa-popover>
|
||||
</div>
|
||||
```
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ category: Utilities
|
||||
Localization is handled by the browser's [`Intl.RelativeTimeFormat` API](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/RelativeTimeFormat). No language packs are required.
|
||||
|
||||
```html {.example}
|
||||
<!-- Web Awesome 2 release date 🎉 -->
|
||||
<!-- Shoelace 2 release date 🎉 -->
|
||||
<wa-relative-time date="2020-07-15T09:17:00-04:00"></wa-relative-time>
|
||||
```
|
||||
|
||||
|
||||
@@ -366,7 +366,7 @@ Here's a comprehensive example showing different lazy loading scenarios:
|
||||
|
||||
const option = document.createElement('wa-option');
|
||||
option.setAttribute('value', 'foo');
|
||||
option.selected = true
|
||||
option.selected = true;
|
||||
option.innerText = 'Foo';
|
||||
|
||||
// For the multiple select with existing selected options, make the new option selected
|
||||
@@ -403,4 +403,4 @@ Here's a comprehensive example showing different lazy loading scenarios:
|
||||
|
||||
:::info
|
||||
The key principle is that the select component prioritizes user interactions and explicit selections over programmatic changes, ensuring a predictable user experience even with dynamically loaded content.
|
||||
:::
|
||||
:::
|
||||
|
||||
@@ -6,7 +6,7 @@ category: Form Controls
|
||||
---
|
||||
|
||||
```html {.example}
|
||||
<wa-textarea label="Type something', will ya"></wa-textarea>
|
||||
<wa-textarea label="Type somethin', will ya"></wa-textarea>
|
||||
```
|
||||
|
||||
:::info
|
||||
|
||||
@@ -113,11 +113,11 @@ Tooltips can be controller programmatically by setting the `trigger` attribute t
|
||||
|
||||
### Removing Arrows
|
||||
|
||||
You can control the size of tooltip arrows by overriding the `--wa-tooltip-arrow-size` design token. To remove them, set the value to `0` as shown below.
|
||||
You can control the size of tooltip arrows by overriding the `--wa-tooltip-arrow-size` design token. To remove the arrow, use the `without-arrow` attribute.
|
||||
|
||||
```html {.example}
|
||||
<wa-button id="no-arrow">No Arrow</wa-button>
|
||||
<wa-tooltip for="no-arrow" style="--wa-tooltip-arrow-size: 0;">This is a tooltip with no arrow</wa-tooltip>
|
||||
<wa-tooltip for="no-arrow" without-arrow>This is a tooltip with no arrow</wa-tooltip>
|
||||
```
|
||||
|
||||
To override it globally, set it in a root block in your stylesheet after the Web Awesome stylesheet is loaded.
|
||||
|
||||
@@ -22,6 +22,7 @@ To get everything included in Web Awesome, add the following code to the `<head>
|
||||
```
|
||||
|
||||
This snippet adds:
|
||||
|
||||
- **Web Awesome styles**, a collection of stylesheets including essential default theme styles, optional [styles for native elements](/docs/utilities/native) and optional [utility classes](/docs/utilities)
|
||||
- **The autoloader**, a lightweight script watches the DOM for unregistered Web Awesome elements and lazy loads them for you — even if they're added dynamically
|
||||
|
||||
@@ -31,7 +32,7 @@ Now you can [start using Web Awesome!](/docs/usage)
|
||||
|
||||
## Using Font Awesome Kit Codes
|
||||
|
||||
Font Awesome users can set their kit code to unlock Font Awesome Pro icons. You can provide it by adding the `data-fa-kit-code` attribute to any element on the page, or by calling the `setKitCode()` method.
|
||||
Font Awesome users can provide their kit code to unlock premium icon packs. You can provide yours by adding the `data-fa-kit-code` attribute to any element on the page, or by calling the `setKitCode()` method.
|
||||
|
||||
```html
|
||||
<!-- Option 1: the data-fa-kit-code attribute -->
|
||||
@@ -44,8 +45,24 @@ Font Awesome users can set their kit code to unlock Font Awesome Pro icons. You
|
||||
</script>
|
||||
```
|
||||
|
||||
:::info
|
||||
Not a Font Awesome user yet? [Learn more about premium icon packs](https://fontawesome.com/) and sign up for an account to unlock them!
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
{# This looks weird, but without it, markdownItAttrs flags the raw calls incorrectly. #}
|
||||
|
||||
<div>
|
||||
{%- raw -%}
|
||||
{% if currentUser.hasPro %}
|
||||
<div>
|
||||
{% include "server/pro-setup.njk" ignore missing %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endraw %}
|
||||
</div>
|
||||
|
||||
## Advanced Setup
|
||||
|
||||
The autoloader is the easiest way to use Web Awesome, but different projects (or your own preferences!) may require different installation methods.
|
||||
@@ -86,15 +103,15 @@ Web Awesome does not a provide a single import with all Web Awesome components.
|
||||
|
||||
```js
|
||||
// Option 1: import all Web Awesome styles
|
||||
import "@awesome.me/webawesome/dist/styles/webawesome.css"
|
||||
import '@awesome.me/webawesome/dist/styles/webawesome.css';
|
||||
|
||||
// Option 2: import only the default theme
|
||||
import "@awesome.me/webawesome/dist/styles/themes/default.css"
|
||||
import '@awesome.me/webawesome/dist/styles/themes/default.css';
|
||||
|
||||
// <wa-button>
|
||||
import "@awesome.me/webawesome/dist/components/button/button.js"
|
||||
import '@awesome.me/webawesome/dist/components/button/button.js';
|
||||
// <wa-input>
|
||||
import "@awesome.me/webawesome/dist/components/input/input.js"
|
||||
import '@awesome.me/webawesome/dist/components/input/input.js';
|
||||
```
|
||||
|
||||
Once they've been imported, you can use them in your HTML normally. Component imports are located in the "Importing" section of each component's documentation.
|
||||
@@ -103,7 +120,7 @@ Once they've been imported, you can use them in your HTML normally. Component im
|
||||
|
||||
Some components rely on assets (icons, images, etc.) and Web Awesome needs to know where they're located. For convenience, Web Awesome will try to auto-detect the correct location based on the script you've loaded it from. This assumes assets are colocated with `webawesome.loader.js` and will "just work" for most users.
|
||||
|
||||
==If you're using the CDN, you can skip this section.== However, if you're [cherry picking](#cherry-picking) or bundling Web Awesome, you'll need to set the base path. You can do this one of two ways.
|
||||
==If you're using the CDN, you can skip this section.== However, if you're [cherry picking](#cherry-picking-from-cdn) or bundling Web Awesome, you'll need to set the base path. You can do this one of two ways.
|
||||
|
||||
```html
|
||||
<!-- Option 1: the data-webawesome attribute -->
|
||||
@@ -136,13 +153,63 @@ Most of the magic behind assets is handled internally by Web Awesome, but if you
|
||||
</script>
|
||||
```
|
||||
|
||||
## The difference between `/dist` and `/dist-cdn`
|
||||
### The Difference Between `/dist` & `/dist-cdn`
|
||||
|
||||
If you have Web Awesome installed locally via NPM, you'll notice 2 directories. `/dist-cdn` and `/cdn`.
|
||||
If you have Web Awesome installed locally via npm, you'll notice the following directories in the project's root:
|
||||
|
||||
The `/dist-cdn` files are bundled differently than the `/dist` files. The `/dist-cdn` files come pre-bundled, which means all dependencies are "inlined" so there are no "bare" references like `import "lit"`. The `/dist` files **DO NOT** come pre-bundled, allowing your bundler of choice to more efficiently de-duplicate dependencies, resulting in smaller bundles and optimal code sharing.
|
||||
```
|
||||
dist/
|
||||
dist-cdn/
|
||||
```
|
||||
|
||||
TLDR:
|
||||
The `dist-cdn` files come with everything bundled together, so you can use them directly without a build tool. The dist files keep dependencies separate, which lets your bundler optimize and share code more efficiently.
|
||||
|
||||
- `@awesome.me/webawesome/dist-cdn` is for CDNs or people not using a bundler.
|
||||
- `@awesome.me/webawesome/dist` is for bundlers or importmaps.
|
||||
Use `dist-cdn` if you're loading directly in the browser or from a CDN. Use `dist` if you're using a bundler like Webpack or Vite.
|
||||
|
||||
## React Users
|
||||
|
||||
React 19+ [supports custom elements](https://react.dev/blog/2024/04/25/react-19#support-for-custom-elements), so you can import them and use them as you'd expect. Just make sure you've included your Web Awesome theme into your app first.
|
||||
|
||||
```jsx
|
||||
import '@awesome.me/webawesome/dist/components/button/button.js';
|
||||
|
||||
function App() {
|
||||
return <wa-button variant="brand">Button</wa-button>;
|
||||
}
|
||||
|
||||
export default App;
|
||||
```
|
||||
|
||||
If you're using TypeScript, you can add type safety using the types file located at:
|
||||
|
||||
```
|
||||
node_modules/@awesome.me/webawesome/dist/custom-elements-jsx.d.ts
|
||||
```
|
||||
|
||||
This gives you inline documentation, autocomplete, and type-safe validation for every component. You can add the types to your project by updating your `tsconfig.json` file as shown below.
|
||||
|
||||
```json
|
||||
{
|
||||
"compilerOptions": {
|
||||
"types": ["node_modules/@awesome.me/webawesome/dist/custom-elements-jsx.d.ts"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Another way is to create a declaration file and extend JSX's `IntrinsicElements`:
|
||||
|
||||
```ts
|
||||
import type { CustomElements, CustomCssProperties } from '@awesome.me/webawesome/dist/custom-elements-jsx.d.ts';
|
||||
|
||||
declare module 'react' {
|
||||
namespace JSX {
|
||||
interface IntrinsicElements extends CustomElements {}
|
||||
}
|
||||
interface CSSProperties extends CustomCssProperties {}
|
||||
}
|
||||
```
|
||||
|
||||
:::details React 18 and below
|
||||
React 18 and below have [poor support](https://custom-elements-everywhere.com/#react) for custom elements. For legacy versions of React, we provide React wrappers for every component. You can find the import instructions by selecting the _React_ tab from the _Importing_ section of each
|
||||
component's documentation.
|
||||
:::
|
||||
@@ -22,43 +22,7 @@ Through the magic of a mutation observer, changing the `lang` attribute will aut
|
||||
|
||||
## Available Translations
|
||||
|
||||
Web Awesome ships with a number of translations. The default is English (US), which also serves as the fallback locale. As such, you do not need to import the English translation.
|
||||
|
||||
Available translations include:
|
||||
|
||||
<div style="columns: 3; gap: 1rem; margin-block-end: 1.5rem;">
|
||||
|
||||
- ar
|
||||
- cs
|
||||
- da
|
||||
- de-ch
|
||||
- de
|
||||
- en-gb
|
||||
- en
|
||||
- es
|
||||
- fa
|
||||
- fi
|
||||
- fr
|
||||
- he
|
||||
- hr
|
||||
- hu
|
||||
- id
|
||||
- it
|
||||
- ja
|
||||
- nb
|
||||
- nl
|
||||
- nn
|
||||
- pl
|
||||
- pt
|
||||
- ru
|
||||
- sl
|
||||
- sv
|
||||
- tr
|
||||
- uk
|
||||
- zh-cn
|
||||
- zh-tw
|
||||
|
||||
</div>
|
||||
Web Awesome ships with [a number of translations](https://github.com/shoelace-style/webawesome/tree/next/packages/webawesome/src/translations). The default is English (US), which also serves as the fallback locale. As such, you do not need to import the English translation.
|
||||
|
||||
You can import translations using the following syntax, where `<code>` is replaced with any language code shown above.
|
||||
|
||||
@@ -92,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.
|
||||
|
||||
@@ -14,7 +14,7 @@ Furthermore, accessibility doesn’t 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.
|
||||
|
||||
We’re 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.
|
||||
We’re 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.
|
||||
|
||||
|
||||
@@ -4,12 +4,80 @@ description: Changes to each version of the project are documented here.
|
||||
layout: page-outline
|
||||
---
|
||||
|
||||
<p class="wa-caption-m">Last updated: <wa-format-date month="long" day="numeric" year="numeric" date="{{ lastUpdatedISO }}"></wa-format-date></p>
|
||||
|
||||
Web Awesome follows [Semantic Versioning](https://semver.org/). Breaking changes in components with the <wa-badge variant="brand">Stable</wa-badge> badge will not be accepted until the next major version. As such, all contributions must consider the project's roadmap and take this into consideration. Features that are deemed no longer necessary will be deprecated but not removed.
|
||||
|
||||
Components with the <wa-badge variant="warning">Experimental</wa-badge> badge should not be used in production. They are made available as release candidates for development and testing purposes. As such, changes to experimental components will not be subject to semantic versioning.
|
||||
|
||||
## Next
|
||||
|
||||
- Added the Kazakh translation [pr:1496]
|
||||
- Added docs for code completion for VS Code and JetBrains [pr:1550]
|
||||
- Added back the missing `form-control-label` part to `<wa-textarea>` for consistency with other form controls [pr:1533]
|
||||
- Fixed a bug in `<wa-button>` where slotted badges weren't properly positioned in buttons with an `href` [issue:1377]
|
||||
- Fixed focus outline styles in `<wa-details>` and native `<details>` [issue:1456]
|
||||
- Fixed focus outline styles in `<wa-scroller>`, `<wa-dialog>`, and `<wa-drawer>` [issue:1484]
|
||||
- Fixed a bug that caused icon button labels to not render in frameworks [issue:1542]
|
||||
- Fixed a bug in `<wa-details>` that caused the `name` property not to reflect [pr:1538]
|
||||
|
||||
## 3.0.0-beta.6
|
||||
|
||||
- Fixed a bug in `<wa-dropdown>` that closed the dropdown event when preventing `wa-select` [issue:1432]
|
||||
- Pin `@ctrl/tinycolor` to `4.1.0` due to malware in `4.1.1` and `4.1.2`. <https://socket.dev/npm/package/@ctrl/tinycolor/overview/4.1.1>
|
||||
|
||||
## 3.0.0-beta.5
|
||||
|
||||
### Bug Fixes and Improvements {data-no-outline}
|
||||
|
||||
- 🚨 BREAKING: Updated `<wa-icon>` to use Font Awesome 7 [pr:1222]
|
||||
- Added the `auto-width` attribute to automatically size icons, since FA7 is fixed-width by default now
|
||||
- Changed the default width of icons to `1.25em` to match FA7's fixed-width proportions
|
||||
- Improved support for duotone icons in `<wa-icon>`, including custom colors, custom opacity, and opacity swapping
|
||||
- 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]
|
||||
- Fixed a bug in `<wa-dropdown>` that caused menus to overflow the viewport instead of resizing [issue:1267]
|
||||
- Fixed a bug in `<wa-dropdown>` that prevented keyboard selection of items when nested in shadow roots [issue:1270]
|
||||
- Fixed a bug in `<wa-dropdown>` that prevented items passed in from slots from being detected [issue:1271]
|
||||
- Fixed a bug in JSX typings that prevented the types file from being exported [pr:1295]
|
||||
- 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]
|
||||
- Added horizontal orientation support with `orientation="horizontal"` for `<wa-card>`
|
||||
|
||||
## 3.0.0-beta.4
|
||||
|
||||
### New Features {data-no-outline}
|
||||
|
||||
- Added the `icon-position` attribute to `<wa-details>` [discuss:1099]
|
||||
- Added the `animating` custom state to `<wa-details>` [pr:1214]
|
||||
- Added `--wa-tooltip-border-color`, `--wa-tooltip-border-style`, and `--wa-tooltip-border-width` tokens [issue:1224]
|
||||
- Added the `without-arrow` attribute to `<wa-popover>` and `<wa-tooltip>` to hide arrows without artifacts
|
||||
- Added JSX types for use with React and others [pr:1256]
|
||||
- Added `<input type="file">` to native styles [pr:1279]
|
||||
|
||||
### Bug Fixes and Improvements {data-no-outline}
|
||||
|
||||
- Fixed a bug in `<wa-details>` that caused the content to overflow the container when animating [issue:1149]
|
||||
- Fixed a bug in `<wa-dialog>` and `<wa-drawer>` that prevented the header from showing when the label was missing [issue:1209]
|
||||
- Fixed a missing dependency required for React wrappers
|
||||
- Fixed missing `:hover` and `:active` styles on native buttons without an appearance modifier class
|
||||
|
||||
## 3.0.0-beta.3
|
||||
|
||||
### New Features {data-no-outline}
|
||||
|
||||
- Added `--track-height` custom property to `<wa-progress-bar>` [pr:1154]
|
||||
@@ -22,9 +90,11 @@ Components with the <wa-badge variant="warning">Experimental</wa-badge> badge sh
|
||||
- Fixed the missing `nanoid` dependency in `package.json` [discuss:1139]
|
||||
- Fixed a bug in `<wa-slider>` that prevented the hint from showing up [discuss:1172]
|
||||
- Fixed a bug in `<wa-textarea>` where setting `resize="auto"` caused the height of the textarea to double [issue:1155]
|
||||
- Fixed a bug in `<wa-color-picker>`, `<wa-checkbox>`, `<wa-input>`, `<wa-radio-group>`, `<wa-switch>`, and `<wa-textarea>` that prevented screen readers from announcing hints [issue:1186]
|
||||
- Fixed a bug in `<wa-card>` that caused slotted media to have incorrectly rounded corners [issue:1107]
|
||||
- Fixed a bug in `<wa-button-group>` that prevented pill buttons from rendering corners properly [issue:1165]
|
||||
- Fixed a bug in `<wa-button-group>` that caused some vertical groups to appear horizontal [issue:1152]
|
||||
- Improved accessibility of `<wa-animated-image>` so keyboard users can focus and toggle the animation [issue:1177]
|
||||
|
||||
## 3.0.0-beta.2
|
||||
|
||||
@@ -173,7 +243,7 @@ Many of these changes and improvements were the direct result of feedback from u
|
||||
|
||||
### Bug fixes {data-no-outline}
|
||||
|
||||
- Specifying inherited CSS properties on `<wa-tooltip>` now works as expected ([thanks Dennis!](https://github.com/shoelace-style/webawesome-alpha/discussions/203))
|
||||
- Specifying inherited CSS properties on `<wa-tooltip>` now works as expected
|
||||
- Fixed a bug in `<wa-select>` that made it hard to use with VueJS, Svelte, and many other frameworks
|
||||
- Fixed a bug in `<wa-select multiple>` that sometimes resulted in empty `<div>` elements being output
|
||||
- Fixed a bug where changing a `<wa-option>` label wouldn't update the display label in `<wa-select>`
|
||||
@@ -393,4 +463,4 @@ Many of these changes and improvements were the direct result of feedback from u
|
||||
|
||||
</details>
|
||||
|
||||
Did we miss something? [Let us know!](https://github.com/shoelace-style/webawesome-alpha/discussions)
|
||||
Did we miss something? [Let us know!](https://github.com/shoelace-style/webawesome/discussions)
|
||||
|
||||
@@ -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>
|
||||
@@ -36,13 +36,27 @@ The [community chat](https://discord.gg/mg8f26C) is open to the public and power
|
||||
Join the Chat
|
||||
</wa-button>
|
||||
|
||||
## Twitter
|
||||
## Social Networks
|
||||
|
||||
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.
|
||||
Follow Web Awesome on [Bluesky](https://bsky.app/profile/webawesome.com), [X (Twitter)](https://x.com/webawesomer), [Mastodon](https://mastodon.social/@webawesome), or [Threads](https://www.threads.com/@web.awesome) for general updates and announcements. 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 Social Networks 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>
|
||||
<div class="wa-cluster wa-gap-l">
|
||||
<wa-button variant="brand" href="https://bsky.app/profile/webawesome.com" rel="noopener noreferrer" target="_blank">
|
||||
<wa-icon name="bluesky" family="brands" slot="start"></wa-icon>
|
||||
Bluesky
|
||||
</wa-button>
|
||||
<wa-button variant="brand" href="https://twitter.com/webawesomer" rel="noopener noreferrer" target="_blank">
|
||||
<wa-icon name="x-twitter" family="brands" slot="start"></wa-icon>
|
||||
X (Twitter)
|
||||
</wa-button>
|
||||
<wa-button variant="brand" href="https://mastodon.social/@webawesome" rel="noopener noreferrer" target="_blank">
|
||||
<wa-icon name="mastodon" family="brands" slot="start"></wa-icon>
|
||||
Mastodon
|
||||
</wa-button>
|
||||
<wa-button variant="brand" href="https://www.threads.com/@web.awesome" rel="noopener noreferrer" target="_blank">
|
||||
<wa-icon name="threads" family="brands" slot="start"></wa-icon>
|
||||
Threads
|
||||
</wa-button>
|
||||
</div>
|
||||
|
||||
@@ -131,7 +131,7 @@ To link to a GitHub issue, PR, or discussion, use the following syntax.
|
||||
|
||||
### Frontmatter
|
||||
|
||||
There's a number of frontmatter properties for doing different things in the Web Awesome documention.
|
||||
There's a number of frontmatter properties for doing different things in the Web Awesome documentation.
|
||||
|
||||
For example, to only show a page in development, use the `unpublished: true` key / value pair.
|
||||
|
||||
@@ -252,22 +252,16 @@ This creates confusion because the part will be documented, but it won't work wh
|
||||
|
||||
### Emitting Events
|
||||
|
||||
Components must only emit events that start with `wa-` as a namespace. For compatibility with frameworks that utilize DOM templates, events must have lowercase, kebab-style names. For example, use `wa-change` instead of `waChange`.
|
||||
Components must only emit events that start with `wa-` as a namespace. For compatibility with frameworks that utilize DOM templates, events must have lowercase, kebab-style names. For example, use `wa-event` instead of `waEvent`.
|
||||
|
||||
This convention avoids the problem of browsers lowercasing attributes, causing some frameworks to be unable to listen to them. This problem isn't specific to one framework, but [Vue's documentation](https://vuejs.org/v2/guide/components-custom-events.html#Event-Names) provides a good explanation of the problem.
|
||||
|
||||
### Change Events
|
||||
|
||||
When change events are emitted by Web Awesome components, they should be named `wa-change` and they should only be emitted as a result of user input. Programmatic changes, such as setting `el.value = '…'` _should not_ result in a change event being emitted. This is consistent with how native form controls work.
|
||||
|
||||
### Data Attribute Invokers
|
||||
|
||||
Some components can be controlled using [data attributes](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/data-*) that trigger specific behaviors. These controls must use the following convention:
|
||||
|
||||
```html
|
||||
<button data-component="action id">
|
||||
Button text
|
||||
</button>
|
||||
<button data-component="action id">Button text</button>
|
||||
```
|
||||
|
||||
The `data-component` portion corresponds to the component's name without the `wa-` prefix. For example, `data-dialog` must control a `<wa-dialog>` component.
|
||||
@@ -277,13 +271,9 @@ The `action` parameter is required and must be a concise, descriptive term indic
|
||||
The `id` parameter must point to the ID of the target component. The ID may be omitted if and only if the target component wraps the element with the `data-` attribute.
|
||||
|
||||
```html
|
||||
<wa-dialog id="my-dialog">
|
||||
Dialog content
|
||||
</wa-dialog>
|
||||
<wa-dialog id="my-dialog"> Dialog content </wa-dialog>
|
||||
|
||||
<button data-dialog="open my-dialog">
|
||||
Open dialog
|
||||
</button>
|
||||
<button data-dialog="open my-dialog">Open dialog</button>
|
||||
```
|
||||
|
||||
### CSS Custom Properties
|
||||
@@ -306,6 +296,7 @@ Then use the following syntax for comments so they appear in the generated docs.
|
||||
* @cssproperty --color: The component's text color.
|
||||
* @cssproperty --background-color: The component's background color.
|
||||
*/
|
||||
@customElement('wa-example')
|
||||
export default class WaExample {
|
||||
// ...
|
||||
}
|
||||
@@ -363,7 +354,7 @@ Form controls should support submission and validation through the following con
|
||||
|
||||
- Form Controls should extend from `WebAwesomeFormAssociatedElement`
|
||||
- All form controls must use `name`, `value`, and `disabled` properties in the same manner as `HTMLInputElement`
|
||||
- All form controls with the `disabled` property *NOT* reflect the `disabled` attribute.
|
||||
- All form controls with the `disabled` property _NOT_ reflect the `disabled` attribute.
|
||||
- All form controls must have an `invalid` property that reflects their validity
|
||||
- All form controls should mirror their native validation attributes such as `required`, `pattern`, `minlength`, `maxlength`, etc. when possible and use the `MirrorValidator`.
|
||||
- All form controls must be tested to work with the standard `<form>` element
|
||||
@@ -406,7 +397,7 @@ Guidelines for writing tests:
|
||||
|
||||
### Running tests
|
||||
|
||||
Right now, tests run both "hydrated" (SSR -> client hydrated) and "client only". If you're debugging only one specific kind you can set an environment variable. For example, to run only the client tests, you can do:
|
||||
Right now, tests run both "hydrated" (SSR → client hydrated) and "client only". If you're debugging only one specific kind you can set an environment variable. For example, to run only the client tests, you can do:
|
||||
|
||||
```bash
|
||||
CSR_ONLY="true" npm run test
|
||||
@@ -416,4 +407,4 @@ or for hydrated rendering only:
|
||||
|
||||
```bash
|
||||
SSR_ONLY="true" npm run test
|
||||
```
|
||||
```
|
||||
|
||||
@@ -6,26 +6,33 @@ wide: true
|
||||
---
|
||||
|
||||
<style>
|
||||
p {
|
||||
max-width: 90ch;
|
||||
}
|
||||
tbody {
|
||||
& .wa-grid {
|
||||
--min-column-size: 5ch;
|
||||
#content {
|
||||
p {
|
||||
max-width: 90ch;
|
||||
}
|
||||
& tr th:first-of-type {
|
||||
width: 20ch;
|
||||
|
||||
tbody {
|
||||
& .wa-grid {
|
||||
--min-column-size: 5ch;
|
||||
}
|
||||
|
||||
& tr th:first-of-type {
|
||||
width: 20ch;
|
||||
}
|
||||
|
||||
& th {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
& tr:hover {
|
||||
background-color: color-mix(in oklch, var(--wa-color-fill-quiet), transparent 60%)
|
||||
}
|
||||
}
|
||||
& th {
|
||||
vertical-align: middle;
|
||||
|
||||
wa-divider {
|
||||
--width: var(--wa-border-width-m);
|
||||
--spacing: var(--wa-space-3xl);
|
||||
}
|
||||
& tr:hover {
|
||||
background-color: color-mix(in oklch, var(--wa-color-fill-quiet), transparent 60%)
|
||||
}
|
||||
}
|
||||
wa-divider {
|
||||
--width: var(--wa-border-width-m);
|
||||
--spacing: var(--wa-space-3xl);
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
title: Component Groups
|
||||
description: Style groups of components that share similar qualities with these Web Awesome custom properties.
|
||||
order: 9999
|
||||
layout: page-outline
|
||||
---
|
||||
|
||||
For components that share similar qualities, Web Awesome includes custom properties to change the appearance of these related components all at once.
|
||||
@@ -84,16 +85,19 @@ Panels consist of components with larger, contained surface areas like [callout]
|
||||
|
||||
## Tooltips
|
||||
|
||||
Tooltip styles are shared between the [tooltip](/docs/components/tooltip) component and the tooltip implementation in [range](/docs/components/range).
|
||||
Tooltip styles are shared between the [tooltip](/docs/components/tooltip) component and the tooltips in [slider](/docs/components/slider) and [copy button](/docs/components/copy-button).
|
||||
|
||||
| Custom Property | Default Value |
|
||||
| ---------------------------- | ----------------------------------- |
|
||||
| `--wa-tooltip-arrow-size` | `0.375rem` <small>(6px)</small> |
|
||||
| `--wa-tooltip-background-color` | `var(--wa-color-neutral-fill-loud)` |
|
||||
| `--wa-tooltip-border-radius` | `var(--wa-border-radius-m)` |
|
||||
| `--wa-tooltip-content-color` | `var(--wa-color-neutral-on-loud)` |
|
||||
| `--wa-tooltip-font-size` | `var(--wa-font-size-s)` |
|
||||
| `--wa-tooltip-line-height` | `var(--wa-line-height-normal)` |
|
||||
| Custom Property | Default Value |
|
||||
| ------------------------------- | ------------------------------------ |
|
||||
| `--wa-tooltip-arrow-size` | `0.375rem` <small>(6px)</small> |
|
||||
| `--wa-tooltip-background-color` | `var(--wa-color-neutral-fill-loud)` |
|
||||
| `--wa-tooltip-border-color` | `var(--wa-tooltip-background-color)` |
|
||||
| `--wa-tooltip-border-style` | `var(--wa-border-style)` |
|
||||
| `--wa-tooltip-border-width` | `var(--wa-border-width-s)` |
|
||||
| `--wa-tooltip-border-radius` | `var(--wa-border-radius-s)` |
|
||||
| `--wa-tooltip-content-color` | `var(--wa-color-neutral-on-loud)` |
|
||||
| `--wa-tooltip-font-size` | `var(--wa-font-size-s)` |
|
||||
| `--wa-tooltip-line-height` | `var(--wa-line-height-normal)` |
|
||||
|
||||
```html {.example}
|
||||
<wa-button id="bullseye-example" appearance="plain">
|
||||
|
||||
@@ -174,3 +174,54 @@ This time we see an empty string, which means the boolean attribute is now prese
|
||||
:::info
|
||||
To wait for multiple components to update, you can use `requestAnimationFrame()` instead of awaiting each element.
|
||||
:::
|
||||
|
||||
## Code Completion
|
||||
|
||||
### VS Code
|
||||
|
||||
Web Awesome ships with a file called `vscode.html-custom-data.json` that can be used to describe its custom elements to [Visual Studio Code](https://code.visualstudio.com/). This enables code completion for Web Awesome components (also known as “code hinting” or “IntelliSense”). To enable it, you need to tell VS Code where the file is.
|
||||
|
||||
1. [Install Web Awesome locally](/docs/#installing-via-npm)
|
||||
2. If it doesn’t already exist, create a folder called `.vscode` at the root of your project
|
||||
3. If it doesn’t already exist, create a file inside that folder called `settings.json`
|
||||
4. Add the following to the file
|
||||
|
||||
```json
|
||||
{
|
||||
"html.customData": ["./node_modules/@awesome.me/webawesome/dist/vscode.html-custom-data.json"]
|
||||
}
|
||||
```
|
||||
|
||||
If `settings.json` already exists, simply add the above line to the root of the object. Note that you may need to restart VS Code for the changes to take effect.
|
||||
|
||||
If you are using WebAwesome through the [CDN](/docs/#quick-start-autoloading-via-cdn) you can manually [download the file]({% cdnUrl 'vscode.html-custom-data.json' %}]({% cdnUrl 'vscode.html-custom-data.json' %}) instead.
|
||||
|
||||
### JetBrains IDEs
|
||||
If you are using a [JetBrains IDE](https://www.jetbrains.com/) and you are installing Web Awesome from NPM, the editor will automatically detect the web-types.json file from the package and you should immediately see component information in your editor.
|
||||
|
||||
If you are installing from the CDN, you can [download a local copy]({% cdnUrl 'web-types.json' %}) and add it to the root of your project. Be sure to add a reference to the web-types.json file in your package.json in order for your editor to properly detect it.
|
||||
|
||||
```json
|
||||
{
|
||||
...
|
||||
"web-types": "./web-types.json"
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
If you are using types from multiple projects, you can add an array of references.
|
||||
|
||||
```json
|
||||
{
|
||||
...
|
||||
"web-types": [
|
||||
...,
|
||||
"./web-types.json"
|
||||
]
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
### Other Editors
|
||||
|
||||
Most popular editors support custom code completion with a bit of configuration. Please [submit a feature request](https://github.com/shoelace-style/webawesome/issues/new/choose) for your editor of choice. PRs are also welcome!
|
||||
|
||||
@@ -20,7 +20,7 @@ tags: layoutUtilities
|
||||
}
|
||||
</style>
|
||||
|
||||
Web Awesome includes classes to set the `align-items` property of flex and grid containers. They can be used alongside other Web Awesome layout utilities, like [cluster](/docs/layout/cluster) and [stack](/docs/layout/stack), to align children in container on the container's cross axis.
|
||||
Web Awesome includes classes to set the `align-items` property of flex and grid containers. They can be used alongside other Web Awesome layout utilities, like [cluster](/docs/utilities/cluster) and [stack](/docs/utilities/stack), to align children in container on the container's cross axis.
|
||||
|
||||
| Class Name | `align-items` Value | Preview |
|
||||
| ------------------------- | ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -380,6 +380,19 @@ Add the `wa-pill` class to give buttons rounded edges.
|
||||
<button class="wa-pill">Pill button</button>
|
||||
```
|
||||
|
||||
When using `<wa-icon>` within a button, wrap adjacent label text in `<span>` or similar to automatically add margin between the icon and the label, just like the `start` and `end` slots of `<wa-button>`.
|
||||
|
||||
```html {.example}
|
||||
<button>
|
||||
<wa-icon name="plane-departure"></wa-icon>
|
||||
<span>Start Icon</span>
|
||||
</button>
|
||||
<button>
|
||||
<span>End Icon</span>
|
||||
<wa-icon name="plane-arrival"></wa-icon>
|
||||
</button>
|
||||
```
|
||||
|
||||
### Form controls
|
||||
|
||||
Create a variety of form controls with `<input type="">`, `<select>`, and `<textarea>`. Each control closely matches the appearance of the corresponding Web Awesome component.
|
||||
@@ -391,6 +404,7 @@ Create a variety of form controls with `<input type="">`, `<select>`, and `<text
|
||||
<label>Time <input type="time" /></label>
|
||||
<label>Number <input type="number" placeholder="12345" /></label>
|
||||
<label>Color <input type="color" value="#f36944" /></label>
|
||||
<label>File <input type="file" multiple /></label>
|
||||
<label>Range <input type="range" /></label>
|
||||
<label>Select
|
||||
<select>
|
||||
@@ -417,6 +431,27 @@ Create a variety of form controls with `<input type="">`, `<select>`, and `<text
|
||||
</script>
|
||||
```
|
||||
|
||||
Add the `wa-size-s`, `wa-size-m`, or `wa-size-l` class to any form control or its parent `<label>` to specify its size.
|
||||
```html {.example}
|
||||
<div class="wa-stack">
|
||||
<input type="text" placeholder="Small input" class="wa-size-s" />
|
||||
<div class="wa-cluster">
|
||||
<label class="wa-size-s"><input type="checkbox" checked /> Small checkbox</label>
|
||||
<label class="wa-size-s"><input type="radio" name="radio-small" value="1" checked /> Small radio</label>
|
||||
</div>
|
||||
<input type="text" placeholder="Medium input" class="wa-size-m" />
|
||||
<div class="wa-cluster">
|
||||
<label class="wa-size-m"><input type="checkbox" checked /> Medium checkbox</label>
|
||||
<label class="wa-size-m"><input type="radio" name="radio-medium" value="1" checked /> Medium radio</label>
|
||||
</div>
|
||||
<input type="text" placeholder="Large input" class="wa-size-l" />
|
||||
<div class="wa-cluster">
|
||||
<label class="wa-size-l"><input type="checkbox" checked /> Large checkbox</label>
|
||||
<label class="wa-size-l"><input type="radio" name="radio-large" value="1" checked /> Large radio</label>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
Add the `wa-filled` class to an input to give it a filled background.
|
||||
|
||||
```html {.example}
|
||||
@@ -440,6 +475,12 @@ Add the `wa-pill` class to an input or select to give it rounded edges.
|
||||
</div>
|
||||
```
|
||||
|
||||
Add any [button](#buttons) modifier class to `<input type="file">` to change the file selector button's color variant, appearance, size, and shape.
|
||||
|
||||
```html {.example}
|
||||
<input type="file" class="wa-filled wa-outlined wa-warning wa-size-s wa-pill" />
|
||||
```
|
||||
|
||||
### Fieldsets
|
||||
|
||||
Group form controls together with `<fieldset>` and `<legend>`.
|
||||
|
||||
@@ -43,6 +43,63 @@ Adding a label may seem redundant at times, but they're very helpful for unsight
|
||||
</wa-card>
|
||||
```
|
||||
|
||||
### Visually Hidden Input Parts
|
||||
|
||||
Sometimes you want a form control to have a cleaner, more minimal appearance by hiding the `label` or `hint` visually. However, removing these elements entirely would make the form inaccessible to users with assistive devices.
|
||||
|
||||
Instead, you can hide them visually while keeping them available to screen readers by adding the `wa-visually-hidden-label` or `wa-visually-hidden-hint` class.
|
||||
|
||||
```html {.example}
|
||||
<wa-input
|
||||
label="Search Articles"
|
||||
type="search"
|
||||
placeholder="Search for..."
|
||||
class="wa-visually-hidden-label"
|
||||
style="margin-block-end: 1rem;"
|
||||
>
|
||||
<wa-icon slot="start" name="magnifying-glass" variant="regular"></wa-icon>
|
||||
</wa-input>
|
||||
|
||||
<wa-input
|
||||
label="Phone Number"
|
||||
type="tel"
|
||||
hint="We'll send you a verification code"
|
||||
placeholder="(555) 867-5309"
|
||||
class="wa-visually-hidden-hint"
|
||||
style="margin-block-end: 1rem;"
|
||||
>
|
||||
<wa-icon slot="start" name="phone" variant="regular"></wa-icon>
|
||||
</wa-input>
|
||||
|
||||
<wa-select
|
||||
label="Country"
|
||||
hint="Select your country for shipping calculations"
|
||||
class="wa-visually-hidden-hint"
|
||||
style="margin-block-end: 1rem;"
|
||||
>
|
||||
<wa-option value="us">United States</wa-option>
|
||||
<wa-option value="ca">Canada</wa-option>
|
||||
<wa-option value="mx">Mexico</wa-option>
|
||||
<wa-option value="uk">United Kingdom</wa-option>
|
||||
<wa-option value="de">Germany</wa-option>
|
||||
<wa-option value="fr">France</wa-option>
|
||||
<wa-option value="wakanda">Wakanda</wa-option>
|
||||
<wa-option value="genovia">Genovia</wa-option>
|
||||
<wa-option value="elbonia">Elbonia</wa-option>
|
||||
<wa-icon slot="start" name="globe" variant="regular"></wa-icon>
|
||||
</wa-select>
|
||||
|
||||
<wa-input
|
||||
label="Email Address"
|
||||
type="email"
|
||||
hint="We'll never share your email or secret identity"
|
||||
placeholder="e.g. miles.morales@brooklynvisions.edu"
|
||||
class="wa-visually-hidden-label wa-visually-hidden-hint"
|
||||
>
|
||||
<wa-icon slot="start" name="envelope" variant="regular"></wa-icon>
|
||||
</wa-input>
|
||||
```
|
||||
|
||||
### Force Visually Hidden
|
||||
|
||||
There are cases where you want to _always_ visually hide certain content, even when it's focused.
|
||||
|
||||
@@ -3,7 +3,7 @@ unlisted: true
|
||||
layout: false
|
||||
---
|
||||
<!doctype html>
|
||||
<html lang="en" class="wa-cloak" data-fa-kit-code="b10bfbde90">
|
||||
<html lang="en" class="wa-cloak" data-fa-kit-code="38c11e3f20">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
@@ -304,15 +304,15 @@ layout: false
|
||||
<wa-divider></wa-divider>
|
||||
<dl class="wa-stack">
|
||||
<div class="wa-flank wa-align-items-center">
|
||||
<dt><wa-icon name="user" label="Name" fixed-width></wa-icon></dt>
|
||||
<dt><wa-icon name="user" label="Name"></wa-icon></dt>
|
||||
<dd>Tom Bombadil</dd>
|
||||
</div>
|
||||
<div class="wa-flank wa-align-items-center">
|
||||
<dt><wa-icon name="calendar-days" label="Date" fixed-width></wa-icon></dt>
|
||||
<dt><wa-icon name="calendar-days" label="Date"></wa-icon></dt>
|
||||
<dd><wa-format-date date="2025-03-15"></wa-format-date></dd>
|
||||
</div>
|
||||
<div class="wa-flank wa-align-items-center">
|
||||
<dt><wa-icon name="coin-vertical" fixed-width></wa-icon></dt>
|
||||
<dt><wa-icon name="coin-vertical"></wa-icon></dt>
|
||||
<dd>Paid with copper pennies</dd>
|
||||
</div>
|
||||
</dl>
|
||||
@@ -344,19 +344,19 @@ layout: false
|
||||
<h4 class="wa-heading-s">What You Get</h4>
|
||||
<div class="wa-stack">
|
||||
<div class="wa-flank">
|
||||
<wa-icon name="user" fixed-width></wa-icon>
|
||||
<wa-icon name="user"></wa-icon>
|
||||
<span class="wa-caption-m">9 users</span>
|
||||
</div>
|
||||
<div class="wa-flank">
|
||||
<wa-icon name="ring" fixed-width></wa-icon>
|
||||
<wa-icon name="ring"></wa-icon>
|
||||
<span class="wa-caption-m">1 ring</span>
|
||||
</div>
|
||||
<div class="wa-flank">
|
||||
<wa-icon name="chess-rook" fixed-width></wa-icon>
|
||||
<wa-icon name="chess-rook"></wa-icon>
|
||||
<span class="wa-caption-m">API access to Isengard</span>
|
||||
</div>
|
||||
<div class="wa-flank">
|
||||
<wa-icon name="feather" fixed-width></wa-icon>
|
||||
<wa-icon name="feather"></wa-icon>
|
||||
<span class="wa-caption-m">Priority eagle support</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -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 {
|
||||
@@ -256,11 +257,22 @@ layout: page
|
||||
{% if currentUser.hasPro %}
|
||||
<span style="text-align: center; width: 100%; font-size: var(--wa-font-size-l);">Thanks for being a Web Awesome Pro subscriber!</span>
|
||||
{% else %}
|
||||
<span><em>Psst!</em> You can pre-order Web Awesome Pro at a low, guaranteed-for-life price — but not for long. Get in while the gettin’s good.</span>
|
||||
<wa-button class="wa-dark" size="small" href="https://www.kickstarter.com/projects/fontawesome/web-awesome">
|
||||
<wa-icon slot="start" name="person-running"></wa-icon>
|
||||
Pre-order WA Pro
|
||||
</wa-button>
|
||||
{% if App.flags.stripeEnabled %}
|
||||
<div class="wa-stack wa-gap-xs">
|
||||
<h3 class="wa-heading-s">Get More with Web Awesome Pro!</h3>
|
||||
<p>Unlock Pro-only themes, components, patterns, and great services like the Theme Builder.</p>
|
||||
</div>
|
||||
<wa-button class="wa-dark" size="small" href="/purchase">
|
||||
<wa-icon slot="start" name="rocket-launch"></wa-icon>
|
||||
Purchase Pro
|
||||
</wa-button>
|
||||
{% else %}
|
||||
<span><em>Psst!</em> You can pre-order Web Awesome Pro at a low, guaranteed-for-life price — but not for long. Get in while the gettin’s good.</span>
|
||||
<wa-button class="wa-dark" size="small" href="https://www.kickstarter.com/projects/fontawesome/web-awesome">
|
||||
<wa-icon slot="start" name="person-running"></wa-icon>
|
||||
Pre-order WA Pro
|
||||
</wa-button>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endraw %}
|
||||
</div>
|
||||
@@ -273,7 +285,7 @@ layout: page
|
||||
<wa-callout variant="brand">
|
||||
<div class="wa-stack">
|
||||
<div class="wa-cluster icon-heading">
|
||||
<wa-icon name="sparkles" variant="regular" fixed-width></wa-icon>
|
||||
<wa-icon name="sparkles" variant="regular"></wa-icon>
|
||||
<h3>Bigger and beta than ever</h3>
|
||||
</div>
|
||||
<p>This beta is battle-tested and built to last, but if you see something, say something. Please <a href="https://github.com/shoelace-style/webawesome/issues">report bugs</a> or <a href="https://github.com/shoelace-style/webawesome/discussions">ask for help</a>!</p>
|
||||
@@ -285,10 +297,10 @@ layout: page
|
||||
<div class="wa-stack">
|
||||
<div class="wa-split">
|
||||
<div class="wa-cluster icon-heading">
|
||||
<wa-icon name="pen-ruler" fixed-width class="brand-orange"></wa-icon>
|
||||
<wa-icon name="pen-ruler" class="brand-orange"></wa-icon>
|
||||
<h3>Get started</h3>
|
||||
</div>
|
||||
<wa-icon name="arrow-right" fixed-width></wa-icon>
|
||||
<wa-icon name="arrow-right"></wa-icon>
|
||||
</div>
|
||||
<p>Check out our installation guide to start building with Web Awesome.</p>
|
||||
</div>
|
||||
@@ -302,28 +314,28 @@ layout: page
|
||||
<div class="grid">
|
||||
<div class="wa-stack">
|
||||
<div class="wa-cluster icon-heading">
|
||||
<wa-icon name="code" fixed-width class="brand-orange"></wa-icon>
|
||||
<wa-icon name="code" class="brand-orange"></wa-icon>
|
||||
<h3>Entirely native</h3>
|
||||
</div>
|
||||
<p>Built on web standards to last for years to come. No excess tooling. No third-party bloat.</p>
|
||||
</div>
|
||||
<div class="wa-stack">
|
||||
<div class="wa-cluster icon-heading">
|
||||
<wa-icon name="palette" fixed-width class="brand-orange"></wa-icon>
|
||||
<wa-icon name="palette" class="brand-orange"></wa-icon>
|
||||
<h3>Fully customizable</h3>
|
||||
</div>
|
||||
<p>Show off your own style with components that consistently adapt to your theme.</p>
|
||||
</div>
|
||||
<div class="wa-stack">
|
||||
<div class="wa-cluster icon-heading">
|
||||
<wa-icon name="wheelchair-move" fixed-width class="brand-orange"></wa-icon>
|
||||
<wa-icon name="wheelchair-move" class="brand-orange"></wa-icon>
|
||||
<h3>Accessibility forward</h3>
|
||||
</div>
|
||||
<p>Build a website that everyone can use. Designed to be inclusive and usable by everyone.</p>
|
||||
</div>
|
||||
<div class="wa-stack">
|
||||
<div class="wa-cluster icon-heading">
|
||||
<wa-icon name="handshake-simple" fixed-width class="brand-orange"></wa-icon>
|
||||
<wa-icon name="handshake-simple" class="brand-orange"></wa-icon>
|
||||
<h3>Proudly open source</h3>
|
||||
</div>
|
||||
<p>Use Web Awesome Free however you like. Always free, always open source.</p>
|
||||
@@ -342,22 +354,22 @@ layout: page
|
||||
<div class="wa-stack">
|
||||
<div class="wa-split">
|
||||
<div class="wa-cluster icon-heading">
|
||||
<wa-icon family="brands" name="github" fixed-width></wa-icon>
|
||||
<wa-icon family="brands" name="github"></wa-icon>
|
||||
<h3>GitHub</h3>
|
||||
</div>
|
||||
<wa-icon name="arrow-up-right" fixed-width></wa-icon>
|
||||
<wa-icon name="arrow-up-right"></wa-icon>
|
||||
</div>
|
||||
<p>Get involved by opening issues, contributing to discussions, or creating PRs.</p>
|
||||
</div>
|
||||
</wa-button>
|
||||
<wa-button href="https://discord.gg/a74U7eYH" rel="noopener noreferrer" target="_blank" appearance="filled" class="tile">
|
||||
<wa-button href="https://discord.gg/mg8f26C" rel="noopener noreferrer" target="_blank" appearance="filled" class="tile">
|
||||
<div class="wa-stack">
|
||||
<div class="wa-split">
|
||||
<div class="wa-cluster icon-heading">
|
||||
<wa-icon family="brands" name="discord" fixed-width></wa-icon>
|
||||
<wa-icon family="brands" name="discord"></wa-icon>
|
||||
<h3>Discord</h3>
|
||||
</div>
|
||||
<wa-icon name="arrow-up-right" fixed-width></wa-icon>
|
||||
<wa-icon name="arrow-up-right"></wa-icon>
|
||||
</div>
|
||||
<p>Share your work, ask questions, and explore ideas with other Web Awesome builders.</p>
|
||||
</div>
|
||||
@@ -365,57 +377,66 @@ layout: page
|
||||
<wa-button href="mailto:hello@webawesome.com" appearance="filled" class="tile">
|
||||
<div class="wa-split">
|
||||
<div class="wa-cluster icon-heading">
|
||||
<wa-icon name="envelope-open" fixed-width></wa-icon>
|
||||
<wa-icon name="envelope-open"></wa-icon>
|
||||
<h3 class="wa-cluster wa-gap-xs">
|
||||
<span>hello@webawesome.com</span>
|
||||
<wa-icon name="hand-wave" variant="regular"></wa-icon>
|
||||
</h3>
|
||||
</div>
|
||||
<wa-icon name="arrow-up-right" fixed-width></wa-icon>
|
||||
<wa-icon name="arrow-up-right"></wa-icon>
|
||||
</div>
|
||||
</wa-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<wa-divider></wa-divider>
|
||||
<wa-divider></wa-divider>
|
||||
|
||||
<div class="wa-stack wa-gap-xl">
|
||||
<h2 class="wa-cluster brand-font">
|
||||
<wa-icon name="hashtag" style="color: var(--wa-brand-orange);"></wa-icon>
|
||||
<span>Stay in the know</span>
|
||||
</h2>
|
||||
<div class="wa-grid">
|
||||
<div class="wa-grid" style="--min-column-size: 30ch;">
|
||||
<wa-button href="https://bsky.app/profile/webawesome.com" rel="noopener noreferrer" target="_blank" appearance="filled" class="tile">
|
||||
<div class="wa-split">
|
||||
<div class="wa-cluster icon-heading">
|
||||
<wa-icon family="brands" name="bluesky" fixed-width></wa-icon>
|
||||
<wa-icon family="brands" name="bluesky"></wa-icon>
|
||||
<h3>Bluesky</h3>
|
||||
</div>
|
||||
<wa-icon name="arrow-up-right" fixed-width></wa-icon>
|
||||
<wa-icon name="arrow-up-right"></wa-icon>
|
||||
</div>
|
||||
</wa-button>
|
||||
<wa-button href="https://mastodon.social/@webawesome" rel="noopener noreferrer" target="_blank" appearance="filled" class="tile">
|
||||
<div class="wa-split">
|
||||
<div class="wa-cluster icon-heading">
|
||||
<wa-icon family="brands" name="mastodon"></wa-icon>
|
||||
<h3>Mastodon</h3>
|
||||
</div>
|
||||
<wa-icon name="arrow-up-right"></wa-icon>
|
||||
</div>
|
||||
</wa-button>
|
||||
<wa-button href="https://x.com/webawesomer" rel="noopener noreferrer" target="_blank" appearance="filled" class="tile">
|
||||
<div class="wa-split">
|
||||
<div class="wa-cluster icon-heading">
|
||||
<wa-icon family="brands" name="x-twitter" fixed-width></wa-icon>
|
||||
<wa-icon family="brands" name="x-twitter"></wa-icon>
|
||||
<h3>Twitter (X)</h3>
|
||||
</div>
|
||||
<wa-icon name="arrow-up-right" fixed-width></wa-icon>
|
||||
<wa-icon name="arrow-up-right"></wa-icon>
|
||||
</div>
|
||||
</wa-button>
|
||||
<wa-button href="https://www.threads.com/@web.awesome" rel="noopener noreferrer" target="_blank" appearance="filled" class="tile">
|
||||
<div class="wa-split">
|
||||
<div class="wa-cluster icon-heading">
|
||||
<wa-icon family="brands" name="threads" fixed-width></wa-icon>
|
||||
<wa-icon family="brands" name="threads"></wa-icon>
|
||||
<h3>Threads</h3>
|
||||
</div>
|
||||
<wa-icon name="arrow-up-right" fixed-width></wa-icon>
|
||||
<wa-icon name="arrow-up-right"></wa-icon>
|
||||
</div>
|
||||
</wa-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<wa-divider></wa-divider>
|
||||
<wa-divider></wa-divider>
|
||||
|
||||
<footer>
|
||||
<div class="wa-crown">
|
||||
@@ -442,4 +463,4 @@ layout: page
|
||||
© Fonticons, Inc.
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"access": "public"
|
||||
},
|
||||
"description": "A forward-thinking library of web components.",
|
||||
"version": "3.0.0-beta.2",
|
||||
"version": "3.0.0-beta.6",
|
||||
"homepage": "https://webawesome.com/",
|
||||
"author": "Web Awesome",
|
||||
"license": "MIT",
|
||||
@@ -19,6 +19,7 @@
|
||||
"import": "./dist/webawesome.js"
|
||||
},
|
||||
"./dist/custom-elements.json": "./dist/custom-elements.json",
|
||||
"./dist/custom-elements-jsx.d.ts": "./dist/custom-elements-jsx.d.ts",
|
||||
"./dist/webawesome.js": "./dist/webawesome.js",
|
||||
"./dist/webawesome.loader.js": "./dist/webawesome.loader.js",
|
||||
"./dist/styles": "./dist/styles",
|
||||
@@ -28,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",
|
||||
@@ -71,19 +73,23 @@
|
||||
"node": ">=14.17.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ctrl/tinycolor": "^4.1.0",
|
||||
"@ctrl/tinycolor": "4.1.0",
|
||||
"@floating-ui/dom": "^1.6.13",
|
||||
"@lit/react": "^1.0.8",
|
||||
"@shoelace-style/animations": "^1.2.0",
|
||||
"@shoelace-style/localize": "^3.2.1",
|
||||
"composed-offset-position": "^0.0.6",
|
||||
"lit": "^3.2.1",
|
||||
"nanoid": "^5.1.5",
|
||||
"qr-creator": "^1.0.0",
|
||||
"style-observer": "^0.0.7"
|
||||
"qr-creator": "^1.0.0"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{ts,js}": [
|
||||
"prettier --write"
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@wc-toolkit/jsx-types": "^1.3.0",
|
||||
"eleventy-plugin-git-commit-date": "^0.1.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,8 @@ const isDeveloping = process.argv.includes('--develop');
|
||||
* @typedef {Object} BuildOptions
|
||||
* @property {Array<string>} [watchedSrcDirectories]
|
||||
* @property {Array<string>} [watchedDocsDirectories]
|
||||
* @property {(eventName: "change" | "add" | "unlink", filePath: string) => unknown} [onWatchEvent]
|
||||
* @property {(eventName: "change" | "add" | "unlink", filePath: string) => unknown} [beforeWatchEvent]
|
||||
* @property {(eventName: "change" | "add" | "unlink", filePath: string) => unknown} [afterWatchEvent]
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -49,8 +50,6 @@ export async function build(options = {}) {
|
||||
options.watchedDocsDirectories = [getDocsDir()];
|
||||
}
|
||||
|
||||
function measureStep() {}
|
||||
|
||||
/**
|
||||
* Runs the full build.
|
||||
*/
|
||||
@@ -122,6 +121,12 @@ export async function build(options = {}) {
|
||||
* Generates React wrappers for all components.
|
||||
*/
|
||||
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();
|
||||
}
|
||||
|
||||
spinner.start('Generating React wrappers');
|
||||
|
||||
try {
|
||||
@@ -156,6 +161,12 @@ export async function build(options = {}) {
|
||||
* Runs TypeScript to generate types.
|
||||
*/
|
||||
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();
|
||||
}
|
||||
|
||||
spinner.start('Running the TypeScript compiler');
|
||||
|
||||
const cwd = process.cwd();
|
||||
@@ -377,22 +388,25 @@ export async function build(options = {}) {
|
||||
},
|
||||
);
|
||||
|
||||
// TODO: Should probably listen for all of these instead of just "change"
|
||||
const watchEvents = [
|
||||
'change',
|
||||
// "unlink",
|
||||
// "add"
|
||||
];
|
||||
const watchEvents = ['change', 'unlink', 'add'];
|
||||
// Rebuild and reload when source files change
|
||||
options.watchedSrcDirectories.forEach(dir => {
|
||||
const watcher = bs.watch(join(dir, '**', '!(*.test).*'));
|
||||
const watcher = bs.watch(join(dir, '**', '!(*.test).*'), { ignoreInitial: true });
|
||||
|
||||
watchEvents.forEach(evt => {
|
||||
watcher.on(evt, handleWatchEvent(evt));
|
||||
});
|
||||
function handleWatchEvent(evt) {
|
||||
return async filename => {
|
||||
spinner.info(`File modified ${chalk.gray(`(${relative(getRootDir(), filename)})`)}`);
|
||||
const changedFile = relative(getRootDir(), filename);
|
||||
|
||||
if (evt === 'change') {
|
||||
spinner.info(`File modified ${chalk.gray(`(${changedFile})`)}`);
|
||||
} else if (evt === 'unlink') {
|
||||
spinner.info(`File deleted ${chalk.gray(`(${changedFile})`)}`);
|
||||
} else if (evt === 'add') {
|
||||
spinner.info(`File added ${chalk.gray(`(${changedFile})`)}`);
|
||||
}
|
||||
|
||||
try {
|
||||
const isTestFile = filename.includes('.test.ts');
|
||||
@@ -405,8 +419,8 @@ export async function build(options = {}) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof options.onWatchEvent === 'function') {
|
||||
await options.onWatchEvent(evt, filename);
|
||||
if (typeof options.beforeWatchEvent === 'function') {
|
||||
await options.beforeWatchEvent(evt, filename);
|
||||
}
|
||||
|
||||
// Copy stylesheets when CSS files change
|
||||
@@ -426,6 +440,10 @@ export async function build(options = {}) {
|
||||
// This needs to be outside of "isComponent" check because SSR needs to run on CSS files too.
|
||||
await generateDocs({ spinner });
|
||||
|
||||
if (typeof options.afterWatchEvent === 'function') {
|
||||
await options.afterWatchEvent(evt, filename);
|
||||
}
|
||||
|
||||
reload();
|
||||
} catch (err) {
|
||||
console.error(chalk.red(err));
|
||||
@@ -440,7 +458,7 @@ export async function build(options = {}) {
|
||||
|
||||
// Rebuild the docs and reload when the docs change
|
||||
options.watchedDocsDirectories.forEach(dir => {
|
||||
const watcher = bs.watch(join(dir, '**', '*.*'));
|
||||
const watcher = bs.watch(join(dir, '**', '*.*'), { ignoreInitial: true });
|
||||
|
||||
watchEvents.forEach(evt => {
|
||||
watcher.on(evt, handleWatchEvent(evt));
|
||||
@@ -448,11 +466,33 @@ export async function build(options = {}) {
|
||||
|
||||
function handleWatchEvent(evt) {
|
||||
return async filename => {
|
||||
spinner.info(`File modified ${chalk.gray(`(${relative(getRootDir(), filename)})`)}`);
|
||||
if (typeof options.onWatchEvent === 'function') {
|
||||
await options.onWatchEvent(evt, filename);
|
||||
const changedFile = relative(getRootDir(), filename);
|
||||
|
||||
let message = '';
|
||||
if (evt === 'change') {
|
||||
message = chalk.blue(`File modified ${chalk.gray(`(${changedFile})`)}`);
|
||||
} else if (evt === 'unlink') {
|
||||
message = chalk.red(`File deleted ${chalk.gray(`(${changedFile})`)}`);
|
||||
} else if (evt === 'add') {
|
||||
message = chalk.green(`File added ${chalk.gray(`(${changedFile})`)}`);
|
||||
}
|
||||
|
||||
if (message) {
|
||||
if (spinner) {
|
||||
spinner.info(message);
|
||||
} else {
|
||||
console.log(message);
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof options.beforeWatchEvent === 'function') {
|
||||
await options.beforeWatchEvent(evt, filename);
|
||||
}
|
||||
await generateDocs({ spinner });
|
||||
|
||||
if (typeof options.afterWatchEvent === 'function') {
|
||||
await options.afterWatchEvent(evt, filename);
|
||||
}
|
||||
reload();
|
||||
};
|
||||
}
|
||||
|
||||
@@ -72,7 +72,6 @@ export async function generateDocs(options = {}) {
|
||||
isDeveloping ??= process.argv.includes('--develop');
|
||||
isIncremental ??= isDeveloping && !process.argv.includes('--no-incremental');
|
||||
|
||||
let eleventy = globalThis.eleventy;
|
||||
/**
|
||||
* Used by the webawesome-app to skip doc generation since it will do its own.
|
||||
*/
|
||||
@@ -80,6 +79,8 @@ export async function generateDocs(options = {}) {
|
||||
return;
|
||||
}
|
||||
|
||||
let eleventy = globalThis.eleventy;
|
||||
|
||||
spinner?.start?.('Writing the docs');
|
||||
|
||||
const outputs = {
|
||||
@@ -118,7 +119,7 @@ export async function generateDocs(options = {}) {
|
||||
return !line.includes('Watching');
|
||||
});
|
||||
const lastLine = info[info.length - 1];
|
||||
output = chalk.gray(`(${lastLine})`);
|
||||
output = chalk.gray(`(${info.join('')})`);
|
||||
eleventy.logger.logger.reset();
|
||||
}
|
||||
} else {
|
||||
@@ -137,13 +138,23 @@ export async function generateDocs(options = {}) {
|
||||
if (!isDeveloping) {
|
||||
await copy(getCdnDir(), join(getSiteDir(), 'dist'));
|
||||
}
|
||||
spinner?.succeed?.(`Writing the docs ${output}`);
|
||||
|
||||
if (spinner) {
|
||||
spinner.succeed(`Writing the docs ${output}`);
|
||||
} else {
|
||||
console.log(`Writing the docs ${output}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn = originalWarn;
|
||||
|
||||
console.error('\n\n' + chalk.red(error) + '\n');
|
||||
|
||||
spinner?.fail?.(chalk.red(`Error while writing the docs.`));
|
||||
if (spinner) {
|
||||
spinner.fail(chalk.red(`Error while writing the docs.`));
|
||||
} else {
|
||||
console.error(chalk.red(`Error while writing the docs.`));
|
||||
}
|
||||
|
||||
if (!isDeveloping) {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ img[aria-hidden='true'] {
|
||||
}
|
||||
}
|
||||
|
||||
:host([play]:not(:hover)) .control-box {
|
||||
:where(:host([play]:not(:hover))) .control-box {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
@@ -50,3 +50,16 @@ img[aria-hidden='true'] {
|
||||
:host(:not([play])) slot[name='pause-icon'] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Show control box on keyboard focus */
|
||||
.animated-image {
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
&:focus-visible .control-box {
|
||||
opacity: 1;
|
||||
outline: var(--wa-focus-ring);
|
||||
outline-offset: var(--wa-focus-ring-offset);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import { WaErrorEvent } from '../../events/error.js';
|
||||
import { WaLoadEvent } from '../../events/load.js';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import WebAwesomeElement from '../../internal/webawesome-element.js';
|
||||
import { LocalizeController } from '../../utilities/localize.js';
|
||||
import '../icon/icon.js';
|
||||
import styles from './animated-image.css';
|
||||
|
||||
@@ -30,6 +31,8 @@ import styles from './animated-image.css';
|
||||
export default class WaAnimatedImage extends WebAwesomeElement {
|
||||
static css = styles;
|
||||
|
||||
private readonly localize = new LocalizeController(this);
|
||||
|
||||
@query('.animated') animatedImage: HTMLImageElement;
|
||||
|
||||
@state() frozenFrame: string;
|
||||
@@ -48,6 +51,13 @@ export default class WaAnimatedImage extends WebAwesomeElement {
|
||||
this.play = !this.play;
|
||||
}
|
||||
|
||||
private handleKeyDown(event: KeyboardEvent) {
|
||||
if (event.key === 'Enter' || event.key === ' ') {
|
||||
event.preventDefault();
|
||||
this.play = !this.play;
|
||||
}
|
||||
}
|
||||
|
||||
private handleLoad() {
|
||||
const canvas = document.createElement('canvas');
|
||||
const { width, height } = this.animatedImage;
|
||||
@@ -82,15 +92,26 @@ export default class WaAnimatedImage extends WebAwesomeElement {
|
||||
}
|
||||
|
||||
render() {
|
||||
const verb = this.localize.term(this.play ? 'pauseAnimation' : 'playAnimation');
|
||||
const label = `${verb} ${this.alt}`;
|
||||
|
||||
return html`
|
||||
<div class="animated-image">
|
||||
<div
|
||||
class="animated-image"
|
||||
tabindex="0"
|
||||
role="button"
|
||||
aria-pressed=${this.play ? 'true' : 'false'}
|
||||
aria-label=${label}
|
||||
@click=${this.handleClick}
|
||||
@keydown=${this.handleKeyDown}
|
||||
>
|
||||
<img
|
||||
class="animated"
|
||||
src=${this.src}
|
||||
alt=${this.alt}
|
||||
crossorigin="anonymous"
|
||||
aria-hidden=${this.play ? 'false' : 'true'}
|
||||
@click=${this.handleClick}
|
||||
role="presentation"
|
||||
@load=${this.handleLoad}
|
||||
@error=${this.handleError}
|
||||
/>
|
||||
@@ -102,10 +123,10 @@ export default class WaAnimatedImage extends WebAwesomeElement {
|
||||
src=${this.frozenFrame}
|
||||
alt=${this.alt}
|
||||
aria-hidden=${this.play ? 'true' : 'false'}
|
||||
@click=${this.handleClick}
|
||||
role="presentation"
|
||||
/>
|
||||
|
||||
<div part="control-box" class="control-box">
|
||||
<div part="control-box" class="control-box" aria-hidden="true">
|
||||
<slot name="play-icon">
|
||||
<wa-icon
|
||||
name="play"
|
||||
|
||||
@@ -3,7 +3,6 @@ import { html } from 'lit';
|
||||
import { customElement, property, query, state } from 'lit/decorators.js';
|
||||
import { classMap } from 'lit/directives/class-map.js';
|
||||
import WebAwesomeElement from '../../internal/webawesome-element.js';
|
||||
import sizeStyles from '../../styles/utilities/size.css';
|
||||
import variantStyles from '../../styles/utilities/variants.css';
|
||||
import type WaButton from '../button/button.js';
|
||||
import styles from './button-group.css';
|
||||
@@ -20,7 +19,7 @@ import styles from './button-group.css';
|
||||
*/
|
||||
@customElement('wa-button-group')
|
||||
export default class WaButtonGroup extends WebAwesomeElement {
|
||||
static css = [sizeStyles, variantStyles, styles];
|
||||
static css = [variantStyles, styles];
|
||||
|
||||
@query('slot') defaultSlot: HTMLSlotElement;
|
||||
|
||||
@@ -36,9 +35,6 @@ export default class WaButtonGroup extends WebAwesomeElement {
|
||||
/** The button group's orientation. */
|
||||
@property({ reflect: true }) orientation: 'horizontal' | 'vertical' = 'horizontal';
|
||||
|
||||
/** The component's size. */
|
||||
@property({ reflect: true }) size: 'small' | 'medium' | 'large'; // unset by default to not override child elements
|
||||
|
||||
/** The button group's theme variant. Defaults to `neutral` if not within another element with a variant. */
|
||||
@property({ reflect: true }) variant: 'neutral' | 'brand' | 'success' | 'warning' | 'danger' = 'neutral';
|
||||
|
||||
@@ -85,7 +81,6 @@ export default class WaButtonGroup extends WebAwesomeElement {
|
||||
|
||||
if (button) {
|
||||
if ((button as WaButton).appearance === 'outlined') this.hasOutlined = true;
|
||||
if (this.size) button.setAttribute('size', this.size);
|
||||
button.classList.add('wa-button-group__button');
|
||||
button.classList.toggle('wa-button-group__horizontal', this.orientation === 'horizontal');
|
||||
button.classList.toggle('wa-button-group__vertical', this.orientation === 'vertical');
|
||||
|
||||
@@ -1,6 +1,18 @@
|
||||
@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;
|
||||
}
|
||||
}
|
||||
@@ -166,6 +178,10 @@
|
||||
aspect-ratio: 1;
|
||||
}
|
||||
|
||||
.button.is-icon-button:has(wa-icon) {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
/* Pill modifier */
|
||||
:host([pill]) .button {
|
||||
border-radius: var(--wa-border-radius-pill);
|
||||
@@ -184,6 +200,10 @@
|
||||
}
|
||||
|
||||
.label {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.is-icon-button .label {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
@@ -242,7 +262,7 @@ wa-icon[part~='caret'] {
|
||||
* Badges
|
||||
*/
|
||||
|
||||
button ::slotted(wa-badge) {
|
||||
.button ::slotted(wa-badge) {
|
||||
border-color: var(--wa-color-surface-default);
|
||||
position: absolute;
|
||||
inset-block-start: 0;
|
||||
|
||||
@@ -11,6 +11,7 @@ import sizeStyles from '../../styles/utilities/size.css';
|
||||
import variantStyles from '../../styles/utilities/variants.css';
|
||||
import { LocalizeController } from '../../utilities/localize.js';
|
||||
import '../icon/icon.js';
|
||||
import type WaIcon from '../icon/icon.js';
|
||||
import '../spinner/spinner.js';
|
||||
import styles from './button.css';
|
||||
|
||||
@@ -179,9 +180,9 @@ export default class WaButton extends WebAwesomeFormAssociatedElement {
|
||||
|
||||
// If there's only an icon and no text, it's an icon button
|
||||
[...nodes].forEach(node => {
|
||||
if (node.nodeType === Node.ELEMENT_NODE && (node as HTMLElement).localName === 'wa-icon') {
|
||||
if (node.nodeType === Node.ELEMENT_NODE && (node as WaIcon).localName === 'wa-icon') {
|
||||
hasIcon = true;
|
||||
if (!hasIconLabel) hasIconLabel = (node as HTMLElement).hasAttribute('label');
|
||||
if (!hasIconLabel) hasIconLabel = (node as WaIcon).label !== undefined;
|
||||
}
|
||||
|
||||
// Concatenate text nodes
|
||||
|
||||
@@ -25,13 +25,8 @@ export default class WaCallout extends WebAwesomeElement {
|
||||
@property({ reflect: true }) variant: 'brand' | 'neutral' | 'success' | 'warning' | 'danger' = 'brand';
|
||||
|
||||
/** The callout's visual appearance. */
|
||||
@property({ reflect: true }) appearance:
|
||||
| 'accent'
|
||||
| 'filled'
|
||||
| 'outlined'
|
||||
| 'plain'
|
||||
| 'outlined filled'
|
||||
| 'outlined accent' = 'outlined filled';
|
||||
@property({ reflect: true }) appearance: 'accent' | 'filled' | 'outlined' | 'plain' | 'outlined filled' =
|
||||
'outlined filled';
|
||||
|
||||
/** The callout's size. */
|
||||
@property({ reflect: true }) size: 'small' | 'medium' | 'large' = 'medium';
|
||||
|
||||
@@ -77,7 +77,9 @@
|
||||
}
|
||||
|
||||
.header {
|
||||
display: block;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
border-block-end-style: inherit;
|
||||
border-block-end-color: var(--wa-color-surface-border);
|
||||
border-block-end-width: var(--wa-panel-border-width);
|
||||
@@ -90,7 +92,9 @@
|
||||
}
|
||||
|
||||
.footer {
|
||||
display: block;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
border-block-start-style: inherit;
|
||||
border-block-start-color: var(--wa-color-surface-border);
|
||||
border-block-start-width: var(--wa-panel-border-width);
|
||||
@@ -102,3 +106,27 @@
|
||||
:host(:not([with-media])) .media {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Orientation Styles */
|
||||
:host([orientation='horizontal']) {
|
||||
flex-direction: row;
|
||||
|
||||
.media {
|
||||
border-start-start-radius: var(--inner-border-radius);
|
||||
border-end-start-radius: var(--inner-border-radius);
|
||||
border-start-end-radius: 0;
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
|
||||
:host([orientation='horizontal']) ::slotted([slot='body']) {
|
||||
display: block;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
:host([orientation='horizontal']) ::slotted([slot='actions']) {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: var(--spacing);
|
||||
}
|
||||
|
||||
@@ -15,6 +15,9 @@ import styles from './card.css';
|
||||
* @slot header - An optional header for the card.
|
||||
* @slot footer - An optional footer for the card.
|
||||
* @slot media - An optional media section to render at the start of the card.
|
||||
* @slot actions - An optional actions section to render at the end for the horizontal card.
|
||||
* @slot header-actions - An optional actions section to render in the header of the vertical card.
|
||||
* @slot footer-actions - An optional actions section to render in the footer of the vertical card.
|
||||
*
|
||||
* @csspart media - The container that wraps the card's media.
|
||||
* @csspart header - The container that wraps the card's header.
|
||||
@@ -42,6 +45,10 @@ export default class WaCard extends WebAwesomeElement {
|
||||
/** Renders the card with a footer. Only needed for SSR, otherwise is automatically added. */
|
||||
@property({ attribute: 'with-footer', type: Boolean, reflect: true }) withFooter = false;
|
||||
|
||||
/** Renders the card's orientation **/
|
||||
@property({ reflect: true })
|
||||
orientation: 'horizontal' | 'vertical' = 'vertical';
|
||||
|
||||
updated() {
|
||||
// Enable the respective slots when detected
|
||||
if (!this.withHeader && this.hasSlotController.test('header')) this.withHeader = true;
|
||||
@@ -50,11 +57,27 @@ export default class WaCard extends WebAwesomeElement {
|
||||
}
|
||||
|
||||
render() {
|
||||
// Horizontal Orientation
|
||||
if (this.orientation === 'horizontal') {
|
||||
return html`
|
||||
<slot name="media" part="media" class="media"></slot>
|
||||
<slot part="body" class="body"></slot>
|
||||
<slot name="actions" part="actions" class="actions"></slot>
|
||||
`;
|
||||
}
|
||||
|
||||
// Vertical Orientation
|
||||
return html`
|
||||
<slot name="media" part="media" class="media"></slot>
|
||||
<slot name="header" part="header" class="header"></slot>
|
||||
<header part="header" class="header">
|
||||
<slot name="header"></slot>
|
||||
<slot name="header-actions"></slot>
|
||||
</header>
|
||||
<slot part="body" class="body"></slot>
|
||||
<slot name="footer" part="footer" class="footer"></slot>
|
||||
<footer part="footer" class="footer">
|
||||
<slot name="footer"></slot>
|
||||
<slot name="footer-actions"></slot>
|
||||
</footer>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,6 +46,10 @@
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
[part~='label'] {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
/* Checked */
|
||||
[part~='control']:has(:checked, :indeterminate) {
|
||||
color: var(--checked-icon-color);
|
||||
|
||||
@@ -234,13 +234,14 @@ export default class WaCheckbox extends WebAwesomeFormAssociatedElement {
|
||||
</label>
|
||||
|
||||
<slot
|
||||
id="hint"
|
||||
part="hint"
|
||||
name="hint"
|
||||
aria-hidden=${hasHint ? 'false' : 'true'}
|
||||
class="${classMap({ 'has-slotted': hasHint })}"
|
||||
id="hint"
|
||||
part="hint"
|
||||
>${this.hint}</slot
|
||||
>
|
||||
${this.hint}
|
||||
</slot>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -490,7 +490,7 @@ export default class WaColorPicker extends WebAwesomeFormAssociatedElement {
|
||||
const target = event.target as HTMLInputElement;
|
||||
const oldValue = this.value;
|
||||
|
||||
// Prevent the `<wa-input>` element's `wa-change` event from bubbling up
|
||||
// Prevent the `<wa-input>` element's `change` event from bubbling up
|
||||
event.stopPropagation();
|
||||
|
||||
if (this.input.value) {
|
||||
@@ -1310,6 +1310,7 @@ export default class WaColorPicker extends WebAwesomeFormAssociatedElement {
|
||||
></button>
|
||||
|
||||
<slot
|
||||
id="hint"
|
||||
name="hint"
|
||||
part="hint"
|
||||
class=${classMap({
|
||||
@@ -1326,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}
|
||||
|
||||
@@ -191,13 +191,13 @@ export default class WaCopyButton extends WebAwesomeElement {
|
||||
<!-- Render a visually hidden label to appease the accessibility checking gods -->
|
||||
<span class="wa-visually-hidden">${this.currentLabel}</span>
|
||||
<slot part="copy-icon" name="copy-icon">
|
||||
<wa-icon library="system" name="copy" variant="regular" fixed-width></wa-icon>
|
||||
<wa-icon library="system" name="copy" variant="regular"></wa-icon>
|
||||
</slot>
|
||||
<slot part="success-icon" name="success-icon" variant="solid" hidden>
|
||||
<wa-icon library="system" name="check" fixed-width></wa-icon>
|
||||
<wa-icon library="system" name="check"></wa-icon>
|
||||
</slot>
|
||||
<slot part="error-icon" name="error-icon" variant="solid" hidden>
|
||||
<wa-icon library="system" name="xmark" fixed-width></wa-icon>
|
||||
<wa-icon library="system" name="xmark"></wa-icon>
|
||||
</slot>
|
||||
<wa-tooltip
|
||||
class=${classMap({
|
||||
|
||||
@@ -6,29 +6,6 @@
|
||||
display: block;
|
||||
}
|
||||
|
||||
:host summary {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
cursor: pointer;
|
||||
|
||||
&::marker,
|
||||
&::-webkit-details-marker {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
outline: var(--wa-focus-ring);
|
||||
outline-offset: calc(var(--spacing) + var(--wa-focus-ring-offset));
|
||||
}
|
||||
}
|
||||
|
||||
details {
|
||||
display: block;
|
||||
overflow-anchor: none;
|
||||
@@ -77,14 +54,16 @@ details {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
:host summary {
|
||||
summary {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: var(--spacing);
|
||||
padding: var(--spacing); /* Add padding here */
|
||||
border-radius: calc(var(--wa-panel-border-radius) - var(--wa-panel-border-width));
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
cursor: pointer;
|
||||
padding: var(--spacing); /* Add padding here */
|
||||
|
||||
&::marker,
|
||||
&::-webkit-details-marker {
|
||||
@@ -97,10 +76,21 @@ details {
|
||||
|
||||
&:focus-visible {
|
||||
outline: var(--wa-focus-ring);
|
||||
outline-offset: calc(var(--spacing) + var(--wa-focus-ring-offset));
|
||||
outline-offset: calc(var(--wa-panel-border-width) + var(--wa-focus-ring-offset));
|
||||
}
|
||||
}
|
||||
|
||||
:host([open]) summary {
|
||||
border-end-start-radius: 0;
|
||||
border-end-end-radius: 0;
|
||||
}
|
||||
|
||||
/* 'Start' icon placement */
|
||||
:host([icon-placement='start']) summary {
|
||||
flex-direction: row-reverse;
|
||||
justify-content: start;
|
||||
}
|
||||
|
||||
[part~='icon'] {
|
||||
flex: 0 0 auto;
|
||||
display: flex;
|
||||
@@ -122,8 +112,7 @@ details {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Overflows get clipped during the closing animation if we don't wait until the close is gone. */
|
||||
:host(:not([open])) .body {
|
||||
.body.animating {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@@ -133,10 +122,3 @@ details {
|
||||
padding-inline: var(--spacing); /* Add horizontal padding */
|
||||
padding-block-end: var(--spacing); /* Add bottom padding */
|
||||
}
|
||||
|
||||
@keyframes show {
|
||||
from {
|
||||
}
|
||||
to {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,13 @@ describe('<wa-details>', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should reflect the name property', async () => {
|
||||
const el = await fixture<WaDetails>(html`<wa-details></wa-details>`);
|
||||
el.name = 'test'
|
||||
await el.updateComplete;
|
||||
expect(el.getAttribute('name')).to.equal('test');
|
||||
});
|
||||
|
||||
it('should be visible with the open attribute', async () => {
|
||||
const el = await fixture<WaDetails>(html`
|
||||
<wa-details open>
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
import type { PropertyValues } from 'lit';
|
||||
import { html } from 'lit';
|
||||
import { customElement, property, query } from 'lit/decorators.js';
|
||||
import { customElement, property, query, state } from 'lit/decorators.js';
|
||||
import { classMap } from 'lit/directives/class-map.js';
|
||||
import { WaAfterHideEvent } from '../../events/after-hide.js';
|
||||
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';
|
||||
@@ -40,6 +43,8 @@ import styles from './details.css';
|
||||
* @cssproperty --spacing - The amount of space around and between the details' content. Expects a single value.
|
||||
* @cssproperty [--show-duration=200ms] - The show duration to use when applying built-in animation classes.
|
||||
* @cssproperty [--hide-duration=200ms] - The hide duration to use when applying built-in animation classes.
|
||||
*
|
||||
* @cssstate animating - Applied when the details is animating expand/collapse.
|
||||
*/
|
||||
@customElement('wa-details')
|
||||
export default class WaDetails extends WebAwesomeElement {
|
||||
@@ -53,6 +58,8 @@ export default class WaDetails extends WebAwesomeElement {
|
||||
@query('.body') body: HTMLElement;
|
||||
@query('.expand-icon-slot') expandIconSlot: HTMLSlotElement;
|
||||
|
||||
@state() isAnimating = false;
|
||||
|
||||
/**
|
||||
* Indicates whether or not the details is open. You can toggle this attribute to show and hide the details, or you
|
||||
* can use the `show()` and `hide()` methods and this attribute will reflect the details' open state.
|
||||
@@ -63,7 +70,7 @@ export default class WaDetails extends WebAwesomeElement {
|
||||
@property() summary: string;
|
||||
|
||||
/** Groups related details elements. When one opens, others with the same name will close. */
|
||||
@property() name: string;
|
||||
@property({ reflect: true }) name: string;
|
||||
|
||||
/** Disables the details so it can't be toggled. */
|
||||
@property({ type: Boolean, reflect: true }) disabled = false;
|
||||
@@ -71,6 +78,14 @@ export default class WaDetails extends WebAwesomeElement {
|
||||
/** The element's visual appearance. */
|
||||
@property({ reflect: true }) appearance: 'filled' | 'outlined' | 'plain' = 'outlined';
|
||||
|
||||
/** The location of the expand/collapse icon. */
|
||||
@property({ attribute: 'icon-placement', reflect: true }) iconPlacement: 'start' | 'end' = 'end';
|
||||
|
||||
disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
this.detailsObserver?.disconnect();
|
||||
}
|
||||
|
||||
firstUpdated() {
|
||||
this.body.style.height = this.open ? 'auto' : '0';
|
||||
if (this.open) {
|
||||
@@ -91,15 +106,34 @@ export default class WaDetails extends WebAwesomeElement {
|
||||
this.detailsObserver.observe(this.details, { attributes: true });
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
this.detailsObserver?.disconnect();
|
||||
updated(changedProperties: PropertyValues<this>) {
|
||||
if (changedProperties.has('isAnimating')) {
|
||||
this.customStates.set('animating', this.isAnimating);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -168,6 +202,7 @@ export default class WaDetails extends WebAwesomeElement {
|
||||
// Close other details with the same name
|
||||
this.closeOthersWithSameName();
|
||||
|
||||
this.isAnimating = true;
|
||||
const duration = parseDuration(getComputedStyle(this.body).getPropertyValue('--show-duration'));
|
||||
// We can't animate to 'auto', so use the scroll height for now
|
||||
await animate(
|
||||
@@ -182,6 +217,7 @@ export default class WaDetails extends WebAwesomeElement {
|
||||
},
|
||||
);
|
||||
this.body.style.height = 'auto';
|
||||
this.isAnimating = false;
|
||||
|
||||
this.dispatchEvent(new WaAfterShowEvent());
|
||||
} else {
|
||||
@@ -194,6 +230,7 @@ export default class WaDetails extends WebAwesomeElement {
|
||||
return;
|
||||
}
|
||||
|
||||
this.isAnimating = true;
|
||||
const duration = parseDuration(getComputedStyle(this.body).getPropertyValue('--hide-duration'));
|
||||
// We can't animate from 'auto', so use the scroll height for now
|
||||
await animate(
|
||||
@@ -205,7 +242,7 @@ export default class WaDetails extends WebAwesomeElement {
|
||||
{ duration, easing: 'linear' },
|
||||
);
|
||||
this.body.style.height = 'auto';
|
||||
|
||||
this.isAnimating = false;
|
||||
this.details.open = false;
|
||||
this.dispatchEvent(new WaAfterHideEvent());
|
||||
}
|
||||
@@ -250,25 +287,22 @@ export default class WaDetails extends WebAwesomeElement {
|
||||
|
||||
<span part="icon">
|
||||
<slot name="expand-icon">
|
||||
<wa-icon
|
||||
library="system"
|
||||
variant="solid"
|
||||
name=${isRtl ? 'chevron-left' : 'chevron-right'}
|
||||
fixed-width
|
||||
></wa-icon>
|
||||
<wa-icon library="system" variant="solid" name=${isRtl ? 'chevron-left' : 'chevron-right'}></wa-icon>
|
||||
</slot>
|
||||
<slot name="collapse-icon">
|
||||
<wa-icon
|
||||
library="system"
|
||||
variant="solid"
|
||||
name=${isRtl ? 'chevron-left' : 'chevron-right'}
|
||||
fixed-width
|
||||
></wa-icon>
|
||||
<wa-icon library="system" variant="solid" name=${isRtl ? 'chevron-left' : 'chevron-right'}></wa-icon>
|
||||
</slot>
|
||||
</span>
|
||||
</summary>
|
||||
|
||||
<div class="body" role="region" aria-labelledby="header">
|
||||
<div
|
||||
class=${classMap({
|
||||
body: true,
|
||||
animating: this.isAnimating,
|
||||
})}
|
||||
role="region"
|
||||
aria-labelledby="header"
|
||||
>
|
||||
<slot part="content" id="content" class="content"></slot>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
@@ -111,6 +111,15 @@
|
||||
padding: var(--spacing);
|
||||
overflow: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
outline: var(--wa-focus-ring);
|
||||
outline-offset: var(--wa-focus-ring-offset);
|
||||
}
|
||||
}
|
||||
|
||||
.footer {
|
||||
|
||||
@@ -37,6 +37,7 @@ import styles from './dialog.css';
|
||||
* behavior such as data loss.
|
||||
* @event wa-after-hide - Emitted after the dialog closes and all animations are complete.
|
||||
*
|
||||
* @csspart dialog - The dialog's internal `<dialog>` element.
|
||||
* @csspart header - The dialog's header. This element wraps the title and header actions.
|
||||
* @csspart header-actions - Optional actions to add to the header. Works best with `<wa-button>`.
|
||||
* @csspart title - The dialog's title.
|
||||
@@ -205,9 +206,7 @@ export default class WaDialog extends WebAwesomeElement {
|
||||
}
|
||||
|
||||
render() {
|
||||
const hasHeader =
|
||||
!this.withoutHeader &&
|
||||
(this.label.length > 0 || this.hasSlotController.test('label') || this.hasSlotController.test('header-actions'));
|
||||
const hasHeader = !this.withoutHeader;
|
||||
const hasFooter = this.hasSlotController.test('footer');
|
||||
|
||||
return html`
|
||||
@@ -226,7 +225,7 @@ export default class WaDialog extends WebAwesomeElement {
|
||||
<header part="header" class="header">
|
||||
<h2 part="title" class="title" id="title">
|
||||
<!-- If there's no label, use an invisible character to prevent the header from collapsing -->
|
||||
<slot name="label"> ${this.label.length > 0 ? this.label : String.fromCharCode(65279)} </slot>
|
||||
<slot name="label"> ${this.label.length > 0 ? this.label : String.fromCharCode(8203)} </slot>
|
||||
</h2>
|
||||
<div part="header-actions" class="header-actions">
|
||||
<slot name="header-actions"></slot>
|
||||
|
||||
@@ -175,6 +175,15 @@
|
||||
padding: var(--spacing);
|
||||
overflow: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
outline: var(--wa-focus-ring);
|
||||
outline-offset: var(--wa-focus-ring-offset);
|
||||
}
|
||||
}
|
||||
|
||||
.footer {
|
||||
|
||||
@@ -38,6 +38,7 @@ import styles from './drawer.css';
|
||||
* the drawer has been closed programmatically. Avoid using this unless closing the drawer will result in destructive
|
||||
* behavior such as data loss.
|
||||
*
|
||||
* @csspart dialog - The drawer's internal `<dialog>` element.
|
||||
* @csspart header - The drawer's header. This element wraps the title and header actions.
|
||||
* @csspart header-actions - Optional actions to add to the header. Works best with `<wa-button>`.
|
||||
* @csspart title - The drawer's title.
|
||||
@@ -218,9 +219,7 @@ export default class WaDrawer extends WebAwesomeElement {
|
||||
}
|
||||
|
||||
render() {
|
||||
const hasHeader =
|
||||
!this.withoutHeader &&
|
||||
(this.label.length > 0 || this.hasSlotController.test('label') || this.hasSlotController.test('header-actions'));
|
||||
const hasHeader = !this.withoutHeader;
|
||||
const hasFooter = this.hasSlotController.test('footer');
|
||||
|
||||
return html`
|
||||
@@ -243,7 +242,7 @@ export default class WaDrawer extends WebAwesomeElement {
|
||||
<header part="header" class="header">
|
||||
<h2 part="title" class="title" id="title">
|
||||
<!-- If there's no label, use an invisible character to prevent the header from collapsing -->
|
||||
<slot name="label"> ${this.label.length > 0 ? this.label : String.fromCharCode(65279)} </slot>
|
||||
<slot name="label"> ${this.label.length > 0 ? this.label : String.fromCharCode(8203)} </slot>
|
||||
</h2>
|
||||
<div part="header-actions" class="header-actions">
|
||||
<slot name="header-actions"></slot>
|
||||
|
||||
@@ -17,6 +17,9 @@
|
||||
color: var(--wa-color-text-normal);
|
||||
text-align: start;
|
||||
user-select: none;
|
||||
overflow: auto;
|
||||
max-width: var(--auto-size-available-width) !important;
|
||||
max-height: var(--auto-size-available-height) !important;
|
||||
|
||||
&.show {
|
||||
animation: show var(--show-duration) ease;
|
||||
|
||||
@@ -8,6 +8,7 @@ import { WaAfterShowEvent } from '../../events/after-show.js';
|
||||
import { WaHideEvent } from '../../events/hide.js';
|
||||
import { WaSelectEvent } from '../../events/select.js';
|
||||
import { WaShowEvent } from '../../events/show.js';
|
||||
import { activeElements } from '../../internal/active-elements.js';
|
||||
import { animateWithClass } from '../../internal/animate.js';
|
||||
import { uniqueId } from '../../internal/math.js';
|
||||
import WebAwesomeElement from '../../internal/webawesome-element.js';
|
||||
@@ -123,19 +124,30 @@ export default class WaDropdown extends WebAwesomeElement {
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets all <wa-dropdown-item> elements slotted in the menu that aren't disabled. */
|
||||
/** Gets all dropdown items slotted in the menu. */
|
||||
private getItems(includeDisabled = false): WaDropdownItem[] {
|
||||
const items = [...this.children].filter(
|
||||
el => el.localName === 'wa-dropdown-item' && !el.hasAttribute('slot'),
|
||||
) as WaDropdownItem[];
|
||||
const items = this.defaultSlot
|
||||
.assignedElements({ flatten: true })
|
||||
.filter(el => el.localName === 'wa-dropdown-item') as WaDropdownItem[];
|
||||
|
||||
return includeDisabled ? items : items.filter(item => !item.disabled);
|
||||
}
|
||||
|
||||
/** Gets all dropdown items in a specific submenu. */
|
||||
private getSubmenuItems(parentItem: WaDropdownItem, includeDisabled = false): WaDropdownItem[] {
|
||||
const items = [...parentItem.children].filter(
|
||||
el => el.localName === 'wa-dropdown-item' && el.getAttribute('slot') === 'submenu',
|
||||
) as WaDropdownItem[];
|
||||
// Find the submenu slot within the parent item
|
||||
const submenuSlot =
|
||||
parentItem.shadowRoot?.querySelector<HTMLSlotElement>('slot[name="submenu"]') ||
|
||||
parentItem.querySelector<HTMLSlotElement>('slot[name="submenu"]');
|
||||
if (!submenuSlot) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Get the items from the submenu slot
|
||||
const items = submenuSlot
|
||||
.assignedElements({ flatten: true })
|
||||
.filter(el => el.localName === 'wa-dropdown-item') as WaDropdownItem[];
|
||||
|
||||
return includeDisabled ? items : items.filter(item => !item.disabled);
|
||||
}
|
||||
|
||||
@@ -277,7 +289,7 @@ export default class WaDropdown extends WebAwesomeElement {
|
||||
return;
|
||||
}
|
||||
|
||||
const activeElement = document.activeElement as HTMLElement;
|
||||
const activeElement = [...activeElements()].find(el => el.localName === 'wa-dropdown-item');
|
||||
const isFocusedOnItem = activeElement?.localName === 'wa-dropdown-item';
|
||||
const currentSubmenuItem = this.getCurrentSubmenuItem();
|
||||
const isInSubmenu = !!currentSubmenuItem;
|
||||
@@ -706,7 +718,9 @@ export default class WaDropdown extends WebAwesomeElement {
|
||||
flip
|
||||
flip-fallback-strategy="best-fit"
|
||||
shift
|
||||
shift-padding="8"
|
||||
shift-padding="10"
|
||||
auto-size="vertical"
|
||||
auto-size-padding="10"
|
||||
>
|
||||
<slot
|
||||
name="trigger"
|
||||
|
||||
@@ -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>`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,30 +4,38 @@
|
||||
--secondary-color: currentColor;
|
||||
--secondary-opacity: 0.4;
|
||||
|
||||
box-sizing: content-box;
|
||||
display: inline-flex;
|
||||
box-sizing: content-box !important;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
vertical-align: -0.125em;
|
||||
}
|
||||
|
||||
/* Standard */
|
||||
:host(:not([auto-width])) {
|
||||
width: 1.25em;
|
||||
height: 1em;
|
||||
}
|
||||
|
||||
/* Auto-width */
|
||||
:host([auto-width]) {
|
||||
width: auto;
|
||||
height: 1em;
|
||||
}
|
||||
|
||||
svg {
|
||||
display: inline-block;
|
||||
width: auto;
|
||||
height: inherit;
|
||||
height: 1em;
|
||||
fill: currentColor;
|
||||
overflow: visible;
|
||||
|
||||
.fa-primary {
|
||||
/* Duotone colors with path-specific opacity fallback */
|
||||
path[data-duotone-primary] {
|
||||
color: var(--primary-color);
|
||||
opacity: var(--primary-opacity);
|
||||
opacity: var(--path-opacity, var(--primary-opacity));
|
||||
}
|
||||
|
||||
.fa-secondary {
|
||||
path[data-duotone-secondary] {
|
||||
color: var(--secondary-color);
|
||||
opacity: var(--secondary-opacity);
|
||||
opacity: var(--path-opacity, var(--secondary-opacity));
|
||||
}
|
||||
}
|
||||
|
||||
:host([fixed-width]) {
|
||||
width: 1em;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
@@ -133,17 +133,6 @@ describe('<wa-icon>', () => {
|
||||
});
|
||||
|
||||
describe('negative cases', () => {
|
||||
// using new library so we can test for malformed icons when registered
|
||||
it("svg not rendered with an icon that doesn't exist in the library", async () => {
|
||||
const el = await fixture<WaIcon>(html` <wa-icon library="test-library" name="does-not-exist"></wa-icon> `);
|
||||
|
||||
// Still renders svgs for empty icons.
|
||||
expect(el.shadowRoot?.querySelector('svg')).to.be.instanceof(SVGElement);
|
||||
|
||||
expect(el.getBoundingClientRect().height).to.equal(16);
|
||||
expect(el.getBoundingClientRect().width).to.equal(16);
|
||||
});
|
||||
|
||||
it('emits wa-error when the file cant be retrieved', async () => {
|
||||
const el = await fixture<WaIcon>(html` <wa-icon library="test-library"></wa-icon> `);
|
||||
const listener = oneEvent(el, 'wa-error') as Promise<WaErrorEvent>;
|
||||
|
||||
@@ -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,17 +54,20 @@ 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;
|
||||
|
||||
/** Draws the icon in a fixed-width both. */
|
||||
@property({ attribute: 'fixed-width', type: Boolean, reflect: true }) fixedWidth: false;
|
||||
/** 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;
|
||||
|
||||
/** Swaps the opacity of duotone icons. */
|
||||
@property({ attribute: 'swap-opacity', type: Boolean, reflect: true }) swapOpacity = false;
|
||||
|
||||
/**
|
||||
* An external URL of an SVG file. Be sure you trust the content you are including, as it will be executed as code and
|
||||
@@ -79,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();
|
||||
@@ -103,7 +106,7 @@ export default class WaIcon extends WebAwesomeElement {
|
||||
|
||||
if (this.name && library) {
|
||||
return {
|
||||
url: library.resolver(this.name, family, this.variant),
|
||||
url: library.resolver(this.name, family, this.variant, this.autoWidth),
|
||||
fromLibrary: true,
|
||||
};
|
||||
}
|
||||
@@ -115,7 +118,7 @@ export default class WaIcon extends WebAwesomeElement {
|
||||
}
|
||||
|
||||
/** Given a URL, this function returns the resulting SVG element or an appropriate error symbol. */
|
||||
private async resolveIcon(url: string, library?: IconLibrary): Promise<SVGResult> {
|
||||
private resolveIcon = async (url: string, library?: IconLibrary): Promise<SVGResult> => {
|
||||
let fileData: Response;
|
||||
|
||||
if (library?.spriteSheet) {
|
||||
@@ -133,10 +136,10 @@ export default class WaIcon extends WebAwesomeElement {
|
||||
// to be passed to the library's mutator function.
|
||||
await this.updateComplete;
|
||||
|
||||
const svg = this.shadowRoot!.querySelector("[part='svg']")!;
|
||||
const svg = this.shadowRoot!.querySelector<SVGElement>("[part='svg']")!;
|
||||
|
||||
if (typeof library.mutator === 'function') {
|
||||
library.mutator(svg as SVGElement);
|
||||
library.mutator(svg, this);
|
||||
}
|
||||
|
||||
return this.svg;
|
||||
@@ -167,7 +170,7 @@ export default class WaIcon extends WebAwesomeElement {
|
||||
} catch {
|
||||
return CACHEABLE_ERROR;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@watch('label')
|
||||
handleLabelChange() {
|
||||
@@ -184,7 +187,7 @@ export default class WaIcon extends WebAwesomeElement {
|
||||
}
|
||||
}
|
||||
|
||||
@watch(['family', 'name', 'library', 'variant', 'src'])
|
||||
@watch(['family', 'name', 'library', 'variant', 'src', 'autoWidth', 'swapOpacity'])
|
||||
async setIcon() {
|
||||
const { url, fromLibrary } = this.getIconSource();
|
||||
const library = fromLibrary ? getIconLibrary(this.library) : undefined;
|
||||
@@ -224,7 +227,7 @@ export default class WaIcon extends WebAwesomeElement {
|
||||
break;
|
||||
default:
|
||||
this.svg = svg.cloneNode(true) as SVGElement;
|
||||
library?.mutator?.(this.svg);
|
||||
library?.mutator?.(this.svg, this);
|
||||
this.dispatchEvent(new WaLoadEvent());
|
||||
}
|
||||
}
|
||||
@@ -237,7 +240,7 @@ export default class WaIcon extends WebAwesomeElement {
|
||||
|
||||
const svg = this.shadowRoot?.querySelector('svg');
|
||||
if (svg) {
|
||||
library?.mutator?.(svg);
|
||||
library?.mutator?.(svg, this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,55 @@
|
||||
import { getKitCode } from '../../utilities/base-path.js';
|
||||
import type { IconLibrary } from './library.js';
|
||||
|
||||
const FA_VERSION = '7.0.1';
|
||||
|
||||
function getIconUrl(name: string, family: string, variant: string) {
|
||||
const kitCode = getKitCode();
|
||||
const isPro = kitCode.length > 0;
|
||||
let folder = 'solid';
|
||||
|
||||
// Notdog (Pro+)
|
||||
if (family === 'notdog') {
|
||||
if (variant === 'solid') folder = 'solid';
|
||||
if (variant === 'duo-solid') folder = 'duo-solid';
|
||||
return `https://ka-p.fontawesome.com/releases/v${FA_VERSION}/svgs/notdog-${folder}/${name}.svg?token=${encodeURIComponent(kitCode)}`;
|
||||
}
|
||||
|
||||
// Chisel (Pro+)
|
||||
if (family === 'chisel') {
|
||||
return `https://ka-p.fontawesome.com/releases/v${FA_VERSION}/svgs/chisel-regular/${name}.svg?token=${encodeURIComponent(kitCode)}`;
|
||||
}
|
||||
|
||||
// Etch (Pro+)
|
||||
if (family === 'etch') {
|
||||
return `https://ka-p.fontawesome.com/releases/v${FA_VERSION}/svgs/etch-solid/${name}.svg?token=${encodeURIComponent(kitCode)}`;
|
||||
}
|
||||
|
||||
// Jelly (Pro+)
|
||||
if (family === 'jelly') {
|
||||
if (variant === 'regular') folder = 'regular';
|
||||
if (variant === 'duo-regular') folder = 'duo-regular';
|
||||
if (variant === 'fill-regular') folder = 'fill-regular';
|
||||
return `https://ka-p.fontawesome.com/releases/v${FA_VERSION}/svgs/jelly-${folder}/${name}.svg?token=${encodeURIComponent(kitCode)}`;
|
||||
}
|
||||
|
||||
// Slab (Pro+)
|
||||
if (family === 'slab') {
|
||||
if (variant === 'solid' || variant === 'regular') folder = 'regular';
|
||||
if (variant === 'press-regular') folder = 'press-regular';
|
||||
return `https://ka-p.fontawesome.com/releases/v${FA_VERSION}/svgs/slab-${folder}/${name}.svg?token=${encodeURIComponent(kitCode)}`;
|
||||
}
|
||||
|
||||
// Thumbprint (Pro+)
|
||||
if (family === 'thumbprint') {
|
||||
return `https://ka-p.fontawesome.com/releases/v${FA_VERSION}/svgs/thumbprint-light/${name}.svg?token=${encodeURIComponent(kitCode)}`;
|
||||
}
|
||||
|
||||
// Whiteboard (Pro+)
|
||||
if (family === 'whiteboard') {
|
||||
return `https://ka-p.fontawesome.com/releases/v${FA_VERSION}/svgs/whiteboard-semibold/${name}.svg?token=${encodeURIComponent(kitCode)}`;
|
||||
}
|
||||
|
||||
// Classic
|
||||
if (family === 'classic') {
|
||||
if (variant === 'thin') folder = 'thin';
|
||||
@@ -45,8 +89,8 @@ function getIconUrl(name: string, family: string, variant: string) {
|
||||
|
||||
// Use the default CDN
|
||||
return isPro
|
||||
? `https://ka-p.fontawesome.com/releases/v6.7.2/svgs/${folder}/${name}.svg?token=${encodeURIComponent(kitCode)}`
|
||||
: `https://ka-f.fontawesome.com/releases/v6.7.2/svgs/${folder}/${name}.svg`;
|
||||
? `https://ka-p.fontawesome.com/releases/v${FA_VERSION}/svgs/${folder}/${name}.svg?token=${encodeURIComponent(kitCode)}`
|
||||
: `https://ka-f.fontawesome.com/releases/v${FA_VERSION}/svgs/${folder}/${name}.svg`;
|
||||
}
|
||||
|
||||
const library: IconLibrary = {
|
||||
@@ -54,6 +98,46 @@ const library: IconLibrary = {
|
||||
resolver: (name: string, family = 'classic', variant = 'solid') => {
|
||||
return getIconUrl(name, family, variant);
|
||||
},
|
||||
mutator: (svg, hostEl) => {
|
||||
// Duotone families
|
||||
if (hostEl?.family && !svg.hasAttribute('data-duotone-initialized')) {
|
||||
const { family, variant } = hostEl;
|
||||
|
||||
if (
|
||||
// Duotone
|
||||
family === 'duotone' ||
|
||||
// Sharp duotone
|
||||
family === 'sharp-duotone' ||
|
||||
// Notdog duo-solid
|
||||
(family === 'notdog' && variant === 'duo-solid') ||
|
||||
// Jelly duo-regular
|
||||
(family === 'jelly' && variant === 'duo-regular') ||
|
||||
// Thumbprint
|
||||
family === 'thumbprint'
|
||||
) {
|
||||
// Identify the primary and secondary paths. The secondary path is the one that has an opacity attribute.
|
||||
const paths = [...svg.querySelectorAll<SVGPathElement>('path')];
|
||||
const primaryPath = paths.find(p => !p.hasAttribute('opacity'));
|
||||
const secondaryPath = paths.find(p => p.hasAttribute('opacity'));
|
||||
|
||||
if (!primaryPath || !secondaryPath) return;
|
||||
|
||||
primaryPath.setAttribute('data-duotone-primary', '');
|
||||
secondaryPath.setAttribute('data-duotone-secondary', '');
|
||||
|
||||
// Swap the primary and secondary opacity using CSS custom properties
|
||||
if (hostEl.swapOpacity && primaryPath && secondaryPath) {
|
||||
const originalOpacity = secondaryPath.getAttribute('opacity') || '0.4';
|
||||
|
||||
// Set path-specific opacity custom properties
|
||||
primaryPath.style.setProperty('--path-opacity', originalOpacity);
|
||||
secondaryPath.style.setProperty('--path-opacity', '1');
|
||||
}
|
||||
|
||||
svg.setAttribute('data-duotone-initialized', '');
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export default library;
|
||||
|
||||
@@ -9,31 +9,31 @@ export const icons: { [key: string]: { [key: string]: string } } = {
|
||||
// Solid variant
|
||||
//
|
||||
solid: {
|
||||
check: `<svg xmlns="http://www.w3.org/2000/svg" height="16" width="14" 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>`,
|
||||
'chevron-down': `<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16" viewBox="0 0 512 512"><path 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>`,
|
||||
'chevron-left': `<svg xmlns="http://www.w3.org/2000/svg" height="16" width="10" viewBox="0 0 320 512"><path 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" 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 0s12.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>`,
|
||||
circle: `<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16" viewBox="0 0 512 512"><path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512z"/></svg>`,
|
||||
eyedropper: `<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16" viewBox="0 0 512 512"><path d="M341.6 29.2L240.1 130.8l-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.4L482.8 170.4c39-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.6v42.4L5.4 462.2c-8.5 12.7-6.8 29.6 4 40.4s27.7 12.5 40.4 4L89.7 480h42.4c21.2 0 41.6-8.4 56.6-23.4L309.4 335.9l-45.3-45.3L143.4 411.3c-3 3-7.1 4.7-11.3 4.7H96V379.9c0-4.2 1.7-8.3 4.7-11.3L221.4 247.9l-45.3-45.3L55.4 323.3z"/></svg>`,
|
||||
'grip-vertical': `<svg xmlns="http://www.w3.org/2000/svg" height="16" width="10" viewBox="0 0 320 512"><path d="M40 352l48 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-40l0-48c0-22.1 17.9-40 40-40zm192 0l48 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-40l0-48c0-22.1 17.9-40 40-40zM40 320c-22.1 0-40-17.9-40-40l0-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 0zM232 192l48 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-40l0-48c0-22.1 17.9-40 40-40zM40 160c-22.1 0-40-17.9-40-40L0 72C0 49.9 17.9 32 40 32l48 0c22.1 0 40 17.9 40 40l0 48c0 22.1-17.9 40-40 40l-48 0zM232 32l48 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-40l0-48c0-22.1 17.9-40 40-40z"/></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"></path></g></g></g></svg>`,
|
||||
minus: `<svg xmlns="http://www.w3.org/2000/svg" height="16" width="14" viewBox="0 0 448 512"><path d="M432 256c0 17.7-14.3 32-32 32L48 288c-17.7 0-32-14.3-32-32s14.3-32 32-32l352 0c17.7 0 32 14.3 32 32z"/></svg>`,
|
||||
pause: `<svg xmlns="http://www.w3.org/2000/svg" height="16" width="10" viewBox="0 0 320 512"><path d="M48 64C21.5 64 0 85.5 0 112V400c0 26.5 21.5 48 48 48H80c26.5 0 48-21.5 48-48V112c0-26.5-21.5-48-48-48H48zm192 0c-26.5 0-48 21.5-48 48V400c0 26.5 21.5 48 48 48h32c26.5 0 48-21.5 48-48V112c0-26.5-21.5-48-48-48H240z"/></svg>`,
|
||||
play: `<svg xmlns="http://www.w3.org/2000/svg" height="16" width="12" viewBox="0 0 384 512"><path d="M73 39c-14.8-9.1-33.4-9.4-48.5-.9S0 62.6 0 80V432c0 17.4 9.4 33.4 24.5 41.9s33.7 8.1 48.5-.9L361 297c14.3-8.7 23-24.2 23-41s-8.7-32.2-23-41L73 39z"/></svg>`,
|
||||
star: `<svg xmlns="http://www.w3.org/2000/svg" height="16" width="18" viewBox="0 0 576 512"><path d="M316.9 18C311.6 7 300.4 0 288.1 0s-23.4 7-28.8 18L195 150.3 51.4 171.5c-12 1.8-22 10.2-25.7 21.7s-.7 24.2 7.9 32.7L137.8 329 113.2 474.7c-2 12 3 24.2 12.9 31.3s23 8 33.8 2.3l128.3-68.5 128.3 68.5c10.8 5.7 23.9 4.9 33.8-2.3s14.9-19.3 12.9-31.3L438.5 329 542.7 225.9c8.6-8.5 11.7-21.2 7.9-32.7s-13.7-19.9-25.7-21.7L381.2 150.3 316.9 18z"/></svg>`,
|
||||
user: `<svg xmlns="http://www.w3.org/2000/svg" height="16" width="14" viewBox="0 0 448 512"><path d="M224 256A128 128 0 1 0 224 0a128 128 0 1 0 0 256zm-45.7 48C79.8 304 0 383.8 0 482.3C0 498.7 13.3 512 29.7 512H418.3c16.4 0 29.7-13.3 29.7-29.7C448 383.8 368.2 304 269.7 304H178.3z"/></svg>`,
|
||||
xmark: `<svg xmlns="http://www.w3.org/2000/svg" height="16" width="12" viewBox="0 0 384 512"><path d="M342.6 150.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L192 210.7 86.6 105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L146.7 256 41.4 361.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L192 301.3 297.4 406.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L237.3 256 342.6 150.6z"/></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 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 512 512"><!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M464 256A208 208 0 1 0 48 256a208 208 0 1 0 416 0zM0 256a256 256 0 1 1 512 0A256 256 0 1 1 0 256zm169.8-90.7c7.9-22.3 29.1-37.3 52.8-37.3l58.3 0c34.9 0 63.1 28.3 63.1 63.1c0 22.6-12.1 43.5-31.7 54.8L280 264.4c-.2 13-10.9 23.6-24 23.6c-13.3 0-24-10.7-24-24l0-13.5c0-8.6 4.6-16.5 12.1-20.8l44.3-25.4c4.7-2.7 7.6-7.7 7.6-13.1c0-8.4-6.8-15.1-15.1-15.1l-58.3 0c-3.4 0-6.4 2.1-7.5 5.3l-.4 1.2c-4.4 12.5-18.2 19-30.6 14.6s-19-18.2-14.6-30.6l.4-1.2zM224 352a32 32 0 1 1 64 0 32 32 0 1 1 -64 0z"/></svg>`,
|
||||
'circle-xmark': `<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16" viewBox="0 0 512 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2023 Fonticons, Inc.--><path d="M256 48a208 208 0 1 1 0 416 208 208 0 1 1 0-416zm0 464A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM175 175c-9.4 9.4-9.4 24.6 0 33.9l47 47-47 47c-9.4 9.4-9.4 24.6 0 33.9s24.6 9.4 33.9 0l47-47 47 47c9.4 9.4 24.6 9.4 33.9 0s9.4-24.6 0-33.9l-47-47 47-47c9.4-9.4 9.4-24.6 0-33.9s-24.6-9.4-33.9 0l-47 47-47-47c-9.4-9.4-24.6-9.4-33.9 0z"/></svg>`,
|
||||
copy: `<svg xmlns="http://www.w3.org/2000/svg" height="16" width="14" viewBox="0 0 448 512"><path d="M384 336H192c-8.8 0-16-7.2-16-16V64c0-8.8 7.2-16 16-16l140.1 0L400 115.9V320c0 8.8-7.2 16-16 16zM192 384H384c35.3 0 64-28.7 64-64V115.9c0-12.7-5.1-24.9-14.1-33.9L366.1 14.1c-9-9-21.2-14.1-33.9-14.1H192c-35.3 0-64 28.7-64 64V320c0 35.3 28.7 64 64 64zM64 128c-35.3 0-64 28.7-64 64V448c0 35.3 28.7 64 64 64H256c35.3 0 64-28.7 64-64V416H272v32c0 8.8-7.2 16-16 16H64c-8.8 0-16-7.2-16-16V192c0-8.8 7.2-16 16-16H96V128H64z"/></svg>`,
|
||||
eye: `<svg xmlns="http://www.w3.org/2000/svg" height="16" width="18" viewBox="0 0 576 512"><path d="M288 80c-65.2 0-118.8 29.6-159.9 67.7C89.6 183.5 63 226 49.4 256c13.6 30 40.2 72.5 78.6 108.3C169.2 402.4 222.8 432 288 432s118.8-29.6 159.9-67.7C486.4 328.5 513 286 526.6 256c-13.6-30-40.2-72.5-78.6-108.3C406.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.1c3.3 7.9 3.3 16.7 0 24.6c-14.9 35.7-46.2 87.7-93 131.1C433.5 443.2 368.8 480 288 480s-145.5-36.8-192.6-80.6C48.6 356 17.3 304 2.5 268.3c-3.3-7.9-3.3-16.7 0-24.6C17.3 208 48.6 156 95.4 112.6zM288 336c44.2 0 80-35.8 80-80s-35.8-80-80-80c-.7 0-1.3 0-2 0c1.3 5.1 2 10.5 2 16c0 35.3-28.7 64-64 64c-5.5 0-10.9-.7-16-2c0 .7 0 1.3 0 2c0 44.2 35.8 80 80 80zm0-208a128 128 0 1 1 0 256 128 128 0 1 1 0-256z"/></svg>`,
|
||||
'eye-slash': `<svg xmlns="http://www.w3.org/2000/svg" height="16" width="20" viewBox="0 0 640 512"><path d="M38.8 5.1C28.4-3.1 13.3-1.2 5.1 9.2S-1.2 34.7 9.2 42.9l592 464c10.4 8.2 25.5 6.3 33.7-4.1s6.3-25.5-4.1-33.7L525.6 386.7c39.6-40.6 66.4-86.1 79.9-118.4c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C465.5 68.8 400.8 32 320 32c-68.2 0-125 26.3-169.3 60.8L38.8 5.1zm151 118.3C226 97.7 269.5 80 320 80c65.2 0 118.8 29.6 159.9 67.7C518.4 183.5 545 226 558.6 256c-12.6 28-36.6 66.8-70.9 100.9l-53.8-42.2c9.1-17.6 14.2-37.5 14.2-58.7c0-70.7-57.3-128-128-128c-32.2 0-61.7 11.9-84.2 31.5l-46.1-36.1zM394.9 284.2l-81.5-63.9c4.2-8.5 6.6-18.2 6.6-28.3c0-5.5-.7-10.9-2-16c.7 0 1.3 0 2 0c44.2 0 80 35.8 80 80c0 9.9-1.8 19.4-5.1 28.2zm51.3 163.3l-41.9-33C378.8 425.4 350.7 432 320 432c-65.2 0-118.8-29.6-159.9-67.7C121.6 328.5 95 286 81.4 256c8.3-18.4 21.5-41.5 39.4-64.8L83.1 161.5C60.3 191.2 44 220.8 34.5 243.7c-3.3 7.9-3.3 16.7 0 24.6c14.9 35.7 46.2 87.7 93 131.1C174.5 443.2 239.2 480 320 480c47.8 0 89.9-12.9 126.2-32.5zm-88-69.3L302 334c-23.5-5.4-43.1-21.2-53.7-42.3l-56.1-44.2c-.2 2.8-.3 5.6-.3 8.5c0 70.7 57.3 128 128 128c13.3 0 26.1-2 38.2-5.8z"/></svg>`,
|
||||
star: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path d="M287.9 0c9.2 0 17.6 5.2 21.6 13.5l68.6 141.3 153.2 22.6c9 1.3 16.5 7.6 19.3 16.3s.5 18.1-5.9 24.5L433.6 328.4l26.2 155.6c1.5 9-2.2 18.1-9.7 23.5s-17.3 6-25.3 1.7l-137-73.2L151 509.1c-8.1 4.3-17.9 3.7-25.3-1.7s-11.2-14.5-9.7-23.5l26.2-155.6L31.1 218.2c-6.5-6.4-8.7-15.9-5.9-24.5s10.3-14.9 19.3-16.3l153.2-22.6L266.3 13.5C270.4 5.2 278.7 0 287.9 0zm0 79L235.4 187.2c-3.5 7.1-10.2 12.1-18.1 13.3L99 217.9 184.9 303c5.5 5.5 8.1 13.3 6.8 21L171.4 443.7l105.2-56.2c7.1-3.8 15.6-3.8 22.6 0l105.2 56.2L384.2 324.1c-1.3-7.7 1.2-15.5 6.8-21l85.9-85.1L358.6 200.5c-7.8-1.2-14.6-6.1-18.1-13.3L287.9 79z"/></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>`,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -2,8 +2,9 @@ import type WaIcon from '../icon/icon.js';
|
||||
import defaultLibrary from './library.default.js';
|
||||
import systemLibrary from './library.system.js';
|
||||
|
||||
export type IconLibraryResolver = (name: string, family: string, variant: string) => string;
|
||||
export type IconLibraryMutator = (svg: SVGElement) => void;
|
||||
export type IconLibraryHostElement = WaIcon;
|
||||
export type IconLibraryResolver = (name: string, family: string, variant: string, autoWidth: boolean) => string;
|
||||
export type IconLibraryMutator = (svg: SVGElement, hostElement?: IconLibraryHostElement) => void;
|
||||
export interface IconLibrary {
|
||||
name: string;
|
||||
resolver: IconLibraryResolver;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)}
|
||||
@@ -432,8 +432,9 @@ export default class WaInput extends WebAwesomeFormAssociatedElement {
|
||||
</div>
|
||||
|
||||
<slot
|
||||
name="hint"
|
||||
id="hint"
|
||||
part="hint"
|
||||
name="hint"
|
||||
class=${classMap({
|
||||
'has-slotted': hasHint,
|
||||
})}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
:host {
|
||||
display: contents;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { expect, fixture, html } from '@open-wc/testing';
|
||||
|
||||
describe('<wa-intersection-observer>', () => {
|
||||
it('should render a component', async () => {
|
||||
const el = await fixture(html` <wa-intersection-observer></wa-intersection-observer> `);
|
||||
|
||||
expect(el).to.exist;
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,200 @@
|
||||
import { html } from 'lit';
|
||||
import { customElement, property } from 'lit/decorators.js';
|
||||
import { WaIntersectEvent } from '../../events/intersect.js';
|
||||
import { clamp } from '../../internal/math.js';
|
||||
import { parseSpaceDelimitedTokens } from '../../internal/parse.js';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import WebAwesomeElement from '../../internal/webawesome-element.js';
|
||||
import styles from './intersection-observer.css';
|
||||
|
||||
/**
|
||||
* @summary Tracks immediate child elements and fires events as they move in and out of view.
|
||||
* @documentation https://webawesome.com/docs/components/intersection-observer
|
||||
* @status stable
|
||||
* @since 2.0
|
||||
*
|
||||
* @slot - Elements to track. Only immediate children of the host are monitored.
|
||||
*
|
||||
* @event {{ entry: IntersectionObserverEntry }} wa-intersect - Fired when a tracked element begins or ceases intersecting.
|
||||
*/
|
||||
@customElement('wa-intersection-observer')
|
||||
export default class WaIntersectionObserver extends WebAwesomeElement {
|
||||
static css = styles;
|
||||
|
||||
private intersectionObserver: IntersectionObserver | null = null;
|
||||
private observedElements = new Map<Element, boolean>();
|
||||
|
||||
/** Element ID to define the viewport boundaries for tracked targets. */
|
||||
@property() root: string | null = null;
|
||||
|
||||
/** Offset space around the root boundary. Accepts values like CSS margin syntax. */
|
||||
@property({ attribute: 'root-margin' }) rootMargin = '0px';
|
||||
|
||||
/** One or more space-separated values representing visibility percentages that trigger the observer callback. */
|
||||
@property() threshold = '0';
|
||||
|
||||
/**
|
||||
* CSS class applied to elements during intersection. Automatically removed when elements leave
|
||||
* the viewport, enabling pure CSS styling based on visibility state.
|
||||
*/
|
||||
@property({ attribute: 'intersect-class' }) intersectClass = '';
|
||||
|
||||
/** If enabled, observation ceases after initial intersection. */
|
||||
@property({ type: Boolean, reflect: true }) once = false;
|
||||
|
||||
/** Deactivates the intersection observer functionality. */
|
||||
@property({ type: Boolean, reflect: true }) disabled = false;
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
|
||||
if (!this.disabled) {
|
||||
this.updateComplete.then(() => {
|
||||
this.startObserver();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
this.stopObserver();
|
||||
}
|
||||
|
||||
private handleSlotChange() {
|
||||
if (!this.disabled) {
|
||||
this.startObserver();
|
||||
}
|
||||
}
|
||||
|
||||
/** Converts threshold property string into numeric array. */
|
||||
private parseThreshold(): number[] {
|
||||
const tokens = parseSpaceDelimitedTokens(this.threshold);
|
||||
return tokens.map((token: string) => {
|
||||
const num = parseFloat(token);
|
||||
return isNaN(num) ? 0 : clamp(num, 0, 1);
|
||||
});
|
||||
}
|
||||
|
||||
/** Locates and returns the root element using the specified ID. */
|
||||
private resolveRoot(): Element | null {
|
||||
if (!this.root) return null;
|
||||
|
||||
try {
|
||||
const doc = this.getRootNode() as Document | ShadowRoot;
|
||||
const target = doc.getElementById(this.root);
|
||||
|
||||
if (!target) {
|
||||
console.warn(`Root element with ID "${this.root}" could not be found.`, this);
|
||||
}
|
||||
|
||||
return target;
|
||||
} catch {
|
||||
console.warn(`Invalid selector for root: "${this.root}"`, this);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/** Initializes or reinitializes the intersection observer instance. */
|
||||
private startObserver() {
|
||||
this.stopObserver();
|
||||
|
||||
// Skip setup if functionality is disabled
|
||||
if (this.disabled) return;
|
||||
|
||||
// Convert threshold string to numeric values
|
||||
const threshold = this.parseThreshold();
|
||||
|
||||
// Locate the root boundary element
|
||||
const rootElement = this.resolveRoot();
|
||||
|
||||
// Set up unified observer for all child elements
|
||||
this.intersectionObserver = new IntersectionObserver(
|
||||
entries => {
|
||||
entries.forEach(entry => {
|
||||
const wasIntersecting = this.observedElements.get(entry.target) ?? false;
|
||||
const isIntersecting = entry.isIntersecting;
|
||||
|
||||
// Record current intersection state
|
||||
this.observedElements.set(entry.target, isIntersecting);
|
||||
|
||||
// Toggle intersection class based on visibility
|
||||
if (this.intersectClass) {
|
||||
if (isIntersecting) {
|
||||
entry.target.classList.add(this.intersectClass);
|
||||
} else {
|
||||
entry.target.classList.remove(this.intersectClass);
|
||||
}
|
||||
}
|
||||
|
||||
// Emit the intersection event
|
||||
const changeEvent = new WaIntersectEvent({ entry });
|
||||
this.dispatchEvent(changeEvent);
|
||||
|
||||
if (isIntersecting && !wasIntersecting) {
|
||||
// When once mode is active, cease tracking after first intersection
|
||||
if (this.once) {
|
||||
this.intersectionObserver?.unobserve(entry.target);
|
||||
this.observedElements.delete(entry.target);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
{
|
||||
root: rootElement,
|
||||
rootMargin: this.rootMargin,
|
||||
threshold,
|
||||
},
|
||||
);
|
||||
|
||||
// Begin tracking all immediate child elements
|
||||
const slot = this.shadowRoot!.querySelector('slot');
|
||||
if (slot !== null) {
|
||||
const elements = slot.assignedElements({ flatten: true });
|
||||
elements.forEach(element => {
|
||||
this.intersectionObserver?.observe(element);
|
||||
// Set initial non-intersecting state
|
||||
this.observedElements.set(element, false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/** Halts the intersection observer and cleans up. */
|
||||
private stopObserver() {
|
||||
// Clear intersection classes from all tracked elements before stopping
|
||||
if (this.intersectClass) {
|
||||
this.observedElements.forEach((_, element) => {
|
||||
element.classList.remove(this.intersectClass);
|
||||
});
|
||||
}
|
||||
|
||||
this.intersectionObserver?.disconnect();
|
||||
this.intersectionObserver = null;
|
||||
this.observedElements.clear();
|
||||
}
|
||||
|
||||
@watch('disabled', { waitUntilFirstUpdate: true })
|
||||
handleDisabledChange() {
|
||||
if (this.disabled) {
|
||||
this.stopObserver();
|
||||
} else {
|
||||
this.startObserver();
|
||||
}
|
||||
}
|
||||
|
||||
@watch('root', { waitUntilFirstUpdate: true })
|
||||
@watch('rootMargin', { waitUntilFirstUpdate: true })
|
||||
@watch('threshold', { waitUntilFirstUpdate: true })
|
||||
handleOptionsChange() {
|
||||
this.startObserver();
|
||||
}
|
||||
|
||||
render() {
|
||||
return html` <slot @slotchange=${this.handleSlotChange}></slot> `;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'wa-intersection-observer': WaIntersectionObserver;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -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;
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -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];
|
||||
@@ -85,6 +85,9 @@ export default class WaPopover extends WebAwesomeElement {
|
||||
/** The ID of the popover's anchor element. This must be an interactive/focusable element such as a button. */
|
||||
@property() for: string | null = null;
|
||||
|
||||
/** Removes the arrow from the popover. */
|
||||
@property({ attribute: 'without-arrow', type: Boolean, reflect: true }) withoutArrow = false;
|
||||
|
||||
private eventController = new AbortController();
|
||||
|
||||
connectedCallback() {
|
||||
@@ -300,7 +303,7 @@ export default class WaPopover extends WebAwesomeElement {
|
||||
skidding=${this.skidding}
|
||||
flip
|
||||
shift
|
||||
arrow
|
||||
?arrow=${!this.withoutArrow}
|
||||
.anchor=${this.anchor}
|
||||
>
|
||||
<div part="body" class="body" @click=${this.handleBodyClick}>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
:host {
|
||||
--size: 8rem;
|
||||
--track-width: 0.25em; /* avoid using rems here - https://github.com/shoelace-style/webawesome-alpha/issues/89 */
|
||||
--track-width: 0.25em; /* avoid using rems here */
|
||||
--track-color: var(--wa-color-neutral-fill-normal);
|
||||
--indicator-width: var(--track-width);
|
||||
--indicator-color: var(--wa-color-brand-fill-loud);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user