mirror of
https://github.com/shoelace-style/webawesome.git
synced 2026-01-13 12:39:14 +00:00
Compare commits
137 Commits
data-actio
...
fix-spinne
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
7b18be876b | ||
|
|
c9e6895ef7 | ||
|
|
64c8647dee | ||
|
|
9e9a00547a | ||
|
|
f747483e32 | ||
|
|
8719bbc88b | ||
|
|
0ff5e7fb7a | ||
|
|
2a52b2766f | ||
|
|
1e8bbc3b06 | ||
|
|
1ef9cb9601 | ||
|
|
5b54410212 | ||
|
|
f621fbb224 | ||
|
|
f5624fbf4a | ||
|
|
f65bc3918e | ||
|
|
f49c10b05b | ||
|
|
3b6c018fed | ||
|
|
c10e1e77c9 | ||
|
|
a1e879035c | ||
|
|
d2625fccab | ||
|
|
29c8ad08bb | ||
|
|
0f68e6f0dd | ||
|
|
4dca2b9541 | ||
|
|
36ccaa4aaa | ||
|
|
c86d7635aa | ||
|
|
d84e842a4e | ||
|
|
fb8c06235f | ||
|
|
f6a10e9dda | ||
|
|
5ce34677ed | ||
|
|
8ebc839dfd | ||
|
|
2779eb1753 | ||
|
|
341ca809e9 | ||
|
|
7232ddee5d | ||
|
|
f63001f18e | ||
|
|
8b831f6ece | ||
|
|
4dee3d0f2d | ||
|
|
10a78d99af | ||
|
|
99d9024339 | ||
|
|
313e31a3f2 | ||
|
|
4eeabc57e5 | ||
|
|
c8c0d0f848 | ||
|
|
e8687b895d | ||
|
|
7743b670ed | ||
|
|
88e99d7cd3 | ||
|
|
c0d18ab9f0 | ||
|
|
6576f67cfa | ||
|
|
057ff5fb31 | ||
|
|
1dac76a35e | ||
|
|
e7f2962984 | ||
|
|
956dc9bdd9 | ||
|
|
62020be8c1 | ||
|
|
429cf68301 | ||
|
|
dd77663a75 | ||
|
|
fa9f18f002 | ||
|
|
c2e2e1afcc | ||
|
|
2628f4ff7c | ||
|
|
16722a191d | ||
|
|
21243729c6 | ||
|
|
1581b99796 | ||
|
|
103cbc439e | ||
|
|
3a40ffe58a | ||
|
|
453e8ff501 | ||
|
|
dc556e5379 | ||
|
|
c11d6129a3 | ||
|
|
dd5c32680a | ||
|
|
81996cbc63 | ||
|
|
d20945d78b | ||
|
|
07327be95e | ||
|
|
b99d7771dc | ||
|
|
10cc9bdc68 | ||
|
|
bc598dad92 | ||
|
|
ff61ac002f | ||
|
|
63ec9d5bc1 | ||
|
|
555327b2fc | ||
|
|
15c6eaec90 | ||
|
|
6f856fbd89 | ||
|
|
df46e1db3b | ||
|
|
d21d829c29 | ||
|
|
2331e88dcf | ||
|
|
c162983ca2 | ||
|
|
0d09037916 | ||
|
|
d2e32a0a53 | ||
|
|
71d45eabbd |
3
.vscode/extensions.json
vendored
3
.vscode/extensions.json
vendored
@@ -3,6 +3,7 @@
|
||||
"dbaeumer.vscode-eslint",
|
||||
"esbenp.prettier-vscode",
|
||||
"bierner.lit-html",
|
||||
"streetsidesoftware.code-spell-checker"
|
||||
"streetsidesoftware.code-spell-checker",
|
||||
"ronnidc.nunjucks"
|
||||
]
|
||||
}
|
||||
|
||||
19
cspell.json
19
cspell.json
@@ -8,8 +8,10 @@
|
||||
"APG",
|
||||
"apos",
|
||||
"atrule",
|
||||
"autocapitalize",
|
||||
"autocorrect",
|
||||
"autofix",
|
||||
"autofocus",
|
||||
"autoload",
|
||||
"autoloader",
|
||||
"autoloading",
|
||||
@@ -43,16 +45,22 @@
|
||||
"csspart",
|
||||
"cssproperty",
|
||||
"cssstate",
|
||||
"datalist",
|
||||
"datetime",
|
||||
"describedby",
|
||||
"dictsort",
|
||||
"Docsify",
|
||||
"dogfood",
|
||||
"dropdowns",
|
||||
"easings",
|
||||
"ecommerce",
|
||||
"eleventy",
|
||||
"elif",
|
||||
"endfor",
|
||||
"endmarkdown",
|
||||
"endraw",
|
||||
"endregion",
|
||||
"endset",
|
||||
"enterkeyhint",
|
||||
"eqeqeq",
|
||||
"erroneou",
|
||||
@@ -60,6 +68,7 @@
|
||||
"esbuild",
|
||||
"exportmaps",
|
||||
"exportparts",
|
||||
"fetchpriority",
|
||||
"fieldsets",
|
||||
"focusin",
|
||||
"focusout",
|
||||
@@ -78,6 +87,7 @@
|
||||
"giga",
|
||||
"globby",
|
||||
"Grayscale",
|
||||
"groupby",
|
||||
"haspopup",
|
||||
"heroicons",
|
||||
"hexa",
|
||||
@@ -88,6 +98,7 @@
|
||||
"inputmode",
|
||||
"ionicon",
|
||||
"ionicons",
|
||||
"jank",
|
||||
"jsDelivr",
|
||||
"jsfiddle",
|
||||
"keydown",
|
||||
@@ -123,8 +134,12 @@
|
||||
"noindex",
|
||||
"noopener",
|
||||
"noreferrer",
|
||||
"noscript",
|
||||
"Notdog",
|
||||
"novalidate",
|
||||
"nowrap",
|
||||
"Numberish",
|
||||
"nums",
|
||||
"oklab",
|
||||
"oklch",
|
||||
"onscrollend",
|
||||
@@ -139,6 +154,7 @@
|
||||
"progressbar",
|
||||
"radiogroup",
|
||||
"Railsbyte",
|
||||
"referrerpolicy",
|
||||
"remixicon",
|
||||
"reregister",
|
||||
"resizer",
|
||||
@@ -157,6 +173,7 @@
|
||||
"scroller",
|
||||
"Scrollers",
|
||||
"Segoe",
|
||||
"selectattr",
|
||||
"semibold",
|
||||
"shadowrootmode",
|
||||
"Shortcode",
|
||||
@@ -165,6 +182,7 @@
|
||||
"slotchange",
|
||||
"smartquotes",
|
||||
"spacebar",
|
||||
"srcdoc",
|
||||
"stylesheet",
|
||||
"svgs",
|
||||
"Tabbable",
|
||||
@@ -189,6 +207,7 @@
|
||||
"typeof",
|
||||
"unbundles",
|
||||
"unbundling",
|
||||
"Uncategorized",
|
||||
"unicons",
|
||||
"unsanitized",
|
||||
"unsupportive",
|
||||
|
||||
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",
|
||||
@@ -589,6 +589,10 @@
|
||||
"node": ">=12.17"
|
||||
}
|
||||
},
|
||||
"node_modules/@awesome.me/webawesome": {
|
||||
"resolved": "packages/webawesome",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@babel/code-frame": {
|
||||
"version": "7.23.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz",
|
||||
@@ -2025,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"
|
||||
}
|
||||
@@ -2484,10 +2487,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@shoelace-style/localize/-/localize-3.2.1.tgz",
|
||||
"integrity": "sha512-r4C9C/5kSfMBIr0D9imvpRdCNXtUNgyYThc4YlS6K5Hchv1UyxNQ9mxwj+BTRH2i1Neits260sR3OjKMnplsFA=="
|
||||
},
|
||||
"node_modules/@shoelace-style/webawesome": {
|
||||
"resolved": "packages/webawesome",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@shoelace-style/webawesome-pro": {
|
||||
"resolved": "packages/webawesome-pro",
|
||||
"link": true
|
||||
@@ -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",
|
||||
@@ -13974,42 +13976,83 @@
|
||||
}
|
||||
},
|
||||
"packages/webawesome": {
|
||||
"name": "@shoelace-style/webawesome",
|
||||
"version": "3.0.0-alpha.13",
|
||||
"name": "@awesome.me/webawesome",
|
||||
"version": "3.0.0-beta.4",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@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"
|
||||
},
|
||||
"devDependencies": {},
|
||||
"devDependencies": {
|
||||
"@wc-toolkit/jsx-types": "^1.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.17.0"
|
||||
}
|
||||
},
|
||||
"packages/webawesome-pro": {
|
||||
"name": "@shoelace-style/webawesome-pro",
|
||||
"version": "3.0.0-alpha.13",
|
||||
"license": "TODO",
|
||||
"version": "3.0.0-beta.3",
|
||||
"dependencies": {
|
||||
"@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"
|
||||
},
|
||||
"devDependencies": {},
|
||||
"devDependencies": {
|
||||
"@wc-toolkit/jsx-types": "^1.3.0"
|
||||
},
|
||||
"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",
|
||||
"integrity": "sha512-Ir/+ZpE9fDsNH0hQ3C68uyThDXzYcim2EqcZ8zn8Chtt1iylPT9xXJB0kPCnqzgcEGikO9RxSrh63MsmVCU7Fw==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"bin": {
|
||||
"nanoid": "bin/nanoid.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18 || >=20"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 @@
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
4
packages/webawesome/.gitignore
vendored
Normal file
4
packages/webawesome/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
_site
|
||||
dist
|
||||
dist-cdn
|
||||
src/react
|
||||
1
packages/webawesome/README.md
Normal file
1
packages/webawesome/README.md
Normal file
@@ -0,0 +1 @@
|
||||
Visit our documentation! <https://webawesome.com>
|
||||
@@ -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,66 +1,132 @@
|
||||
import { parse as HTMLParse } from 'node-html-parser';
|
||||
import * as fs from 'node:fs';
|
||||
import * as path from 'node:path';
|
||||
import { anchorHeadingsPlugin } from './_utils/anchor-headings.js';
|
||||
import { codeExamplesPlugin } from './_utils/code-examples.js';
|
||||
import { copyCodePlugin } from './_utils/copy-code.js';
|
||||
import { currentLink } from './_utils/current-link.js';
|
||||
import { highlightCodePlugin } from './_utils/highlight-code.js';
|
||||
import { anchorHeadingsTransformer } from './_transformers/anchor-headings.js';
|
||||
import { codeExamplesTransformer } from './_transformers/code-examples.js';
|
||||
import { copyCodeTransformer } from './_transformers/copy-code.js';
|
||||
import { currentLinkTransformer } from './_transformers/current-link.js';
|
||||
import { highlightCodeTransformer } from './_transformers/highlight-code.js';
|
||||
import { outlineTransformer } from './_transformers/outline.js';
|
||||
import { getComponents } from './_utils/manifest.js';
|
||||
import { markdown } from './_utils/markdown.js';
|
||||
// import { formatCodePlugin } from './_utils/format-code.js';
|
||||
import { SimulateWebAwesomeApp } from './_utils/simulate-webawesome-app.js';
|
||||
// import { formatCodePlugin } from './_plugins/format-code.js';
|
||||
// import litPlugin from '@lit-labs/eleventy-plugin-lit';
|
||||
import { readFile } from 'fs/promises';
|
||||
import nunjucks from 'nunjucks';
|
||||
// import componentList from './_data/componentList.js';
|
||||
import * as filters from './_utils/filters.js';
|
||||
import { outlinePlugin } from './_utils/outline.js';
|
||||
import { replaceTextPlugin } from './_utils/replace-text.js';
|
||||
import { searchPlugin } from './_utils/search.js';
|
||||
|
||||
import process from 'process';
|
||||
|
||||
import * as url from 'url';
|
||||
import { replaceTextPlugin } from './_plugins/replace-text.js';
|
||||
import { searchPlugin } from './_plugins/search.js';
|
||||
const __dirname = url.fileURLToPath(new URL('.', import.meta.url));
|
||||
|
||||
const packageData = JSON.parse(await readFile(path.join(__dirname, '..', 'package.json'), 'utf-8'));
|
||||
const isDev = process.argv.includes('--develop');
|
||||
const passThroughExtensions = ['js', 'css', 'png', 'svg', 'jpg', 'mp4'];
|
||||
|
||||
const globalData = {
|
||||
package: packageData,
|
||||
layout: 'page.njk',
|
||||
server: {
|
||||
head: '',
|
||||
loginOrAvatar: '',
|
||||
flashes: '',
|
||||
},
|
||||
};
|
||||
async function getPackageData() {
|
||||
return JSON.parse(await readFile(path.join(__dirname, '..', 'package.json'), 'utf-8'));
|
||||
}
|
||||
|
||||
export default async function (eleventyConfig) {
|
||||
/**
|
||||
* If you plan to add or remove any of these extensions, make sure to let either Konnor or Cory know as these passthrough extensions
|
||||
* will also need to be updated in the Web Awesome App.
|
||||
*/
|
||||
const passThroughExtensions = ['js', 'css', 'png', 'svg', 'jpg', 'mp4'];
|
||||
|
||||
const docsDir = path.join(process.env.BASE_DIR || '.', 'docs');
|
||||
let packageData = await getPackageData();
|
||||
let allComponents = getComponents();
|
||||
|
||||
const distDir = process.env.UNBUNDLED_DIST_DIRECTORY || path.resolve(__dirname, '../dist');
|
||||
const customElementsManifest = path.join(distDir, 'custom-elements.json');
|
||||
const stylesheets = path.join(distDir, 'styles');
|
||||
|
||||
eleventyConfig.addWatchTarget(customElementsManifest);
|
||||
eleventyConfig.setWatchThrottleWaitTime(10); // in milliseconds
|
||||
|
||||
eleventyConfig.on('eleventy.beforeWatch', async function (changedFiles) {
|
||||
let updatePackageData = false;
|
||||
let updateComponentData = false;
|
||||
changedFiles.forEach(file => {
|
||||
if (file.includes('package.json')) {
|
||||
updatePackageData = true;
|
||||
}
|
||||
|
||||
if (file.includes('custom-elements.json')) {
|
||||
updateComponentData = true;
|
||||
}
|
||||
});
|
||||
|
||||
if (updatePackageData) {
|
||||
packageData = await getPackageData();
|
||||
}
|
||||
|
||||
if (updateComponentData) {
|
||||
allComponents = getComponents();
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* If you plan to add or remove any of these extensions, make sure to let either Konnor or Cory know as these
|
||||
* passthrough extensions will also need to be updated in the Web Awesome App.
|
||||
*/
|
||||
const passThrough = [...passThroughExtensions.map(ext => path.join(docsDir, '**/*.' + ext))];
|
||||
|
||||
/**
|
||||
* This is the guard we use for now to make sure our final built files dont need a 2nd pass by the server. This keeps us able to still deploy the bare HTML files on Vercel until the app is ready.
|
||||
* This is the guard we use for now to make sure our final built files don't need a 2nd pass by the server. This keeps
|
||||
* us able to still deploy the bare HTML files on Vercel until the app is ready.
|
||||
*/
|
||||
const serverBuild = process.env.WEBAWESOME_SERVER === 'true';
|
||||
|
||||
// Add template data
|
||||
for (let name in globalData) {
|
||||
eleventyConfig.addGlobalData(name, globalData[name]);
|
||||
}
|
||||
//
|
||||
// Set all global template data here
|
||||
//
|
||||
eleventyConfig.addGlobalData('package', packageData);
|
||||
eleventyConfig.addGlobalData('layout', 'page.njk');
|
||||
eleventyConfig.addGlobalData('server', {
|
||||
head: '',
|
||||
loginOrAvatar: '',
|
||||
flashes: '',
|
||||
});
|
||||
|
||||
// Template filters - {{ content | filter }}
|
||||
eleventyConfig.addFilter('inlineMarkdown', content => markdown.renderInline(content || ''));
|
||||
eleventyConfig.addFilter('markdown', content => markdown.render(content || ''));
|
||||
eleventyConfig.addFilter('stripExtension', string => path.parse(string + '').name);
|
||||
eleventyConfig.addFilter('stripPrefix', content => content.replace(/^wa-/, ''));
|
||||
// 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;
|
||||
});
|
||||
|
||||
for (let name in filters) {
|
||||
eleventyConfig.addFilter(name, filters[name]);
|
||||
}
|
||||
// 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) {
|
||||
const items = [...collection]; // Create a copy to avoid mutating original
|
||||
return items.sort((a, b) => {
|
||||
const aValue = property ? a[property] : a;
|
||||
const bValue = property ? b[property] : b;
|
||||
if (aValue === firstValue) return -1;
|
||||
if (bValue === firstValue) return 1;
|
||||
return 0;
|
||||
});
|
||||
});
|
||||
|
||||
//
|
||||
// Add the componentPages collection
|
||||
//
|
||||
eleventyConfig.addCollection('componentPages', function (collectionApi) {
|
||||
const componentPages = collectionApi.getFilteredByGlob(
|
||||
path.join(eleventyConfig.directories.input, 'docs/components/**/*.md'),
|
||||
);
|
||||
|
||||
return componentPages.map(page => {
|
||||
const componentName = path.basename(page.inputPath, '.md');
|
||||
const tagName = `wa-${componentName}`;
|
||||
const component = allComponents.find(c => c.tagName === tagName);
|
||||
|
||||
// Add component to the page's data
|
||||
if (component) {
|
||||
page.data.component = component;
|
||||
}
|
||||
|
||||
return page;
|
||||
});
|
||||
});
|
||||
|
||||
// Shortcodes - {% shortCode arg1, arg2 %}
|
||||
eleventyConfig.addShortcode('cdnUrl', location => {
|
||||
@@ -76,64 +142,51 @@ export default async function (eleventyConfig) {
|
||||
return '';
|
||||
});
|
||||
|
||||
eleventyConfig.addTransform('second-nunjucks-transform', function NunjucksTransform(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;
|
||||
}
|
||||
|
||||
/** This largely mimics what an app would do and just stubs out what we don't care about. */
|
||||
return nunjucks.renderString(content, {
|
||||
// Stub the server EJS shortcodes.
|
||||
server: {
|
||||
head: '',
|
||||
loginOrAvatar: '',
|
||||
flashes: '',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
// Paired shortcodes - {% shortCode %}content{% endShortCode %}
|
||||
eleventyConfig.addPairedShortcode('markdown', content => markdown.render(content || ''));
|
||||
|
||||
// Helpers
|
||||
eleventyConfig.addNunjucksGlobal('getComponent', tagName => {
|
||||
const component = allComponents.find(c => c.tagName === tagName);
|
||||
if (!component) {
|
||||
throw new Error(
|
||||
`Unable to find "<${tagName}>". Make sure the file name is the same as the tag name (without prefix).`,
|
||||
);
|
||||
}
|
||||
return component;
|
||||
});
|
||||
|
||||
// Use our own markdown instance
|
||||
eleventyConfig.setLibrary('md', markdown);
|
||||
|
||||
// Add anchors to headings
|
||||
eleventyConfig.addPlugin(anchorHeadingsPlugin({ container: '#content' }));
|
||||
eleventyConfig.addTransform('doc-transforms', function (content) {
|
||||
let doc = HTMLParse(content, { blockTextElements: { code: true } });
|
||||
|
||||
// Add an outline to the page
|
||||
eleventyConfig.addPlugin(
|
||||
outlinePlugin({
|
||||
container: '#content',
|
||||
target: '.outline-links',
|
||||
selector: 'h2, h3',
|
||||
ifEmpty: doc => {
|
||||
doc.querySelector('#outline')?.remove();
|
||||
},
|
||||
}),
|
||||
);
|
||||
const transformers = [
|
||||
anchorHeadingsTransformer({ container: '#content' }),
|
||||
outlineTransformer({
|
||||
container: '#content',
|
||||
target: '.outline-links',
|
||||
selector: 'h2, h3',
|
||||
ifEmpty: doc => {
|
||||
doc.querySelector('#outline')?.remove();
|
||||
},
|
||||
}),
|
||||
// Add current link classes
|
||||
currentLinkTransformer(),
|
||||
codeExamplesTransformer(),
|
||||
highlightCodeTransformer(),
|
||||
copyCodeTransformer(),
|
||||
];
|
||||
|
||||
// Add current link classes
|
||||
eleventyConfig.addPlugin(currentLink());
|
||||
for (const transformer of transformers) {
|
||||
transformer.call(this, doc);
|
||||
}
|
||||
|
||||
// Add code examples for `<code class="example">` blocks
|
||||
eleventyConfig.addPlugin(codeExamplesPlugin());
|
||||
return doc.toString();
|
||||
});
|
||||
|
||||
// Highlight code blocks with Prism
|
||||
eleventyConfig.addPlugin(highlightCodePlugin());
|
||||
|
||||
// Add copy code buttons to code blocks
|
||||
eleventyConfig.addPlugin(copyCodePlugin);
|
||||
|
||||
// Various text replacements
|
||||
eleventyConfig.addPlugin(
|
||||
replaceTextPlugin([
|
||||
{
|
||||
@@ -143,17 +196,17 @@ export default async function (eleventyConfig) {
|
||||
// Replace [issue:1234] with a link to the issue on GitHub
|
||||
{
|
||||
replace: /\[pr:([0-9]+)\]/gs,
|
||||
replaceWith: '<a href="https://github.com/shoelace-style/webawesome/pull/$1">#$1</a>',
|
||||
replaceWith: '<a href="https://github.com/shoelace-style/webawesome/pull/$1" target="_blank">#$1</a>',
|
||||
},
|
||||
// Replace [pr:1234] with a link to the pull request on GitHub
|
||||
{
|
||||
replace: /\[issue:([0-9]+)\]/gs,
|
||||
replaceWith: '<a href="https://github.com/shoelace-style/webawesome/issues/$1">#$1</a>',
|
||||
replaceWith: '<a href="https://github.com/shoelace-style/webawesome/issues/$1" target="_blank">#$1</a>',
|
||||
},
|
||||
// Replace [discuss:1234] with a link to the discussion on GitHub
|
||||
{
|
||||
replace: /\[discuss:([0-9]+)\]/gs,
|
||||
replaceWith: '<a href="https://github.com/shoelace-style/webawesome/discussions/$1">#$1</a>',
|
||||
replaceWith: '<a href="https://github.com/shoelace-style/webawesome/discussions/$1" target="_blank">#$1</a>',
|
||||
},
|
||||
]),
|
||||
);
|
||||
@@ -176,15 +229,18 @@ export default async function (eleventyConfig) {
|
||||
// eleventyConfig.addPlugin(formatCodePlugin());
|
||||
// }
|
||||
|
||||
let assetsDir = path.join(process.env.BASE_DIR || 'docs', 'assets');
|
||||
fs.cpSync(assetsDir, path.join(eleventyConfig.directories.output, 'assets'), { 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);
|
||||
}
|
||||
|
||||
// // SSR plugin
|
||||
// // Make sure this is the last thing, we don't want to run the risk of accidentally transforming shadow roots with the nunjucks 2nd transform.
|
||||
// if (!isDev) {
|
||||
// //
|
||||
// // Problematic components in SSR land:
|
||||
@@ -207,6 +263,20 @@ export default async function (eleventyConfig) {
|
||||
// componentModules,
|
||||
// });
|
||||
// }
|
||||
|
||||
// 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) {
|
||||
// Only run the transform on files nunjucks would transform.
|
||||
if (!this.page.inputPath.match(/.(md|html|njk)$/)) {
|
||||
return content;
|
||||
}
|
||||
|
||||
/** This largely mimics what an app would do and just stubs out what we don't care about. */
|
||||
return SimulateWebAwesomeApp(content);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export const config = {
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
/**
|
||||
* @module components Fetches components from custom-elements.json and exposes them in a saner format.
|
||||
*/
|
||||
import { readFileSync } from 'fs';
|
||||
import { dirname, join, resolve } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const customElementsJSON = process.env.DIST_DIR
|
||||
? join(process.env.DIST_DIR, 'custom-elements.json')
|
||||
: resolve(__dirname, '../../dist/custom-elements.json');
|
||||
|
||||
const manifest = JSON.parse(readFileSync(customElementsJSON), 'utf-8');
|
||||
|
||||
const components = manifest.modules.flatMap(module => {
|
||||
return module.declarations
|
||||
.filter(c => c?.customElement)
|
||||
.map(declaration => {
|
||||
// Generate the dist path based on the src path and attach it to the component
|
||||
declaration.path = module.path.replace(/^src\//, 'dist/').replace(/\.ts$/, '.js');
|
||||
|
||||
// Remove private members and those that lack a description
|
||||
const members = declaration.members?.filter(member => member.description && member.privacy !== 'private');
|
||||
|
||||
const methods = members?.filter(prop => prop.kind === 'method' && prop.privacy !== 'private');
|
||||
const attributes = declaration.attributes ?? [];
|
||||
const properties = members?.filter(prop => {
|
||||
// Look for a corresponding attribute
|
||||
const attribute = attributes?.find(attr => attr.fieldName === prop.name);
|
||||
if (attribute) {
|
||||
prop.attribute = attribute.name || attribute.fieldName;
|
||||
}
|
||||
|
||||
return prop.kind === 'field' && prop.privacy !== 'private';
|
||||
});
|
||||
|
||||
return {
|
||||
...declaration,
|
||||
slug: declaration.tagName.replace(/^wa-/, ''),
|
||||
methods,
|
||||
attributes,
|
||||
properties,
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
// Build dependency graphs
|
||||
components.forEach(component => {
|
||||
const dependencies = [];
|
||||
|
||||
// Recursively fetch sub-dependencies
|
||||
function getDependencies(tag) {
|
||||
const cmp = components.find(c => c.tagName === tag);
|
||||
if (!cmp || !Array.isArray(component.dependencies)) {
|
||||
return;
|
||||
}
|
||||
|
||||
cmp.dependencies?.forEach(dependentTag => {
|
||||
if (!dependencies.includes(dependentTag)) {
|
||||
dependencies.push(dependentTag);
|
||||
}
|
||||
getDependencies(dependentTag);
|
||||
});
|
||||
}
|
||||
|
||||
getDependencies(component.tagName);
|
||||
|
||||
component.dependencies = dependencies.sort();
|
||||
});
|
||||
|
||||
// Sort by name
|
||||
components.sort((a, b) => {
|
||||
if (a.name < b.name) return -1;
|
||||
if (a.name > b.name) return 1;
|
||||
return 0;
|
||||
});
|
||||
|
||||
export default components;
|
||||
@@ -1,3 +0,0 @@
|
||||
import componentList from './componentList.js';
|
||||
|
||||
export default Object.fromEntries(componentList.map(component => [component.slug, component]));
|
||||
@@ -1 +0,0 @@
|
||||
export { hueRanges as default } from '../assets/data/index.js';
|
||||
@@ -1 +0,0 @@
|
||||
["red", "orange", "yellow", "green", "cyan", "blue", "indigo", "purple", "pink", "gray"]
|
||||
@@ -1 +0,0 @@
|
||||
export { default as default } from '../../src/styles/color/scripts/palettes-analyzed.js';
|
||||
738
packages/webawesome/docs/_data/themer.js
Normal file
738
packages/webawesome/docs/_data/themer.js
Normal file
@@ -0,0 +1,738 @@
|
||||
/**
|
||||
* All themes used in the themer.
|
||||
*/
|
||||
export const themes = [
|
||||
{
|
||||
//
|
||||
// #region Default
|
||||
//
|
||||
name: 'Default',
|
||||
description: 'Your trusty companion, like a perfectly broken-in pair of jeans.',
|
||||
filename: 'default.css',
|
||||
isPro: false,
|
||||
fonts: {
|
||||
body: {
|
||||
name: 'OS Default',
|
||||
css: 'ui-sans-serif, system-ui, sans-serif',
|
||||
href: null,
|
||||
},
|
||||
heading: {
|
||||
name: 'OS Default',
|
||||
css: 'ui-sans-serif, system-ui, sans-serif',
|
||||
href: null,
|
||||
},
|
||||
code: {
|
||||
name: 'OS Default',
|
||||
css: 'ui-monospace, monospace',
|
||||
href: null,
|
||||
},
|
||||
longform: {
|
||||
name: 'OS Default',
|
||||
css: 'ui-serif, serif',
|
||||
href: null,
|
||||
},
|
||||
},
|
||||
icons: {
|
||||
family: 'classic',
|
||||
weight: 1,
|
||||
},
|
||||
palette: {
|
||||
name: 'Default',
|
||||
filename: 'default.css',
|
||||
},
|
||||
colorBrand: {
|
||||
color: 'blue',
|
||||
},
|
||||
tokens: {
|
||||
// Fonts
|
||||
'--wa-font-family-body': 'ui-sans-serif, system-ui, sans-serif',
|
||||
'--wa-font-family-heading': 'var(--wa-font-family-body)',
|
||||
'--wa-font-family-code': 'ui-monospace, monospace',
|
||||
'--wa-font-family-longform': 'ui-serif, serif',
|
||||
'--wa-font-weight-body': 400,
|
||||
'--wa-font-weight-heading': 600,
|
||||
'--wa-font-weight-code': 400,
|
||||
'--wa-font-weight-longform': 400,
|
||||
|
||||
// Elements
|
||||
'--wa-border-radius-scale': 1,
|
||||
'--wa-space-scale': 1,
|
||||
'--wa-border-width-scale': 1,
|
||||
},
|
||||
},
|
||||
// #endregion
|
||||
|
||||
//
|
||||
// #region Awesome
|
||||
//
|
||||
{
|
||||
name: 'Awesome',
|
||||
description: 'Punchy and vibrant, the rock star of themes.',
|
||||
filename: 'awesome.css',
|
||||
isPro: false,
|
||||
fonts: {
|
||||
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',
|
||||
},
|
||||
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',
|
||||
},
|
||||
code: {
|
||||
name: 'OS Default',
|
||||
css: 'ui-monospace, monospace',
|
||||
href: null,
|
||||
},
|
||||
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',
|
||||
},
|
||||
},
|
||||
icons: {
|
||||
family: 'classic',
|
||||
weight: 2,
|
||||
},
|
||||
palette: {
|
||||
name: 'Bright',
|
||||
filename: 'bright.css',
|
||||
},
|
||||
colorBrand: {
|
||||
color: 'blue',
|
||||
},
|
||||
tokens: {
|
||||
// Fonts
|
||||
'--wa-font-family-body': 'Quicksand, sans-serif',
|
||||
'--wa-font-family-heading': 'var(--wa-font-family-body)',
|
||||
'--wa-font-family-code': 'ui-monospace, monospace',
|
||||
'--wa-font-family-longform': '"Crimson Pro", serif',
|
||||
'--wa-font-weight-body': 500,
|
||||
'--wa-font-weight-heading': 700,
|
||||
'--wa-font-weight-code': 500,
|
||||
'--wa-font-weight-longform': 500,
|
||||
|
||||
// Elements
|
||||
'--wa-border-radius-scale': 1.5,
|
||||
'--wa-space-scale': 1,
|
||||
'--wa-border-width-scale': 2,
|
||||
},
|
||||
},
|
||||
// #endregion
|
||||
|
||||
//
|
||||
// #region Shoelace
|
||||
//
|
||||
{
|
||||
name: 'Shoelace',
|
||||
description: 'The original, familiar look you know and love from Shoelace.',
|
||||
filename: 'shoelace.css',
|
||||
isPro: false,
|
||||
fonts: {
|
||||
body: {
|
||||
name: 'OS Default',
|
||||
css: 'ui-sans-serif, system-ui, sans-serif',
|
||||
href: null,
|
||||
},
|
||||
heading: {
|
||||
name: 'OS Default',
|
||||
css: 'ui-sans-serif, system-ui, sans-serif',
|
||||
href: null,
|
||||
},
|
||||
code: {
|
||||
name: 'OS Default',
|
||||
css: 'ui-monospace, monospace',
|
||||
href: null,
|
||||
},
|
||||
longform: {
|
||||
name: 'OS Default',
|
||||
css: 'ui-serif, serif',
|
||||
href: null,
|
||||
},
|
||||
},
|
||||
icons: {
|
||||
family: 'classic',
|
||||
weight: 1,
|
||||
},
|
||||
palette: {
|
||||
name: 'Shoelace',
|
||||
filename: 'shoelace.css',
|
||||
},
|
||||
colorBrand: {
|
||||
color: 'blue',
|
||||
},
|
||||
tokens: {
|
||||
// Fonts
|
||||
'--wa-font-family-body': 'ui-sans-serif, system-ui, sans-serif',
|
||||
'--wa-font-family-heading': 'var(--wa-font-family-body)',
|
||||
'--wa-font-family-code': 'ui-monospace, monospace',
|
||||
'--wa-font-family-longform': 'ui-serif, serif',
|
||||
'--wa-font-weight-body': 400,
|
||||
'--wa-font-weight-heading': 600,
|
||||
'--wa-font-weight-code': 400,
|
||||
'--wa-font-weight-longform': 400,
|
||||
|
||||
// Elements
|
||||
'--wa-border-radius-scale': 0.7,
|
||||
'--wa-space-scale': 1,
|
||||
'--wa-border-width-scale': 1,
|
||||
},
|
||||
},
|
||||
// #endregion
|
||||
|
||||
//
|
||||
// #region Active
|
||||
//
|
||||
{
|
||||
name: 'Active',
|
||||
description: 'Energetic and tactile, always in motion.',
|
||||
filename: 'active.css',
|
||||
isPro: true,
|
||||
fonts: {
|
||||
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',
|
||||
},
|
||||
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',
|
||||
},
|
||||
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',
|
||||
},
|
||||
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',
|
||||
},
|
||||
},
|
||||
icons: {
|
||||
family: 'classic',
|
||||
weight: 1,
|
||||
},
|
||||
palette: {
|
||||
name: 'Rudimentary',
|
||||
filename: 'rudimentary.css',
|
||||
},
|
||||
colorBrand: {
|
||||
color: 'green',
|
||||
},
|
||||
tokens: {
|
||||
// Fonts
|
||||
'--wa-font-family-body': 'Inter, sans-serif',
|
||||
'--wa-font-family-heading': 'var(--wa-font-family-body)',
|
||||
'--wa-font-family-code': '"Geist Mono", monospace',
|
||||
'--wa-font-family-longform': 'Aleo, serif',
|
||||
'--wa-font-weight-body': 400,
|
||||
'--wa-font-weight-heading': 650,
|
||||
'--wa-font-weight-code': 400,
|
||||
'--wa-font-weight-longform': 400,
|
||||
|
||||
// Elements
|
||||
'--wa-border-radius-scale': 1.75,
|
||||
'--wa-space-scale': 1,
|
||||
'--wa-border-width-scale': 1,
|
||||
},
|
||||
},
|
||||
// #endregion
|
||||
|
||||
//
|
||||
// #region Brutalist
|
||||
//
|
||||
{
|
||||
name: 'Brutalist',
|
||||
description: 'Sharp, square, and unapologetically bold.',
|
||||
filename: 'brutalist.css',
|
||||
isPro: true,
|
||||
fonts: {
|
||||
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',
|
||||
},
|
||||
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',
|
||||
},
|
||||
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',
|
||||
},
|
||||
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',
|
||||
},
|
||||
},
|
||||
icons: {
|
||||
family: 'classic',
|
||||
weight: 2,
|
||||
},
|
||||
palette: {
|
||||
name: 'Default',
|
||||
filename: 'default.css',
|
||||
},
|
||||
colorBrand: {
|
||||
color: 'blue',
|
||||
},
|
||||
tokens: {
|
||||
// Fonts
|
||||
'--wa-font-family-body': 'Space Grotesk, sans-serif',
|
||||
'--wa-font-family-heading': 'IBM Plex Sans Condensed, sans-serif',
|
||||
'--wa-font-family-code': 'Space Mono, monospace',
|
||||
'--wa-font-family-longform': 'Podkova, serif',
|
||||
'--wa-font-weight-body': 400,
|
||||
'--wa-font-weight-heading': 500,
|
||||
'--wa-font-weight-code': 400,
|
||||
'--wa-font-weight-longform': 400,
|
||||
|
||||
// Elements
|
||||
'--wa-border-radius-scale': 0,
|
||||
'--wa-space-scale': 1.125,
|
||||
'--wa-border-width-scale': 2,
|
||||
},
|
||||
},
|
||||
// #endregion
|
||||
|
||||
//
|
||||
// #region Glossy
|
||||
//
|
||||
{
|
||||
name: 'Glossy',
|
||||
description: 'Bustling with plenty of luster and shine.',
|
||||
filename: 'glossy.css',
|
||||
isPro: true,
|
||||
fonts: {
|
||||
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',
|
||||
},
|
||||
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',
|
||||
},
|
||||
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',
|
||||
},
|
||||
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',
|
||||
},
|
||||
},
|
||||
icons: {
|
||||
family: 'classic',
|
||||
weight: 1,
|
||||
},
|
||||
palette: {
|
||||
name: 'Elegant',
|
||||
filename: 'elegant.css',
|
||||
},
|
||||
colorBrand: {
|
||||
color: 'indigo',
|
||||
},
|
||||
tokens: {
|
||||
// Fonts
|
||||
'--wa-font-family-body': 'Figtree, sans-serif',
|
||||
'--wa-font-family-heading': 'var(--wa-font-family-body)',
|
||||
'--wa-font-family-code': '"Chivo Mono", monospace',
|
||||
'--wa-font-family-longform': 'Fraunces, serif',
|
||||
'--wa-font-weight-body': 400,
|
||||
'--wa-font-weight-heading': 800,
|
||||
'--wa-font-weight-code': 400,
|
||||
'--wa-font-weight-longform': 350,
|
||||
|
||||
// Elements
|
||||
'--wa-border-radius-scale': 1.33,
|
||||
'--wa-space-scale': 1.125,
|
||||
'--wa-border-width-scale': 1,
|
||||
},
|
||||
},
|
||||
// #endregion
|
||||
|
||||
//
|
||||
// #region Matter
|
||||
//
|
||||
{
|
||||
name: 'Matter',
|
||||
description: 'Digital design inspired by the real world.',
|
||||
filename: 'matter.css',
|
||||
isPro: true,
|
||||
fonts: {
|
||||
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',
|
||||
},
|
||||
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',
|
||||
},
|
||||
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',
|
||||
},
|
||||
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',
|
||||
},
|
||||
},
|
||||
icons: {
|
||||
family: 'classic',
|
||||
weight: 1,
|
||||
},
|
||||
palette: {
|
||||
name: 'Mild',
|
||||
filename: 'mild.css',
|
||||
},
|
||||
colorBrand: {
|
||||
color: 'purple',
|
||||
},
|
||||
tokens: {
|
||||
// Fonts
|
||||
'--wa-font-family-body': 'Wix Madefor Text, sans-serif',
|
||||
'--wa-font-family-heading': 'var(--wa-font-family-body)',
|
||||
'--wa-font-family-code': 'Roboto Mono, monospace',
|
||||
'--wa-font-family-longform': 'Roboto Serif, serif',
|
||||
'--wa-font-weight-body': 400,
|
||||
'--wa-font-weight-heading': 500,
|
||||
'--wa-font-weight-code': 400,
|
||||
'--wa-font-weight-longform': 400,
|
||||
|
||||
// Elements
|
||||
'--wa-border-radius-scale': 1.33,
|
||||
'--wa-space-scale': 1,
|
||||
'--wa-border-width-scale': 1,
|
||||
},
|
||||
},
|
||||
// #endregion
|
||||
|
||||
//
|
||||
// #region Mellow
|
||||
//
|
||||
{
|
||||
name: 'Mellow',
|
||||
description: 'Soft and soothing, like a lazy Sunday morning.',
|
||||
filename: 'mellow.css',
|
||||
isPro: true,
|
||||
fonts: {
|
||||
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',
|
||||
},
|
||||
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',
|
||||
},
|
||||
code: {
|
||||
name: 'OS Default',
|
||||
css: 'ui-monospace, monospace',
|
||||
href: null,
|
||||
},
|
||||
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',
|
||||
},
|
||||
},
|
||||
icons: {
|
||||
family: 'classic',
|
||||
weight: 1.5,
|
||||
},
|
||||
palette: {
|
||||
name: 'Natural',
|
||||
filename: 'natural.css',
|
||||
},
|
||||
colorBrand: {
|
||||
color: 'blue',
|
||||
},
|
||||
tokens: {
|
||||
// Fonts
|
||||
'--wa-font-family-body': 'Mulish, sans-serif',
|
||||
'--wa-font-family-heading': 'Lora, serif',
|
||||
'--wa-font-family-code': 'ui-monospace, monospace',
|
||||
'--wa-font-family-longform': 'Lora, serif',
|
||||
'--wa-font-weight-body': 400,
|
||||
'--wa-font-weight-heading': 700,
|
||||
'--wa-font-weight-code': 400,
|
||||
'--wa-font-weight-longform': 400,
|
||||
|
||||
// Elements
|
||||
'--wa-border-radius-scale': 1,
|
||||
'--wa-space-scale': 1.125,
|
||||
'--wa-border-width-scale': 1.5,
|
||||
},
|
||||
},
|
||||
// #endregion
|
||||
|
||||
//
|
||||
// #region Playful
|
||||
//
|
||||
{
|
||||
name: 'Playful',
|
||||
description: 'Cheerful and engaging, like a playground on screen.',
|
||||
filename: 'playful.css',
|
||||
isPro: true,
|
||||
fonts: {
|
||||
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',
|
||||
},
|
||||
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',
|
||||
},
|
||||
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',
|
||||
},
|
||||
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',
|
||||
},
|
||||
},
|
||||
icons: {
|
||||
family: 'classic',
|
||||
weight: 3,
|
||||
},
|
||||
palette: {
|
||||
name: 'Rudimentary',
|
||||
filename: 'rudimentary.css',
|
||||
},
|
||||
colorBrand: {
|
||||
color: 'purple',
|
||||
},
|
||||
tokens: {
|
||||
// Fonts
|
||||
'--wa-font-family-body': 'Nunito, sans-serif',
|
||||
'--wa-font-family-heading': 'Fredoka, sans-serif',
|
||||
'--wa-font-family-code': 'Azeret Mono, monospace',
|
||||
'--wa-font-family-longform': 'BioRhyme, serif',
|
||||
'--wa-font-weight-body': 500,
|
||||
'--wa-font-weight-heading': 600,
|
||||
'--wa-font-weight-code': 400,
|
||||
'--wa-font-weight-longform': 400,
|
||||
|
||||
// Elements
|
||||
'--wa-border-radius-scale': 2,
|
||||
'--wa-space-scale': 1,
|
||||
'--wa-border-width-scale': 3,
|
||||
},
|
||||
},
|
||||
// #endregion
|
||||
|
||||
//
|
||||
// #region Premium
|
||||
//
|
||||
{
|
||||
name: 'Premium',
|
||||
description: 'The ultimate in sophistication and style.',
|
||||
filename: 'premium.css',
|
||||
isPro: true,
|
||||
fonts: {
|
||||
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',
|
||||
},
|
||||
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',
|
||||
},
|
||||
code: {
|
||||
name: 'OS Default',
|
||||
css: 'ui-monospace, monospace',
|
||||
href: null,
|
||||
},
|
||||
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',
|
||||
},
|
||||
},
|
||||
icons: {
|
||||
family: 'classic',
|
||||
weight: 1.5,
|
||||
},
|
||||
palette: {
|
||||
name: 'Anodized',
|
||||
filename: 'anodized.css',
|
||||
},
|
||||
colorBrand: {
|
||||
color: 'cyan',
|
||||
},
|
||||
tokens: {
|
||||
// Fonts
|
||||
'--wa-font-family-body': 'DM Sans, sans-serif',
|
||||
'--wa-font-family-heading': 'Playfair Display, serif',
|
||||
'--wa-font-family-code': 'ui-monospace, monospace',
|
||||
'--wa-font-family-longform': 'Playfair, serif',
|
||||
'--wa-font-weight-body': 400,
|
||||
'--wa-font-weight-heading': 500,
|
||||
'--wa-font-weight-code': 400,
|
||||
'--wa-font-weight-longform': 400,
|
||||
|
||||
// Elements
|
||||
'--wa-border-radius-scale': 0.5,
|
||||
'--wa-space-scale': 1,
|
||||
'--wa-border-width-scale': 1.5,
|
||||
},
|
||||
},
|
||||
// #endregion
|
||||
|
||||
//
|
||||
// #region Tailspin
|
||||
//
|
||||
{
|
||||
name: 'Tailspin',
|
||||
description: 'Like a bird in flight, guiding you from there to here.',
|
||||
filename: 'tailspin.css',
|
||||
isPro: true,
|
||||
fonts: {
|
||||
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',
|
||||
},
|
||||
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',
|
||||
},
|
||||
code: {
|
||||
name: 'OS Default',
|
||||
css: 'ui-monospace, monospace',
|
||||
href: null,
|
||||
},
|
||||
longform: {
|
||||
name: 'OS Default',
|
||||
css: 'ui-serif, serif',
|
||||
href: null,
|
||||
},
|
||||
},
|
||||
icons: {
|
||||
family: 'classic',
|
||||
weight: 1,
|
||||
},
|
||||
palette: {
|
||||
name: 'Vogue',
|
||||
filename: 'vogue.css',
|
||||
},
|
||||
colorBrand: {
|
||||
color: 'indigo',
|
||||
},
|
||||
tokens: {
|
||||
// Fonts
|
||||
'--wa-font-family-body': 'Inter, sans-serif',
|
||||
'--wa-font-family-heading': 'var(--wa-font-family-body)',
|
||||
'--wa-font-family-code': 'ui-monospace, monospace',
|
||||
'--wa-font-family-longform': 'ui-serif, serif',
|
||||
'--wa-font-weight-body': 400,
|
||||
'--wa-font-weight-heading': 700,
|
||||
'--wa-font-weight-code': 400,
|
||||
'--wa-font-weight-longform': 400,
|
||||
|
||||
// Elements
|
||||
'--wa-border-radius-scale': 1,
|
||||
'--wa-space-scale': 0.875,
|
||||
'--wa-border-width-scale': 1,
|
||||
},
|
||||
},
|
||||
// #endregion
|
||||
];
|
||||
|
||||
/**
|
||||
* All fonts used by themes, collected from the four font categories.
|
||||
*/
|
||||
export const fonts = themes
|
||||
.flatMap(theme => [theme.fonts.body, theme.fonts.heading, theme.fonts.code, theme.fonts.longform])
|
||||
.filter(
|
||||
(font, index, array) =>
|
||||
array.findIndex(f => f.name === font.name && f.css === font.css && f.href === font.href) === index,
|
||||
);
|
||||
|
||||
/**
|
||||
* Font presets derived from themes, with unique font names in order: heading > body > code > longform
|
||||
*/
|
||||
export const fontPresets = themes
|
||||
.map(theme => {
|
||||
const fontNames = [
|
||||
theme.fonts.heading.name,
|
||||
theme.fonts.body.name,
|
||||
theme.fonts.code.name,
|
||||
theme.fonts.longform.name,
|
||||
];
|
||||
const uniqueFonts = fontNames.filter((name, index) => fontNames.indexOf(name) === index);
|
||||
|
||||
return {
|
||||
name: theme.name,
|
||||
displayName: uniqueFonts.join(' · '),
|
||||
fontFamilyBody: theme.fonts.body.css,
|
||||
fontFamilyHeading: theme.fonts.heading.css,
|
||||
fontFamilyCode: theme.fonts.code.css,
|
||||
fontFamilyLongform: theme.fonts.longform.css,
|
||||
fontWeightBody: theme.tokens['--wa-font-weight-body'],
|
||||
fontWeightHeading: theme.tokens['--wa-font-weight-heading'],
|
||||
fontWeightCode: theme.tokens['--wa-font-weight-code'],
|
||||
fontWeightLongform: theme.tokens['--wa-font-weight-longform'],
|
||||
};
|
||||
})
|
||||
.filter((preset, index, array) => array.findIndex(p => p.displayName === preset.displayName) === index);
|
||||
|
||||
/**
|
||||
* Element presets derived from themes.
|
||||
*/
|
||||
export const elementPresets = themes.map(theme => ({
|
||||
name: theme.name,
|
||||
borderRadiusScale: theme.tokens['--wa-border-radius-scale'],
|
||||
spaceScale: theme.tokens['--wa-space-scale'],
|
||||
borderWidthScale: theme.tokens['--wa-border-width-scale'],
|
||||
}));
|
||||
|
||||
/**
|
||||
* All palettes used by themes in a simple array.
|
||||
*/
|
||||
export const palettes = themes
|
||||
.map(theme => ({
|
||||
...theme.palette,
|
||||
isPro: theme.isPro,
|
||||
}))
|
||||
.filter(
|
||||
(palette, index, array) =>
|
||||
array.findIndex(p => p.name === palette.name && p.filename === palette.filename) === index,
|
||||
);
|
||||
|
||||
/**
|
||||
* Available icons.
|
||||
*/
|
||||
export const icons = [
|
||||
{ name: 'Classic', libraryName: 'classic' },
|
||||
{ name: 'Sharp', libraryName: 'sharp' },
|
||||
{ name: 'Duotone', libraryName: 'duotone' },
|
||||
{ name: 'Sharp Duotone', libraryName: 'sharp-duotone' },
|
||||
];
|
||||
|
||||
export const colors = ['red', 'orange', 'yellow', 'green', 'cyan', 'blue', 'indigo', 'purple', 'pink', 'gray'];
|
||||
export const tints = ['95', '90', '80', '70', '60', '50', '40', '30', '20', '10', '05'];
|
||||
@@ -1,62 +0,0 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
// import { inlined } from '../../dist/components/icon/library.wa.js';
|
||||
const distDirectory = process.env.UNBUNDLED_DIST_DIRECTORY || path.join(path.resolve(), 'dist');
|
||||
|
||||
const THEME_DIR = path.join(distDirectory, 'styles', 'themes');
|
||||
|
||||
const themeFiles = fs.readdirSync(THEME_DIR).filter(file => file.endsWith('.css') && !file.endsWith('base.css'));
|
||||
|
||||
const declarationRegex = /^\s*--wa-(?<property>[a-z-]+)?:\s*(?<value>.+?)\s*(\/\*.+?\*\/)?\s*;$/gm;
|
||||
const importRegex = /^\s*@import\s+url\(['"](?<path>.+?)['"]\);$/gm;
|
||||
const themes = {};
|
||||
|
||||
for (const file of themeFiles) {
|
||||
const id = file.replace('.css', '');
|
||||
const { imports, declarations } = readCSSFile(file);
|
||||
let theme = { palette: 'default', declarations, imports };
|
||||
|
||||
for (const url of imports) {
|
||||
if (url.endsWith('/color.css')) {
|
||||
// Color settings
|
||||
const color = readCSSFile(url);
|
||||
for (const colorUrl of color.imports) {
|
||||
if (colorUrl.startsWith('../../color/')) {
|
||||
// Color palette
|
||||
theme.palette = getFileSlug(colorUrl);
|
||||
} else if (colorUrl.startsWith('../../brand/')) {
|
||||
// Brand color
|
||||
theme.brand = getFileSlug(colorUrl);
|
||||
}
|
||||
}
|
||||
} else if (url.endsWith('/dimension.css')) {
|
||||
theme.dimension = true;
|
||||
}
|
||||
}
|
||||
|
||||
let icon = {};
|
||||
icon.family = theme.declarations['icon-family'] ?? theme.default?.iconFamily ?? 'classic';
|
||||
icon.variant = theme.declarations['icon-variant'] ?? theme.default?.iconVariant ?? 'solid';
|
||||
theme.icons = icon;
|
||||
|
||||
theme.rounding = Number(theme.declarations['border-radius-scale'] ?? theme.default?.rounding ?? 1);
|
||||
theme.spacing = Number(theme.declarations['space-scale'] ?? theme.default?.spacing ?? 1);
|
||||
theme.borderWidth = Number(theme.declarations['border-width-scale'] ?? theme.default?.borderWidth ?? 1);
|
||||
|
||||
themes[id] = theme;
|
||||
}
|
||||
|
||||
export default themes;
|
||||
|
||||
function readCSSFile(url) {
|
||||
const contents = fs.readFileSync(path.join(THEME_DIR, url), 'utf8');
|
||||
const imports = [...contents.matchAll(importRegex)].map(match => match.groups.path);
|
||||
const declarations = Object.fromEntries(
|
||||
[...contents.matchAll(declarationRegex)].map(match => [match.groups.property, match.groups.value]),
|
||||
);
|
||||
return { imports, declarations };
|
||||
}
|
||||
|
||||
function getFileSlug(url) {
|
||||
return url.split('/').pop().replace('.css', '');
|
||||
}
|
||||
@@ -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">
|
||||
@@ -8,19 +8,43 @@
|
||||
<script type="module" src="/assets/scripts/scroll.js"></script>
|
||||
<script type="module" src="/assets/scripts/turbo.js"></script>
|
||||
<script type="module" src="/assets/scripts/search.js"></script>
|
||||
<script type="module" src="/assets/scripts/search-list.js"></script>
|
||||
<script type="module" src="/assets/scripts/outline.js"></script>
|
||||
<script type="module" src="/assets/scripts/color-scheme.js"></script>
|
||||
<script type="module" src="/assets/scripts/theme.js"></script>
|
||||
{% if hasSidebar %}<script type="module" src="/assets/scripts/sidebar.js"></script>{% endif %}
|
||||
{% if hasSidebar %}<script type="module" src="/assets/scripts/sidebar-tweaks.js"></script>{% endif %}
|
||||
<script defer data-domain="backers.webawesome.com" src="https://plausible.io/js/script.js"></script>
|
||||
<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 %}
|
||||
|
||||
<script type="module">
|
||||
// Set the initial color scheme before the page renders to prevent flashing
|
||||
const value = localStorage.getItem('color-scheme') || 'auto';
|
||||
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="1140">
|
||||
<wa-page view="desktop" disable-navigation-toggle mobile-breakpoint="1180">
|
||||
<header slot="header" class="wa-split">
|
||||
{# Logo #}
|
||||
<div id="docs-branding">
|
||||
@@ -34,22 +58,30 @@
|
||||
<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="warning" appearance="filled" class="wa-desktop-only">Alpha</wa-badge>
|
||||
<wa-badge variant="brand" appearance="filled" class="wa-desktop-only">Beta</wa-badge>
|
||||
</div>
|
||||
|
||||
<div id="docs-toolbar" class="wa-cluster wa-gap-xs">
|
||||
<div id="docs-toolbar" class="wa-cluster">
|
||||
{# Desktop selectors #}
|
||||
<div class="wa-desktop-only wa-cluster wa-gap-xs">
|
||||
{% include "preset-theme-selector.njk" %}
|
||||
{% include "theme-selector.njk" %}
|
||||
{% include "color-scheme-selector.njk" %}
|
||||
</div>
|
||||
|
||||
{# Search #}
|
||||
<wa-button id="search-trigger" appearance="outlined" size="small" data-search>
|
||||
<wa-icon slot="prefix" name="magnifying-glass"></wa-icon>
|
||||
Search
|
||||
<kbd slot="suffix" class="wa-desktop-only">/</kbd>
|
||||
</wa-button>
|
||||
<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" %}
|
||||
@@ -60,8 +92,8 @@
|
||||
{% if hasSidebar %}
|
||||
{# Mobile selectors #}
|
||||
<div class="wa-mobile-only" slot="navigation-header">
|
||||
<div class="wa-cluster wa-gap-xs">
|
||||
{% include "preset-theme-selector.njk" %}
|
||||
<div class="wa-cluster wa-gap-xs" style="flex-wrap: nowrap;">
|
||||
{% include "theme-selector.njk" %}
|
||||
{% include "color-scheme-selector.njk" %}
|
||||
</div>
|
||||
</div>
|
||||
@@ -79,7 +111,6 @@
|
||||
</aside>
|
||||
{% endif %}
|
||||
|
||||
|
||||
{# Main #}
|
||||
<main id="content">
|
||||
{# Expandable outline #}
|
||||
@@ -94,10 +125,11 @@
|
||||
<div id="flashes">{% server "flashes" %}</div>
|
||||
|
||||
{% block header %}
|
||||
{% include 'breadcrumbs.njk' %}
|
||||
<h1 class="title">{{ title }}</h1>
|
||||
{% endblock %}
|
||||
|
||||
{% block beforeContent %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{{ content | safe }}
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
{% set ancestors = page.url | ancestors %}
|
||||
|
||||
{% if ancestors.length > 0 %}
|
||||
<wa-breadcrumb id="docs-breadcrumbs">
|
||||
{% for ancestor in ancestors %}
|
||||
{% if ancestor.page.url != "/" %}
|
||||
<wa-breadcrumb-item href="{{ ancestor.page.url }}">{{ ancestor.data.title }}</wa-breadcrumb-item>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
<wa-breadcrumb-item>{# Current page #}</wa-breadcrumb-item>
|
||||
</wa-breadcrumb>
|
||||
{% else %}
|
||||
{% endif %}
|
||||
@@ -1,19 +1,25 @@
|
||||
{# Color scheme selector #}
|
||||
<wa-select class="color-scheme-selector" appearance="filled" size="small" value="auto" pill title="Press \ to toggle">
|
||||
<wa-icon class="only-light" slot="prefix" name="sun" variant="regular"></wa-icon>
|
||||
<wa-icon class="only-dark" slot="prefix" name="moon" variant="regular"></wa-icon>
|
||||
<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="prefix" name="sun" variant="regular"></wa-icon>
|
||||
<wa-icon slot="start" name="sun" variant="regular"></wa-icon>
|
||||
Light
|
||||
</wa-option>
|
||||
<wa-option value="dark">
|
||||
<wa-icon slot="prefix" name="moon" variant="regular"></wa-icon>
|
||||
<wa-icon slot="start" name="moon" variant="regular"></wa-icon>
|
||||
Dark
|
||||
</wa-option>
|
||||
<wa-divider></wa-divider>
|
||||
<wa-option value="auto">
|
||||
<wa-icon class="only-light" slot="prefix" name="sun" variant="regular"></wa-icon>
|
||||
<wa-icon class="only-dark" slot="prefix" name="moon" variant="regular"></wa-icon>
|
||||
<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>
|
||||
System
|
||||
</wa-option>
|
||||
</wa-select>
|
||||
|
||||
<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');
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
<table class="colors wa-palette-{{ paletteId }} contrast-table" data-min-contrast="{{ minContrast }}">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
{% for tint_bg in tints -%}
|
||||
{% for tint_fg in tints | reverse -%}
|
||||
{% if (tint_fg - tint_bg) | abs == difference %}
|
||||
<th>{{ tint_fg }} on {{ tint_bg }}</th>
|
||||
{% endif %}
|
||||
{%- endfor -%}
|
||||
{%- endfor %}
|
||||
</tr>
|
||||
</thead>
|
||||
{% for hue in hues -%}
|
||||
<tr data-hue="{{ hue }}">
|
||||
<th>{{ hue | capitalize }}</th>
|
||||
{% for tint_bg in tints -%}
|
||||
{% set color_bg = palettes[paletteId][hue][tint_bg] %}
|
||||
{% for tint_fg in tints | reverse -%}
|
||||
{% set color_fg = palettes[paletteId][hue][tint_fg] %}
|
||||
{% if (tint_fg - tint_bg) | abs == difference %}
|
||||
{% set contrast_wcag = '' %}
|
||||
{% if color_fg and color_bg -%}
|
||||
{% set contrast_wcag = color_bg.contrast(color_fg, 'WCAG21') %}
|
||||
{%- endif %}
|
||||
<td v-for="contrast of [contrasts.{{ hue }}['{{ tint_bg }}']['{{ tint_fg }}']]"
|
||||
data-tint-bg="{{ tint_bg }}" data-tint-fg="{{ tint_fg }}" data-original-contrast="{{ contrast_wcag }}">
|
||||
<div v-content:number="contrast.value"
|
||||
class="color swatch" :class="{
|
||||
'value-up': contrast.value - contrast.original > 0.0001,
|
||||
'value-down': contrast.original - contrast.value > 0.0001,
|
||||
'contrast-fail': contrast.value < {{ minContrast }}
|
||||
}"
|
||||
style="--color: var(--wa-color-{{ hue }}-{{ tint_bg }}); color: var(--wa-color-{{ hue }}-{{ tint_fg }})"
|
||||
:style="{
|
||||
'--color': contrast.bgColor,
|
||||
color: contrast.fgColor,
|
||||
}"
|
||||
>
|
||||
{% if contrast_wcag %}
|
||||
{{ contrast_wcag | number({maximumSignificantDigits: 2}) }}
|
||||
{% else %}
|
||||
{{ tint_fg }} on {{ tint_bg }}
|
||||
{% endif %}
|
||||
</div>
|
||||
</td>
|
||||
{% endif %}
|
||||
{%- endfor -%}
|
||||
{%- endfor -%}
|
||||
</tr>
|
||||
{%- endfor %}
|
||||
</table>
|
||||
@@ -1,18 +0,0 @@
|
||||
{# Cards for pages listed by category #}
|
||||
|
||||
<section id="grid" class="index-grid">
|
||||
{% set groupedPages = allPages | groupPages(categories, page) %}
|
||||
{% for category, pages in groupedPages -%}
|
||||
{% if groupedPages.meta.groupCount > 1 and pages.length > 0 %}
|
||||
<h2 class="index-category" id="{{ category | slugify }}">
|
||||
{% if pages.meta.url %}<a href="{{ pages.meta.url }}">{{ pages.meta.title }}</a>
|
||||
{% else %}
|
||||
{{ pages.meta.title }}
|
||||
{% endif %}
|
||||
</h2>
|
||||
{% endif %}
|
||||
{%- for page in pages -%}
|
||||
{% include "page-card.njk" %}
|
||||
{%- endfor -%}
|
||||
{%- endfor -%}
|
||||
</section>
|
||||
@@ -17,51 +17,13 @@
|
||||
|
||||
<link rel="icon" href="/assets/images/webawesome-logo.svg" />
|
||||
<link rel="apple-touch-icon" href="/assets/images/app-icon.png">
|
||||
|
||||
{# Scripts #}
|
||||
{# Hydration stuff #}
|
||||
<script src="/assets/scripts/hydration-errors.js"></script>
|
||||
<link rel="stylesheet" href="/assets/styles/hydration-errors.css">
|
||||
<link rel="preconnect" href="https://cdn.jsdelivr.net">
|
||||
|
||||
{# Internal components #}
|
||||
<script type="module" src="/assets/components/scoped.js"></script>
|
||||
|
||||
{# Web Awesome #}
|
||||
<script type="module" src="/dist/webawesome.loader.js"></script>
|
||||
|
||||
{# Fallback loading when using the free repo #}
|
||||
<link rel="preconnect" href="https://early.webawesome.com">
|
||||
<script type="module">
|
||||
document.addEventListener("wa-discovery-complete", loadLayout)
|
||||
function loadLayout () {
|
||||
if (!customElements.get("wa-page")) {
|
||||
import("https://early.webawesome.com/webawesome@3.0.0-alpha.13/dist/components/page/page.js")
|
||||
.catch((e) => {
|
||||
console.error(e)
|
||||
// known errors with dual registration. This is only a thing in the free repo.
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<script type="module" src="/assets/scripts/theme-picker.js"></script>
|
||||
{# Preset Theme #}
|
||||
{% if noTheme %}
|
||||
{% elif forceTheme %}
|
||||
<link id="theme-stylesheet" rel="stylesheet" id="theme-stylesheet" href="/dist/styles/themes/{{ forceTheme }}.css" render="blocking" fetchpriority="high" />
|
||||
{% else %}
|
||||
<noscript><link id="theme-stylesheet" rel="stylesheet" id="theme-stylesheet" href="/dist/styles/themes/default.css" render="blocking" fetchpriority="high" /></noscript>
|
||||
<script>
|
||||
{
|
||||
let preset = localStorage.presetTheme ?? 'default';
|
||||
let script = document.currentScript;
|
||||
script.insertAdjacentHTML('beforebegin', `<link id="theme-stylesheet" rel="stylesheet" id="theme-stylesheet" href="/dist/styles/themes/${ preset }.css" render="blocking" fetchpriority="high" />`);
|
||||
}
|
||||
</script>
|
||||
<script type="module" src="/assets/scripts/preset-theme-picker.js"></script>
|
||||
{% endif %}
|
||||
|
||||
<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" />
|
||||
|
||||
{# Used by Web Awesome App to inject other assets into the head. #}
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
<wa-tab-group class="import-stylesheet-code">
|
||||
<wa-tab panel="html">In HTML</wa-tab>
|
||||
<wa-tab panel="css">In CSS</wa-tab>
|
||||
<wa-tab-panel name="html">
|
||||
|
||||
Add the following code to the `<head>` of your page:
|
||||
```html
|
||||
<link rel="stylesheet" href="{% cdnUrl stylesheet %}" />
|
||||
```
|
||||
</wa-tab-panel>
|
||||
<wa-tab-panel name="css">
|
||||
|
||||
Add the following code at the top of your CSS file:
|
||||
```css
|
||||
@import url('{% cdnUrl stylesheet %}');
|
||||
```
|
||||
</wa-tab-panel>
|
||||
</wa-tab-group>
|
||||
@@ -1,13 +0,0 @@
|
||||
{%- if not page.data.unlisted -%}
|
||||
{% if page.url %}<a href="{{ page.url }}"{{ page.data.keywords | attr('data-keywords') }}>{% endif %}
|
||||
<wa-card with-header>
|
||||
<div slot="header">
|
||||
{% include "svgs/" + (page.data.icon or "thumbnail-placeholder") + ".njk" ignore missing %}
|
||||
</div>
|
||||
<span class="page-name">{{ page.data.title }}</span>
|
||||
{% if pageSubtitle -%}
|
||||
<div class="wa-caption-s">{{ pageSubtitle }}</div>
|
||||
{%- endif %}
|
||||
</wa-card>
|
||||
{% if page.url %}</a>{% endif %}
|
||||
{% endif %}
|
||||
@@ -1,26 +0,0 @@
|
||||
<div id="page_slots_demo">
|
||||
<link rel="stylesheet" href="/docs/components/page/demo.css">
|
||||
{% set slots = components.page.slots %}
|
||||
|
||||
<fieldset id="page_slots_fieldset">
|
||||
<legend>Slots</legend>
|
||||
<div class="options">
|
||||
{% for slot in slots %}
|
||||
{% if (slot.name != "skip-to-content") and (slot.name != "navigation-toggle-icon") %}
|
||||
<wa-checkbox name="slot" value="{{ slot.name }}" {{ 'checked' if slot.name != "menu" and slot.name != 'navigation-toggle' | safe}} class="{{ 'default' if not slot.name }}"
|
||||
data-description="{{ slot.description | inlineMarkdown }}" title="{{ slot.description | inlineMarkdown | striptags | safe }}">
|
||||
{{ slot.name or "(default)" }}
|
||||
</wa-checkbox>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</fieldset>
|
||||
<wa-viewport-demo viewport="1000">
|
||||
<iframe srcdoc="" id="page_slots_iframe"></iframe>
|
||||
</wa-viewport-demo>
|
||||
</div>
|
||||
|
||||
<script type="module">
|
||||
const cacheBust = new Date().toString()
|
||||
import(`/docs/components/page/demo.js?${cacheBust}`)
|
||||
</script>
|
||||
@@ -1,9 +0,0 @@
|
||||
{# Preset theme selector #}
|
||||
<wa-select appearance="filled" size="small" value="default" pill class="preset-theme-selector">
|
||||
<wa-icon slot="prefix" name="paintbrush" variant="regular"></wa-icon>
|
||||
{% for theme in collections.theme | sort %}
|
||||
<wa-option value="{{ theme.page.fileSlug }}">
|
||||
{{ theme.data.title }}
|
||||
</wa-option>
|
||||
{% endfor %}
|
||||
</wa-select>
|
||||
@@ -24,7 +24,7 @@
|
||||
aria-haspopup="listbox"
|
||||
aria-activedescendant
|
||||
>
|
||||
<wa-icon slot="prefix" name="search"></wa-icon>
|
||||
<wa-icon slot="start" name="search"></wa-icon>
|
||||
</wa-input>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
{# 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>
|
||||
|
||||
{# Getting started #}
|
||||
<h2>Getting Started</h2>
|
||||
<ul>
|
||||
@@ -11,7 +18,8 @@
|
||||
{# Resources #}
|
||||
<h2>Resources</h2>
|
||||
<ul>
|
||||
<li><a href="https://github.com/shoelace-style/webawesome-alpha/discussions" target="_blank">Help & Support</a></li>
|
||||
<li><a href="https://github.com/shoelace-style/webawesome/discussions" target="_blank">Help & Support</a></li>
|
||||
<li><a href="https://github.com/shoelace-style/webawesome/">Source Code</a></li>
|
||||
<li><a href="/docs/resources/community">Community</a></li>
|
||||
<li><a href="/docs/resources/accessibility">Accessibility</a></li>
|
||||
<li><a href="/docs/resources/browser-support">Browser Support</a></li>
|
||||
@@ -20,58 +28,6 @@
|
||||
<li><a href="/docs/resources/visual-tests">Visual Tests</a></li>
|
||||
</ul>
|
||||
|
||||
<!-- Themes -->
|
||||
<wa-details appearance="outlined">
|
||||
<h2 slot="summary">
|
||||
<a href="/docs/themes/" title="Overview">
|
||||
Themes
|
||||
<wa-icon name="grid-2" aria-hidden="true"></wa-icon>
|
||||
</a>
|
||||
</h2>
|
||||
<ul>
|
||||
<li><a href="/docs/themes/default/">Default</a></li>
|
||||
<li><a href="/docs/themes/shoelace/">Shoelace</a></li>
|
||||
<li><a href="/docs/themes/awesome/">Awesome</a></li>
|
||||
<li>
|
||||
<a href="/docs/themes/active/">Active</a>
|
||||
<wa-badge class="pro" appearance="accent" attention="none">PRO</wa-badge>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/themes/brutalist/">Brutalist</a>
|
||||
<wa-badge class="pro" appearance="accent" attention="none">PRO</wa-badge>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/themes/glossy/">Glossy</a>
|
||||
<wa-badge class="pro" appearance="accent" attention="none">PRO</wa-badge>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/themes/matter/">Matter</a>
|
||||
<wa-badge class="pro" appearance="accent" attention="none">PRO</wa-badge>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/themes/mellow/">Mellow</a>
|
||||
<wa-badge class="pro" appearance="accent" attention="none">PRO</wa-badge>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/themes/playful/">Playful</a>
|
||||
<wa-badge class="pro" appearance="accent" attention="none">PRO</wa-badge>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/themes/premium/">Premium</a>
|
||||
<wa-badge class="pro" appearance="accent" attention="none">PRO</wa-badge>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/themes/tailspin/">Tailspin</a>
|
||||
<wa-badge class="pro" appearance="accent" attention="none">PRO</wa-badge>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/themes/custom/">Custom</a>
|
||||
<wa-badge class="pro" appearance="accent" attention="none">PRO</wa-badge>
|
||||
</li>
|
||||
<li><a href="/docs/themes/creating/">Creating Themes</a></li>
|
||||
</ul>
|
||||
</wa-details>
|
||||
|
||||
<!-- Components -->
|
||||
<wa-details appearance="outlined">
|
||||
<h2 slot="summary">
|
||||
@@ -121,21 +77,18 @@
|
||||
<li><a href="/docs/components/dialog/">Dialog</a></li>
|
||||
<li><a href="/docs/components/divider/">Divider</a></li>
|
||||
<li><a href="/docs/components/drawer/">Drawer</a></li>
|
||||
<li><a href="/docs/components/dropdown/">Dropdown</a></li>
|
||||
<li>
|
||||
<a href="/docs/components/dropdown">Dropdown</a>
|
||||
<ul>
|
||||
<li><a href="/docs/components/dropdown-item">Dropdown Item</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="/docs/components/format-bytes/">Format Bytes</a></li>
|
||||
<li><a href="/docs/components/format-date/">Format Date</a></li>
|
||||
<li><a href="/docs/components/format-number/">Format Number</a></li>
|
||||
<li><a href="/docs/components/icon/">Icon</a></li>
|
||||
<li><a href="/docs/components/icon-button/">Icon Button</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/menu/">Menu</a>
|
||||
<ul>
|
||||
<li><a href="/docs/components/menu-item/">Menu Item</a></li>
|
||||
<li><a href="/docs/components/menu-label/">Menu Label</a></li>
|
||||
</ul>
|
||||
</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>
|
||||
@@ -175,6 +128,7 @@
|
||||
<li><a href="/docs/components/tooltip/">Tooltip</a></li>
|
||||
<li><a href="/docs/components/tree/">Tree</a></li>
|
||||
<li><a href="/docs/components/tree-item/">Tree Item</a></li>
|
||||
<li><a href="/docs/components/zoomable-frame">Zoomable Frame</a></li>
|
||||
{# PLOP_NEW_COMPONENT_PLACEHOLDER #}
|
||||
</ul>
|
||||
</wa-details>
|
||||
@@ -264,6 +218,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>
|
||||
@@ -277,18 +233,30 @@
|
||||
<a href="/docs/patterns/blog-news/">Blog & News</a>
|
||||
<wa-badge class="pro" appearance="accent" attention="none">PRO</wa-badge>
|
||||
<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>
|
||||
@@ -306,10 +274,19 @@
|
||||
</li>
|
||||
<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>
|
||||
@@ -351,47 +328,34 @@
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/patterns/layouts/">Layouts</a>
|
||||
<wa-badge class="pro" appearance="accent" attention="none">PRO</wa-badge>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="/docs/patterns/layouts/ecommerce/">Ecommerce</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/patterns/layouts/app/">App</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/patterns/layouts/blog/">Blog</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</wa-details>
|
||||
|
||||
<!-- Color palettes -->
|
||||
<wa-details appearance="outlined">
|
||||
<h2 slot="summary">
|
||||
<a href="/docs/palettes/" title="Overview">
|
||||
Color Palettes
|
||||
<wa-icon name="grid-2" aria-hidden="true"></wa-icon>
|
||||
</a>
|
||||
</h2>
|
||||
<ul>
|
||||
<li><a href="/docs/palettes/default/">Default</a></li>
|
||||
<li>
|
||||
<a href="/docs/palettes/anodized/">Anodized</a>
|
||||
<wa-badge class="pro" appearance="accent" attention="none">PRO</wa-badge>
|
||||
</li>
|
||||
<li><a href="/docs/palettes/bright/">Bright</a></li>
|
||||
<li>
|
||||
<a href="/docs/palettes/elegant/">Elegant</a>
|
||||
<wa-badge class="pro" appearance="accent" attention="none">PRO</wa-badge>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/palettes/mild/">Mild</a>
|
||||
<wa-badge class="pro" appearance="accent" attention="none">PRO</wa-badge>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/palettes/natural/">Natural</a>
|
||||
<wa-badge class="pro" appearance="accent" attention="none">PRO</wa-badge>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/palettes/rudimentary/">Rudimentary</a>
|
||||
<wa-badge class="pro" appearance="accent" attention="none">PRO</wa-badge>
|
||||
</li>
|
||||
<li><a href="/docs/palettes/shoelace/">Shoelace</a></li>
|
||||
<li>
|
||||
<a href="/docs/palettes/vogue/">Vogue</a>
|
||||
<wa-badge class="pro" appearance="accent" attention="none">PRO</wa-badge>
|
||||
</li>
|
||||
</ul>
|
||||
</wa-details>
|
||||
<!-- Theming -->
|
||||
<h2>Theming</h2>
|
||||
<ul>
|
||||
<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>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<!-- Design tokens -->
|
||||
<wa-details appearance="outlined">
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
{% if since -%}
|
||||
<wa-badge variant="neutral">Since {{ since }}</wa-badge>
|
||||
{% endif -%}
|
||||
|
||||
{%- if status %}
|
||||
{%- if status == "experimental" %}
|
||||
<wa-badge variant="warning">
|
||||
<wa-icon name="flask"></wa-icon>
|
||||
Experimental
|
||||
</wa-badge>
|
||||
{%- elif status == "stable" %}
|
||||
<wa-badge variant="brand">Stable</wa-badge>
|
||||
{%- else %}
|
||||
<wa-badge>{{ status}}</wa-badge>
|
||||
{%- endif -%}
|
||||
{%- endif %}
|
||||
|
||||
{%- if isPro %}
|
||||
<wa-badge class="pro">PRO</wa-badge>
|
||||
{%- endif -%}
|
||||
@@ -1,4 +0,0 @@
|
||||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0 6C0 2.68629 2.68629 0 6 0H26C29.3137 0 32 2.68629 32 6V26C32 29.3137 29.3137 32 26 32H6C2.68629 32 0 29.3137 0 26V6Z" fill="var(--wa-color-neutral-fill-normal)"/>
|
||||
<path d="M23.4688 13.2188C23.5938 13.5 23.5 13.7812 23.2812 14L21.9375 15.2188C21.9688 15.4688 21.9688 15.75 21.9688 16C21.9688 16.2812 21.9688 16.5625 21.9375 16.8125L23.2812 18.0312C23.5 18.2188 23.5938 18.5312 23.4688 18.8125C23.3438 19.1875 23.1875 19.5312 23 19.875L22.8438 20.125C22.625 20.4688 22.4062 20.8125 22.1562 21.0938C21.9688 21.3438 21.6562 21.4062 21.375 21.3125L19.6562 20.7812C19.2188 21.0938 18.75 21.3438 18.2812 21.5625L17.875 23.3438C17.8125 23.625 17.5938 23.8438 17.3125 23.9062C16.875 23.9688 16.4375 24 15.9688 24C15.5312 24 15.0938 23.9688 14.6562 23.9062C14.375 23.8438 14.1562 23.625 14.0938 23.3438L13.6875 21.5625C13.1875 21.3438 12.75 21.0938 12.3125 20.7812L10.5938 21.3125C10.3125 21.4062 10 21.3438 9.8125 21.125C9.5625 20.8125 9.34375 20.4688 9.125 20.125L8.96875 19.875C8.78125 19.5312 8.625 19.1875 8.5 18.8125C8.375 18.5312 8.46875 18.25 8.6875 18.0312L10.0312 16.8125C10 16.5625 10 16.2812 10 16C10 15.75 10 15.4688 10.0312 15.2188L8.6875 14C8.46875 13.7812 8.375 13.5 8.5 13.2188C8.625 12.8438 8.78125 12.5 8.96875 12.1562L9.125 11.9062C9.34375 11.5625 9.5625 11.2188 9.8125 10.9062C10 10.6875 10.3125 10.625 10.5938 10.7188L12.3125 11.25C12.75 10.9375 13.2188 10.6562 13.6875 10.4688L14.0938 8.6875C14.1562 8.40625 14.375 8.1875 14.6562 8.125C15.0938 8.0625 15.5312 8 16 8C16.4375 8 16.875 8.0625 17.3125 8.125C17.5938 8.15625 17.8125 8.40625 17.875 8.6875L18.2812 10.4688C18.7812 10.6562 19.2188 10.9375 19.6562 11.25L21.375 10.7188C21.6562 10.625 21.9688 10.6875 22.1562 10.9062C22.4062 11.2188 22.625 11.5625 22.8438 11.9062L23 12.1562C23.1875 12.5 23.3438 12.8438 23.5 13.2188H23.4688ZM16 18.5C16.875 18.5 17.6875 18.0312 18.1562 17.25C18.5938 16.5 18.5938 15.5312 18.1562 14.75C17.6875 14 16.875 13.5 16 13.5C15.0938 13.5 14.2812 14 13.8125 14.75C13.375 15.5312 13.375 16.5 13.8125 17.25C14.2812 18.0312 15.0938 18.5 16 18.5Z" fill="var(--wa-color-neutral-on-normal)"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.1 KiB |
@@ -1,20 +0,0 @@
|
||||
{% set paletteId = palette.fileSlug or page.fileSlug %}
|
||||
{% set suffixes = ['-80', '', '-20'] %}
|
||||
|
||||
<wa-scoped class="palette-icon-host">
|
||||
<template>
|
||||
<link rel="stylesheet" href="/dist/styles/color/{{ paletteId }}.css">
|
||||
<link rel="stylesheet" href="/assets/styles/theme-icons.css">
|
||||
|
||||
<div class="palette-icon" style="--hues: {{ hues|length }}; --suffixes: {{ suffixes|length }}">
|
||||
{% for hue in hues -%}
|
||||
{% set hueIndex = loop.index %}
|
||||
{% for suffix in suffixes -%}
|
||||
<div class="swatch"
|
||||
data-hue="{{ hue }}" data-suffix="{{ suffix }}"
|
||||
style="--color: var(--wa-color-{{ hue }}{{ suffix }}); grid-column: {{ hueIndex }}; grid-row: {{ loop.index }}"> </div>
|
||||
{%- endfor %}
|
||||
{%- endfor %}
|
||||
</div>
|
||||
</template>
|
||||
</wa-scoped>
|
||||
@@ -1,24 +0,0 @@
|
||||
{% set themeId = theme.fileSlug %}
|
||||
|
||||
<wa-scoped class="theme-icon-host theme-color-icon-host">
|
||||
<template>
|
||||
<link rel="stylesheet" href="/dist/styles/utilities.css">
|
||||
<link rel="stylesheet" href="/dist/styles/themes/{{ page.fileSlug or 'default' }}.css">
|
||||
<link rel="stylesheet" href="/dist/styles/themes/{{ themeId }}/color.css">
|
||||
<link rel="stylesheet" href="/assets/styles/theme-icons.css">
|
||||
|
||||
<div class="theme-icon theme-color-icon wa-theme-{{ themeId }}">
|
||||
<div class="wa-brand wa-accent">A</div>
|
||||
<div class="wa-brand wa-outlined">A</div>
|
||||
<div class="wa-brand wa-filled">A</div>
|
||||
<div class="wa-brand wa-plain">A</div>
|
||||
{# <div class="wa-danger wa-outlined wa-filled"><wa-icon slot="icon" name="circle-exclamation" variant="regular"></wa-icon></div> #}
|
||||
|
||||
<div class="wa-neutral wa-accent">A</div>
|
||||
<div class="wa-neutral wa-outlined">A</div>
|
||||
<div class="wa-neutral wa-filled">A</div>
|
||||
<div class="wa-neutral wa-plain">A</div>
|
||||
{# <div class="wa-warning wa-outlined wa-filled"><wa-icon slot="icon" name="triangle-exclamation" variant="regular"></wa-icon></div> #}
|
||||
</div>
|
||||
</template>
|
||||
</wa-scoped>
|
||||
@@ -1,16 +0,0 @@
|
||||
{% set themeId = theme.fileSlug or page.fileSlug %}
|
||||
|
||||
<wa-scoped class="theme-icon-host theme-typography-icon-host">
|
||||
<template>
|
||||
<link rel="stylesheet" href="/dist/styles/native/content.css">
|
||||
<link rel="stylesheet" href="/dist/styles/native/blockquote.css">
|
||||
<link rel="stylesheet" href="/dist/styles/themes/{{ page.fileSlug or 'default' }}.css">
|
||||
<link rel="stylesheet" href="/dist/styles/themes/{{ themeId }}/typography.css">
|
||||
<link rel="stylesheet" href="/assets/styles/theme-icons.css">
|
||||
|
||||
<div class="theme-icon theme-typography-icon wa-theme-{{ themeId }}" data-no-outline data-no-anchor role="presentation">
|
||||
<h3>Title</h3>
|
||||
<p>Body text</p>
|
||||
</div>
|
||||
</template>
|
||||
</wa-scoped>
|
||||
@@ -1,29 +0,0 @@
|
||||
{% set themeId = theme.fileSlug or page.fileSlug %}
|
||||
|
||||
|
||||
<wa-scoped class="theme-icon-host theme-overall-icon-host">
|
||||
<template>
|
||||
<link rel="stylesheet" href="/dist/styles/utilities.css">
|
||||
<link rel="stylesheet" href="/dist/styles/native/content.css">
|
||||
<link rel="stylesheet" href="/dist/styles/themes/{{ themeId }}.css">
|
||||
<link rel="stylesheet" href="/assets/styles/theme-icons.css">
|
||||
|
||||
<div class="theme-icon theme-overall-icon" role="presentation" data-no-anchor data-no-outline>
|
||||
<div class="row row-1">
|
||||
<h2>Aa</h2>
|
||||
<div class="swatches">
|
||||
<div class="wa-brand"></div>
|
||||
|
||||
<div class="wa-success"></div>
|
||||
<div class="wa-warning"></div>
|
||||
<div class="wa-danger"></div>
|
||||
<div class="wa-neutral"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row row-2">
|
||||
<wa-input value="Input" size="small" inert></wa-input>
|
||||
<wa-button size="small" variant="brand" inert>Go</wa-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</wa-scoped>
|
||||
41
packages/webawesome/docs/_includes/theme-selector.njk
Normal file
41
packages/webawesome/docs/_includes/theme-selector.njk
Normal file
@@ -0,0 +1,41 @@
|
||||
<wa-select appearance="filled" size="small" value="default" class="theme-selector">
|
||||
<wa-icon slot="start" name="paintbrush" variant="regular"></wa-icon>
|
||||
|
||||
{# Free themes #}
|
||||
{% for theme in themer.themes %}
|
||||
{% if not theme.isPro %}
|
||||
<wa-option
|
||||
value="{{ theme.filename | stripExtension }}"
|
||||
data-brand="{{ theme.colorBrand.color }}"
|
||||
data-palette="{{ theme.palette.filename | stripExtension }}"
|
||||
>
|
||||
{{ theme.name }}
|
||||
</wa-option>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{# Pro themes #}
|
||||
{% for theme in themer.themes %}
|
||||
{% if loop.first %}<wa-divider></wa-divider>{% endif %}
|
||||
{% if theme.isPro %}
|
||||
<wa-option
|
||||
value="{{ theme.filename | stripExtension }}"
|
||||
data-brand="{{ theme.colorBrand.color }}"
|
||||
data-palette="{{ theme.palette.filename | stripExtension }}"
|
||||
>
|
||||
{{ theme.name }}
|
||||
</wa-option>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</wa-select>
|
||||
|
||||
<script>
|
||||
// Immediately set the correct values from storage
|
||||
document.querySelectorAll('wa-select.theme-selector').forEach(el => {
|
||||
el.setAttribute('value', localStorage.getItem('theme') || 'default');
|
||||
});
|
||||
|
||||
// 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'}`);
|
||||
</script>
|
||||
@@ -1,335 +0,0 @@
|
||||
<div class="showcase-examples-wrapper" aria-hidden="true" data-no-outline>
|
||||
<div class="showcase-examples">
|
||||
<wa-card>
|
||||
<div slot="header" class="wa-split">
|
||||
<h3 class="wa-heading-m">Your Cart</h3>
|
||||
<wa-icon-button name="xmark" tabindex="-1"></wa-icon-button>
|
||||
</div>
|
||||
<div class="wa-stack wa-gap-xl">
|
||||
<div class="wa-flank">
|
||||
<wa-avatar shape="rounded" style="--background-color: var(--wa-color-green-60); --text-color: var(--wa-color-green-95);">
|
||||
<wa-icon slot="icon" name="sword-laser"></wa-icon>
|
||||
</wa-avatar>
|
||||
<div class="wa-stack wa-gap-2xs">
|
||||
<div class="wa-split wa-gap-2xs">
|
||||
<strong>Initiate Saber</strong>
|
||||
<strong>$179.99</strong>
|
||||
</div>
|
||||
<div class="wa-split wa-gap-2xs wa-caption-m">
|
||||
<span>Green</span>
|
||||
<a href="#" tabindex="-1">Remove</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<wa-divider></wa-divider>
|
||||
<div class="wa-flank">
|
||||
<wa-avatar shape="rounded" style="--background-color: var(--wa-color-cyan-60); --text-color: var(--wa-color-cyan-95);">
|
||||
<wa-icon slot="icon" name="robot-astromech"></wa-icon>
|
||||
</wa-avatar>
|
||||
<div class="wa-stack wa-gap-2xs">
|
||||
<div class="wa-split wa-gap-2xs">
|
||||
<strong>Repair Droid</strong>
|
||||
<strong>$3,049.99</strong>
|
||||
</div>
|
||||
<div class="wa-split wa-gap-2xs wa-caption-m">
|
||||
<span>R-series</span>
|
||||
<a href="#" tabindex="-1">Remove</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div slot="footer" class="wa-stack">
|
||||
<div class="wa-split">
|
||||
<strong>Subtotal</strong>
|
||||
<strong>$3,229.98</strong>
|
||||
</div>
|
||||
<span class="wa-caption-m">Shipping and taxes calculated at checkout.</span>
|
||||
<wa-button tabindex="-1" variant="brand">
|
||||
<wa-icon slot="prefix" name="shopping-bag"></wa-icon>
|
||||
Checkout
|
||||
</wa-button>
|
||||
</div>
|
||||
</wa-card>
|
||||
<wa-card>
|
||||
<wa-avatar shape="rounded" style="--size: 1.9lh; float: left; margin-right: var(--wa-space-m);">
|
||||
<wa-icon slot="icon" name="hat-wizard" style="font-size: 1.75em;"></wa-icon>
|
||||
</wa-avatar>
|
||||
<p class="wa-body-l" style="margin: 0;">“All we have to decide is what to do with the time that is given to us. There are other forces at work in this world, Frodo, besides the will of evil.”</p>
|
||||
</wa-card>
|
||||
<wa-card>
|
||||
<div class="wa-stack">
|
||||
<h3 class="wa-heading-m">Sign In</h3>
|
||||
<wa-input tabindex="-1" label="Email" placeholder="ddjarin@mandalore.gov">
|
||||
<wa-icon slot="prefix" name="envelope" variant="regular"></wa-icon>
|
||||
</wa-input>
|
||||
<wa-input tabindex="-1" label="Password" type="password">
|
||||
<wa-icon slot="prefix" name="lock" variant="regular"></wa-icon>
|
||||
</wa-input>
|
||||
<wa-button tabindex="-1" variant="brand">Sign In</wa-button>
|
||||
<a href="#" tabindex="-1" class="wa-body-s">I forgot my password</a>
|
||||
</div>
|
||||
</wa-card>
|
||||
<wa-card>
|
||||
<div class="wa-stack">
|
||||
<div class="wa-split">
|
||||
<h3 class="wa-heading-m">To-Do</h3>
|
||||
<wa-icon-button tabindex="-1" name="plus" label="Add task"></wa-icon-button>
|
||||
</div>
|
||||
<wa-checkbox tabindex="-1" checked>Umbrella for Adelard</wa-checkbox>
|
||||
<wa-checkbox tabindex="-1" checked>Waste-paper basket for Dora</wa-checkbox>
|
||||
<wa-checkbox tabindex="-1" checked>Pen and ink for Milo</wa-checkbox>
|
||||
<wa-checkbox tabindex="-1">Mirror for Angelica</wa-checkbox>
|
||||
<wa-checkbox tabindex="-1">Silver spoons for Lobelia</wa-checkbox>
|
||||
</div>
|
||||
<div slot="footer">
|
||||
<a href="" tabindex="-1">View all completed</a>
|
||||
</div>
|
||||
</wa-card>
|
||||
<wa-card>
|
||||
<div class="wa-stack">
|
||||
<div class="wa-frame wa-border-radius-m" style="align-self: center; max-inline-size: 25ch;">
|
||||
<img src="https://images.unsplash.com/photo-1667514627762-521b1c815a89?q=20" alt="Album art">
|
||||
</div>
|
||||
<div class="wa-flank:end wa-align-items-start">
|
||||
<div class="wa-stack wa-gap-3xs">
|
||||
<div class="wa-cluster wa-gap-xs" style="height: 2.25em;">
|
||||
<strong>The Stone Troll</strong>
|
||||
<small><wa-badge variant="neutral" appearance="filled">E</wa-badge></small>
|
||||
</div>
|
||||
<span class="wa-caption-m">Samwise G</span>
|
||||
</div>
|
||||
<wa-icon-button tabindex="-1" name="ellipsis" label="Options"></wa-icon-button>
|
||||
</div>
|
||||
<div class="wa-stack wa-gap-2xs">
|
||||
<wa-progress-bar value="34" style="height: 0.5em"></wa-progress-bar>
|
||||
<div class="wa-split">
|
||||
<span class="wa-caption-xs">1:01</span>
|
||||
<span class="wa-caption-xs">-1:58</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="wa-grid wa-align-items-center" style="--min-column-size: 1em; justify-items: center;">
|
||||
<wa-icon-button tabindex="-1" name="backward" label="Skip backward"></wa-icon-button>
|
||||
<wa-icon-button tabindex="-1" name="pause" style="font-size: var(--wa-font-size-2xl);" label="Pause"></wa-icon-button>
|
||||
<wa-icon-button tabindex="-1" name="forward" label="Skip forward"></wa-icon-button>
|
||||
</div>
|
||||
</div>
|
||||
</wa-card>
|
||||
<wa-card>
|
||||
<div class="wa-stack">
|
||||
<h3 class="wa-heading-m">Chalmun's Spaceport Cantina</h3>
|
||||
<div class="wa-cluster wa-gap-xs">
|
||||
<wa-rating value="4.6" readonly tabindex="-1"></wa-rating>
|
||||
<strong>4.6</strong>
|
||||
<span>(419 reviews)</span>
|
||||
</div>
|
||||
<div class="wa-cluster wa-gap-xs">
|
||||
<div class="wa-cluster wa-gap-3xs">
|
||||
<wa-icon name="dollar" style="color: var(--wa-color-green-60);"></wa-icon>
|
||||
<wa-icon name="dollar" style="color: var(--wa-color-green-60);"></wa-icon>
|
||||
<wa-icon name="dollar" style="color: var(--wa-color-green-60);"></wa-icon>
|
||||
</div>
|
||||
<span class="wa-caption-m">•</span>
|
||||
<wa-tag size="small">Cocktail Bar</wa-tag>
|
||||
<wa-tag size="small">Gastropub</wa-tag>
|
||||
<wa-tag size="small">Local Fare</wa-tag>
|
||||
<wa-tag size="small">Gluten Free</wa-tag>
|
||||
</div>
|
||||
<div class="wa-flank wa-gap-xs">
|
||||
<wa-icon name="location-dot"></wa-icon>
|
||||
<a href="#" class="wa-caption-m" tabindex="-1">Mos Eisley, Tatooine</a>
|
||||
</div>
|
||||
</div>
|
||||
</wa-card>
|
||||
<wa-card>
|
||||
<div class="wa-stack">
|
||||
<div class="wa-flank:end">
|
||||
<h3 id="odds-label" class="wa-heading-m">Tell Me the Odds</h3>
|
||||
<wa-switch size="large" aria-labelledby="odds-label" tabindex="-1"></wa-switch>
|
||||
</div>
|
||||
<p class="wa-body-s">Allow protocol droids to inform you of probabilities, such as the success rate of navigating an asteroid field. We recommend setting this to "Never."</p>
|
||||
</div>
|
||||
</wa-card>
|
||||
<wa-card>
|
||||
<div class="wa-stack">
|
||||
<div class="wa-split wa-align-items-start">
|
||||
<dl class="wa-stack wa-gap-2xs">
|
||||
<dt class="wa-heading-s">Amount</dt>
|
||||
<dd class="wa-heading-l">$5,610.00</dd>
|
||||
</dl>
|
||||
<wa-badge appearance="filled outlined" variant="success">Paid</wa-badge>
|
||||
</div>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<dd>Paid with copper pennies</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
<div slot="footer">
|
||||
<a href="" class="wa-cluster wa-gap-2xs" tabindex="-1">
|
||||
<span>Download Receipt</span>
|
||||
<wa-icon name="arrow-right"></wa-icon>
|
||||
</a>
|
||||
</div>
|
||||
</wa-card>
|
||||
<wa-card>
|
||||
<div class="wa-stack">
|
||||
<div class="wa-split">
|
||||
<div class="wa-cluster wa-heading-l">
|
||||
<wa-icon name="book-sparkles"></wa-icon>
|
||||
<h3>Fellowship</h3>
|
||||
</div>
|
||||
<wa-badge>Most Popular</wa-badge>
|
||||
</div>
|
||||
<span class="wa-flank wa-align-items-baseline wa-gap-2xs">
|
||||
<span class="wa-heading-2xl">$120</span>
|
||||
<span class="wa-caption-l">per year</span>
|
||||
</span>
|
||||
<p class="wa-caption-l">Carry great power (and great responsibility).</p>
|
||||
<wa-button variant="brand" tabindex="-1">Get this Plan</wa-button>
|
||||
</div>
|
||||
<div slot="footer" class="wa-stack wap-gap-s">
|
||||
<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>
|
||||
<span class="wa-caption-m">9 users</span>
|
||||
</div>
|
||||
<div class="wa-flank">
|
||||
<wa-icon name="ring" fixed-width></wa-icon>
|
||||
<span class="wa-caption-m">1 ring</span>
|
||||
</div>
|
||||
<div class="wa-flank">
|
||||
<wa-icon name="chess-rook" fixed-width></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>
|
||||
<span class="wa-caption-m">Priority eagle support</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</wa-card>
|
||||
<wa-card with-footer>
|
||||
<div class="wa-flank:end">
|
||||
<div class="wa-stack wa-gap-xs">
|
||||
<div class="wa-cluster wa-gap-xs">
|
||||
<h3 class="wa-heading-s">Migs Mayfeld</h3 class="wa-heading-s">
|
||||
<wa-badge pill>Admin</wa-badge>
|
||||
</div>
|
||||
<span class="wa-caption-m">Bounty Hunter</span>
|
||||
</div>
|
||||
<wa-avatar image="https://images.unsplash.com/photo-1633268335280-a41fbde58707?q=80&w=3348&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" label="Avatar of a man wearing a sci-fi helmet (Photograph by Nandu Vasudevan)"></wa-avatar>
|
||||
</div>
|
||||
<div slot="footer" class="wa-grid wa-gap-xs" style="--min-column-size: 10ch;">
|
||||
<wa-button appearance="outlined" tabindex="-1">
|
||||
<wa-icon slot="prefix" name="at"></wa-icon>
|
||||
Email
|
||||
</wa-button>
|
||||
<wa-button appearance="outlined" tabindex="-1">
|
||||
<wa-icon slot="prefix" name="phone"></wa-icon>
|
||||
Phone
|
||||
</wa-button>
|
||||
</div>
|
||||
</wa-card>
|
||||
<wa-card>
|
||||
<div class="wa-flank:end">
|
||||
<a href="" class="wa-flank wa-link-plain" tabindex="-1">
|
||||
<wa-avatar shape="rounded" style="--background-color: var(--wa-color-yellow-90); --text-color: var(--wa-color-yellow-50)">
|
||||
<wa-icon slot="icon" name="egg-fried"></wa-icon>
|
||||
</wa-avatar>
|
||||
<div class="wa-gap-2xs wa-stack">
|
||||
<span class="wa-heading-s">Second Breakfast</span>
|
||||
<span class="wa-caption-m">19 Items</span>
|
||||
</div>
|
||||
</a>
|
||||
<wa-dropdown>
|
||||
<wa-icon-button id="more-actions-2" slot="trigger" name="ellipsis-vertical" label="View menu" tabindex="-1"></wa-icon-button>
|
||||
<wa-menu>
|
||||
<wa-menu-item>Copy link</wa-menu-item>
|
||||
<wa-menu-item>Rename</wa-menu-item>
|
||||
<wa-menu-item>Move to trash</wa-menu-item>
|
||||
</wa-menu>
|
||||
</wa-dropdown>
|
||||
<wa-tooltip for="more-actions-2">View menu</wa-tooltip>
|
||||
</div>
|
||||
</wa-card>
|
||||
<wa-card with-header with-footer>
|
||||
<div slot="header" class="wa-stack wa-gap-xs">
|
||||
<h2 class="wa-heading-m">Decks</h2>
|
||||
</div>
|
||||
<div class="wa-stack wa-gap-xl">
|
||||
<p class="wa-caption-m">You haven’t created any decks yet. Get started by selecting an aspect that matches your play style.</p>
|
||||
<div class="wa-grid wa-gap-xl" style="--min-column-size: 30ch;">
|
||||
<a href="" class="wa-flank wa-align-items-start wa-link-plain" tabindex="-1">
|
||||
<wa-avatar shape="rounded" style="--background-color: var(--wa-color-blue-90);color: var(--wa-color-blue-50);">
|
||||
<wa-icon slot="icon" name="shield"></wa-icon>
|
||||
</wa-avatar>
|
||||
<div class="wa-stack wa-gap-2xs">
|
||||
<span class="wa-align-items-center wa-cluster wa-gap-xs wa-heading-s">
|
||||
Vigilance <wa-icon name="arrow-right"></wa-icon>
|
||||
</span>
|
||||
<p class="wa-caption-m">
|
||||
Protect, defend, and restore as you ready heavy-hitters.
|
||||
</p>
|
||||
</div>
|
||||
</a>
|
||||
<a href="" class="wa-flank wa-align-items-start wa-link-plain" tabindex="-1">
|
||||
<wa-avatar shape="rounded" style="--background-color: var(--wa-color-green-90);color: var(--wa-color-green-50);">
|
||||
<wa-icon slot="icon" name="chevrons-up"></wa-icon>
|
||||
</wa-avatar>
|
||||
<div class="wa-stack wa-gap-2xs">
|
||||
<span class="wa-align-items-center wa-cluster wa-gap-xs wa-heading-s">
|
||||
Command <wa-icon name="arrow-right"></wa-icon>
|
||||
</span>
|
||||
<p class="wa-caption-m">
|
||||
Build imposing armies and stockpile resources.
|
||||
</p>
|
||||
</div>
|
||||
</a>
|
||||
<a href=""class="wa-flank wa-align-items-start wa-link-plain" tabindex="-1">
|
||||
<wa-avatar shape="rounded" style="--background-color: var(--wa-color-red-90);color: var(--wa-color-red-50);">
|
||||
<wa-icon slot="icon" name="explosion"></wa-icon>
|
||||
</wa-avatar>
|
||||
<div class="wa-stack wa-gap-2xs">
|
||||
<span class="wa-align-items-center wa-cluster wa-gap-xs wa-heading-s">
|
||||
Aggression <wa-icon name="arrow-right"></wa-icon>
|
||||
</span>
|
||||
<p class="wa-caption-m">
|
||||
Relentlessly deal damage and apply pressure to your opponent.
|
||||
</p>
|
||||
</div>
|
||||
</a>
|
||||
<a href="" class="wa-flank wa-align-items-start wa-link-plain" tabindex="-1">
|
||||
<wa-avatar shape="rounded" style="--background-color: var(--wa-color-yellow-90);color: var(--wa-color-yellow-50);">
|
||||
<wa-icon slot="icon" name="moon-stars"></wa-icon>
|
||||
</wa-avatar>
|
||||
<div class="wa-stack wa-gap-2xs">
|
||||
<span class="wa-align-items-center wa-cluster wa-gap-xs wa-heading-s">
|
||||
Cunning <wa-icon name="arrow-right"></wa-icon>
|
||||
</span>
|
||||
<p class="wa-caption-m">
|
||||
Disrupt and frustrate your opponent with dastardly tricks.
|
||||
</p>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div slot="footer">
|
||||
<a href="" class="wa-cluster wa-gap-xs" tabindex="-1">
|
||||
<span>Or start a deck from scratch</span>
|
||||
<wa-icon name="arrow-right"></wa-icon>
|
||||
</a>
|
||||
</div>
|
||||
</wa-card>
|
||||
</div>
|
||||
</div>
|
||||
@@ -70,7 +70,9 @@
|
||||
<div class="alignment">
|
||||
<wa-avatar></wa-avatar>
|
||||
<wa-rating></wa-rating>
|
||||
<wa-icon-button name="gear" label="Settings"></wa-icon-button>
|
||||
<wa-button appearance="plain">
|
||||
<wa-icon name="gear" label="Settings"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-spinner></wa-spinner>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -8,27 +8,6 @@
|
||||
<th><code>.wa-[appearance]</code></th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th><code>accent</code> + <code>outlined</code></th>
|
||||
<td>
|
||||
<div class="wa-cluster wa-gap-2xs">
|
||||
<wa-badge variant="brand" appearance="accent outlined">Brand</wa-badge>
|
||||
<wa-badge variant="neutral" appearance="accent outlined">Neutral</wa-badge>
|
||||
<wa-badge variant="success" appearance="accent outlined">Success</wa-badge>
|
||||
<wa-badge variant="warning" appearance="accent outlined">Warning</wa-badge>
|
||||
<wa-badge variant="danger" appearance="accent outlined">Danger</wa-badge>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="wa-cluster wa-gap-2xs">
|
||||
<wa-badge variant="brand" class="wa-accent wa-outlined">Brand</wa-badge>
|
||||
<wa-badge variant="neutral" class="wa-accent wa-outlined">Neutral</wa-badge>
|
||||
<wa-badge variant="success" class="wa-accent wa-outlined">Success</wa-badge>
|
||||
<wa-badge variant="warning" class="wa-accent wa-outlined">Warning</wa-badge>
|
||||
<wa-badge variant="danger" class="wa-accent wa-outlined">Danger</wa-badge>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><code>accent</code></th>
|
||||
<td>
|
||||
|
||||
@@ -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,9 +512,8 @@
|
||||
<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>
|
||||
@@ -592,9 +521,8 @@
|
||||
</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>
|
||||
@@ -605,9 +533,8 @@
|
||||
<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>
|
||||
@@ -615,9 +542,8 @@
|
||||
</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>
|
||||
@@ -628,9 +554,8 @@
|
||||
<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>
|
||||
@@ -638,9 +563,8 @@
|
||||
</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>
|
||||
@@ -651,9 +575,8 @@
|
||||
<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>
|
||||
@@ -661,9 +584,8 @@
|
||||
</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>
|
||||
@@ -674,9 +596,8 @@
|
||||
<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>
|
||||
@@ -684,9 +605,8 @@
|
||||
</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>
|
||||
@@ -695,4 +615,4 @@
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -17,27 +17,6 @@
|
||||
<button>Button</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><code>accent</code> + <code>outlined</code></th>
|
||||
<td>
|
||||
<div class="wa-cluster wa-gap-2xs">
|
||||
<wa-button variant="brand" appearance="accent outlined">Brand</wa-button>
|
||||
<wa-button variant="neutral" appearance="accent outlined">Neutral</wa-button>
|
||||
<wa-button variant="success" appearance="accent outlined">Success</wa-button>
|
||||
<wa-button variant="warning" appearance="accent outlined">Warning</wa-button>
|
||||
<wa-button variant="danger" appearance="accent outlined">Danger</wa-button>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="wa-cluster wa-gap-2xs">
|
||||
<button class="wa-brand wa-accent wa-outlined">Brand</button>
|
||||
<button class="wa-neutral wa-accent wa-outlined">Neutral</button>
|
||||
<button class="wa-success wa-accent wa-outlined">Success</button>
|
||||
<button class="wa-warning wa-accent wa-outlined">Warning</button>
|
||||
<button class="wa-danger wa-accent wa-outlined">Danger</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><code>accent</code></th>
|
||||
<td>
|
||||
@@ -465,8 +444,8 @@
|
||||
</wa-select>
|
||||
</td>
|
||||
<td>
|
||||
<label class="wa-filled">Select (filled)
|
||||
<select value="1">
|
||||
<label>Select (filled)
|
||||
<select class="wa-filled" value="1">
|
||||
<option value="1">Option</option>
|
||||
</select>
|
||||
</label>
|
||||
|
||||
@@ -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">
|
||||
@@ -258,20 +198,16 @@
|
||||
<th><code>small</code>/<code>s</code></th>
|
||||
<td>
|
||||
<wa-dropdown size="small">
|
||||
<wa-button slot="trigger" caret>Dropdown</wa-button>
|
||||
<wa-menu>
|
||||
<wa-menu-item>Menu Item 1</wa-menu-item>
|
||||
<wa-menu-item>Menu Item 2</wa-menu-item>
|
||||
</wa-menu>
|
||||
<wa-button slot="trigger" with-caret>Dropdown</wa-button>
|
||||
<wa-dropdown-item>Menu Item 1</wa-dropdown-item>
|
||||
<wa-dropdown-item>Menu Item 2</wa-dropdown-item>
|
||||
</wa-dropdown>
|
||||
</td>
|
||||
<td>
|
||||
<wa-dropdown class="wa-size-s">
|
||||
<wa-button slot="trigger" caret>Dropdown</wa-button>
|
||||
<wa-menu>
|
||||
<wa-menu-item>Menu Item 1</wa-menu-item>
|
||||
<wa-menu-item>Menu Item 2</wa-menu-item>
|
||||
</wa-menu>
|
||||
<wa-button slot="trigger" with-caret>Dropdown</wa-button>
|
||||
<wa-dropdown-item>Menu Item 1</wa-dropdown-item>
|
||||
<wa-dropdown-item>Menu Item 2</wa-dropdown-item>
|
||||
</wa-dropdown>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -279,20 +215,16 @@
|
||||
<th><code>medium</code>/<code>m</code></th>
|
||||
<td>
|
||||
<wa-dropdown size="medium">
|
||||
<wa-button slot="trigger" caret>Dropdown</wa-button>
|
||||
<wa-menu>
|
||||
<wa-menu-item>Menu Item 1</wa-menu-item>
|
||||
<wa-menu-item>Menu Item 2</wa-menu-item>
|
||||
</wa-menu>
|
||||
<wa-button slot="trigger" with-caret>Dropdown</wa-button>
|
||||
<wa-dropdown-item>Menu Item 1</wa-dropdown-item>
|
||||
<wa-dropdown-item>Menu Item 2</wa-dropdown-item>
|
||||
</wa-dropdown>
|
||||
</td>
|
||||
<td>
|
||||
<wa-dropdown class="wa-size-m">
|
||||
<wa-button slot="trigger" caret>Dropdown</wa-button>
|
||||
<wa-menu>
|
||||
<wa-menu-item>Menu Item 1</wa-menu-item>
|
||||
<wa-menu-item>Menu Item 2</wa-menu-item>
|
||||
</wa-menu>
|
||||
<wa-button slot="trigger" with-caret>Dropdown</wa-button>
|
||||
<wa-dropdown-item>Menu Item 1</wa-dropdown-item>
|
||||
<wa-dropdown-item>Menu Item 2</wa-dropdown-item>
|
||||
</wa-dropdown>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -300,20 +232,16 @@
|
||||
<th><code>large</code>/<code>l</code></th>
|
||||
<td>
|
||||
<wa-dropdown size="large">
|
||||
<wa-button slot="trigger" caret>Dropdown</wa-button>
|
||||
<wa-menu>
|
||||
<wa-menu-item>Menu Item 1</wa-menu-item>
|
||||
<wa-menu-item>Menu Item 2</wa-menu-item>
|
||||
</wa-menu>
|
||||
<wa-button slot="trigger" with-caret>Dropdown</wa-button>
|
||||
<wa-dropdown-item>Menu Item 1</wa-dropdown-item>
|
||||
<wa-dropdown-item>Menu Item 2</wa-dropdown-item>
|
||||
</wa-dropdown>
|
||||
</td>
|
||||
<td>
|
||||
<wa-dropdown class="wa-size-l">
|
||||
<wa-button slot="trigger" caret>Dropdown</wa-button>
|
||||
<wa-menu>
|
||||
<wa-menu-item>Menu Item 1</wa-menu-item>
|
||||
<wa-menu-item>Menu Item 2</wa-menu-item>
|
||||
</wa-menu>
|
||||
<wa-button slot="trigger" with-caret>Dropdown</wa-button>
|
||||
<wa-dropdown-item>Menu Item 1</wa-dropdown-item>
|
||||
<wa-dropdown-item>Menu Item 2</wa-dropdown-item>
|
||||
</wa-dropdown>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -322,66 +250,6 @@
|
||||
</div>
|
||||
<wa-divider></wa-divider>
|
||||
|
||||
<h3>Menu</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-menu size="small">
|
||||
<wa-menu-item>Menu Item 1</wa-menu-item>
|
||||
<wa-menu-item>Menu Item 2</wa-menu-item>
|
||||
</wa-menu>
|
||||
</td>
|
||||
<td>
|
||||
<wa-menu class="wa-size-s">
|
||||
<wa-menu-item>Menu Item 1</wa-menu-item>
|
||||
<wa-menu-item>Menu Item 2</wa-menu-item>
|
||||
</wa-menu>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><code>medium</code>/<code>m</code></th>
|
||||
<td>
|
||||
<wa-menu size="medium">
|
||||
<wa-menu-item>Menu Item 1</wa-menu-item>
|
||||
<wa-menu-item>Menu Item 2</wa-menu-item>
|
||||
</wa-menu>
|
||||
</td>
|
||||
<td>
|
||||
<wa-menu class="wa-size-m">
|
||||
<wa-menu-item>Menu Item 1</wa-menu-item>
|
||||
<wa-menu-item>Menu Item 2</wa-menu-item>
|
||||
</wa-menu>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><code>large</code>/<code>l</code></th>
|
||||
<td>
|
||||
<wa-menu size="large">
|
||||
<wa-menu-item>Menu Item 1</wa-menu-item>
|
||||
<wa-menu-item>Menu Item 2</wa-menu-item>
|
||||
</wa-menu>
|
||||
</td>
|
||||
<td>
|
||||
<wa-menu class="wa-size-l">
|
||||
<wa-menu-item>Menu Item 1</wa-menu-item>
|
||||
<wa-menu-item>Menu Item 2</wa-menu-item>
|
||||
</wa-menu>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<wa-divider></wa-divider>
|
||||
|
||||
<h3>Input</h3>
|
||||
|
||||
<div class="table-scroll">
|
||||
@@ -799,4 +667,4 @@
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,18 +1,12 @@
|
||||
<!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 }}">
|
||||
|
||||
{% block header %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{{ content | safe }}
|
||||
{% endblock %}
|
||||
|
||||
{% block afterContent %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{{ content | safe }}
|
||||
{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
{% set hasSidebar = true %}
|
||||
{% set hasOutline = true %}
|
||||
|
||||
{% extends '../_includes/base.njk' %}
|
||||
|
||||
{# Component header #}
|
||||
{% block header %}
|
||||
{% include 'breadcrumbs.njk' %}
|
||||
<h1 class="title">{{ title }}</h1>
|
||||
<div class="block-info">
|
||||
{% set snippets = (elements or element or snippets or snippet) | dict %}
|
||||
|
||||
{% for snippet, link in snippets %}
|
||||
{% if snippet %}
|
||||
<code class="class">
|
||||
{%- if link -%}
|
||||
<a href="{{ link }}">{{ snippet }}</a>
|
||||
{%- else -%}
|
||||
{{ snippet }}
|
||||
{%- endif-%}
|
||||
</code>
|
||||
{%- endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% include '../_includes/status.njk' %}
|
||||
</div>
|
||||
{% if description %}
|
||||
<p class="summary">
|
||||
{{ description | inlineMarkdown | safe }}
|
||||
</p>
|
||||
{% endif %}
|
||||
{% block notes %}{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
{# Content #}
|
||||
{% block content %}
|
||||
{{ content | safe }}
|
||||
{% endblock %}
|
||||
@@ -1,5 +1,22 @@
|
||||
{% extends '../_layouts/block.njk' %}
|
||||
{% set component = components[page.fileSlug] %}
|
||||
{% extends '../_layouts/docs.njk' %}
|
||||
{% set component = getComponent('wa-' + page.fileSlug) %}
|
||||
|
||||
{# Component header #}
|
||||
{% block beforeContent %}
|
||||
<div class="component-info">
|
||||
<code class="component-tag"><{{ component.tagName }}></code>
|
||||
<wa-badge variant="neutral">Since {{ component.since }}</wa-badge>
|
||||
<wa-badge
|
||||
{% if component.status == 'stable' %}variant="brand"{% endif %}
|
||||
{% if component.status == 'experimental' %}variant="warning"{% endif %}
|
||||
>
|
||||
{{ component.status }}
|
||||
</wa-badge>
|
||||
</div>
|
||||
<p class="component-summary">
|
||||
{{ component.summary | inlineMarkdown | safe }}
|
||||
</p>
|
||||
{% endblock %}
|
||||
|
||||
{# Component API #}
|
||||
{% block afterContent %}
|
||||
@@ -9,7 +26,7 @@
|
||||
<p>Learn more about <a href="/docs/usage/#slots">using slots</a>.</p>
|
||||
|
||||
<wa-scroller>
|
||||
<table class="component-table">
|
||||
<table class="component-table wa-hover-rows">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="table-name">Name</th>
|
||||
@@ -40,7 +57,7 @@
|
||||
<p>Learn more about <a href="/docs/usage/#attributes-and-properties">attributes and properties</a>.</p>
|
||||
|
||||
<wa-scroller>
|
||||
<table class="component-table">
|
||||
<table class="component-table wa-hover-rows">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="table-name">Name</th>
|
||||
@@ -87,7 +104,7 @@
|
||||
<p>Learn more about <a href="/docs/usage/#methods">methods</a>.</p>
|
||||
|
||||
<wa-scroller>
|
||||
<table class="component-table">
|
||||
<table class="component-table wa-hover-rows">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="table-name">Name</th>
|
||||
@@ -122,7 +139,7 @@
|
||||
<p>Learn more about <a href="/docs/usage/#events">events</a>.</p>
|
||||
|
||||
<wa-scroller>
|
||||
<table class="component-table">
|
||||
<table class="component-table wa-hover-rows">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="table-name">Name</th>
|
||||
@@ -149,7 +166,7 @@
|
||||
<p>Learn more about <a href="/docs/customizing/#custom-properties">CSS custom properties</a>.</p>
|
||||
|
||||
<wa-scroller>
|
||||
<table class="component-table">
|
||||
<table class="component-table wa-hover-rows">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="table-name">Name</th>
|
||||
@@ -181,7 +198,7 @@
|
||||
<p>Learn more about <a href="/docs/customizing/#custom-states">custom states</a>.</p>
|
||||
|
||||
<wa-scroller>
|
||||
<table class="component-table">
|
||||
<table class="component-table wa-hover-rows">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="table-name">Name</th>
|
||||
@@ -208,7 +225,7 @@
|
||||
<p>Learn more about <a href="/docs/customizing/#css-parts">CSS parts</a>.</p>
|
||||
|
||||
<wa-scroller>
|
||||
<table class="component-table">
|
||||
<table class="component-table wa-hover-rows">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="table-name">Name</th>
|
||||
@@ -247,6 +264,9 @@
|
||||
The <a href="/docs/#quick-start-autoloading-via-cdn">autoloader</a> is the recommended way to import components. If you prefer to do it manually, use one of the following code snippets.
|
||||
</p>
|
||||
|
||||
|
||||
{% set componentName = component.tagName | stripPrefix %}
|
||||
{% set componentPath = ["components/", componentName, "/", componentName, ".js"] | join("") %}
|
||||
<wa-tab-group label="How would you like to import this component?">
|
||||
<wa-tab panel="cdn">CDN</wa-tab>
|
||||
<wa-tab panel="npm">npm</wa-tab>
|
||||
@@ -255,19 +275,20 @@
|
||||
<p>
|
||||
To manually import this component from the CDN, use the following code.
|
||||
</p>
|
||||
<pre><code class="language-js">import '{% cdnUrl component.path %}';</code></pre>
|
||||
|
||||
<pre><code class="language-js">import '{% cdnUrl componentPath %}';</code></pre>
|
||||
</wa-tab-panel>
|
||||
<wa-tab-panel name="npm">
|
||||
Coming soon!
|
||||
<p>
|
||||
To manually import this component from NPM, use the following code.
|
||||
</p>
|
||||
<pre><code class="language-js">import '@awesome.me/webawesome/dist/{{ componentPath }}';</code></pre>
|
||||
</wa-tab-panel>
|
||||
<wa-tab-panel name="react">
|
||||
Coming soon!
|
||||
{# NOTE - disabled for alpha
|
||||
<p>
|
||||
To manually import this component from React, use the following code.
|
||||
</p>
|
||||
<pre><code class="language-js">import '@shoelace-style/webawesome/react/{{ component.tagName | stripPrefix }}';</code></pre>
|
||||
#}
|
||||
<pre><code class="language-js">import {{ component.name }} from '@awesome.me/webawesome/dist/react/{{ componentName }}';</code></pre>
|
||||
</wa-tab-panel>
|
||||
</wa-tab-group>
|
||||
|
||||
@@ -275,11 +296,11 @@
|
||||
|
||||
<div class="component-help">
|
||||
<strong>Need a hand?</strong>
|
||||
<wa-button size="small" appearance="filled" variant="neutral" href="https://github.com/shoelace-style/webawesome-alpha/issues" target="_blank">
|
||||
<wa-button size="small" appearance="filled" variant="neutral" href="https://github.com/shoelace-style/webawesome/issues" target="_blank">
|
||||
<wa-icon slot="prefix" name="bug"></wa-icon>
|
||||
Report a bug
|
||||
</wa-button>
|
||||
<wa-button size="small" appearance="filled" variant="neutral" href="https://github.com/shoelace-style/webawesome-alpha/discussions" target="_blank">
|
||||
<wa-button size="small" appearance="filled" variant="neutral" href="https://github.com/shoelace-style/webawesome/discussions" target="_blank">
|
||||
<wa-icon slot="prefix" name="message-question"></wa-icon>
|
||||
Ask for help
|
||||
</wa-button>
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
{% extends '../_layouts/block.njk' %}
|
||||
|
||||
{# Component header #}
|
||||
{% block notes %}
|
||||
{% if component %}
|
||||
<wa-callout variant="success">
|
||||
<wa-icon slot="icon" name="lightbulb" variant="regular"></wa-icon>
|
||||
Want to do more?
|
||||
Check out the {% for name in (component | toList) -%}
|
||||
{{ ' and ' if loop.last and not loop.first }}<a href="/docs/components/{{ name }}"><code><wa-{{ name }}></code></a>{{ ', ' if not loop.last }}
|
||||
{%- endfor %} component{{ 's' if (component | isList) }}</a>!
|
||||
</wa-callout>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block afterContent %}
|
||||
{% if file %}
|
||||
{% markdown %}
|
||||
## Opting In to Native {{ title }} Styles
|
||||
|
||||
If you want to use the Native {{ title }} styles **without including the entirety of Web Awesome Native Styles**,
|
||||
you can include the following CSS files from the Web Awesome CDN.
|
||||
|
||||
{% set stylesheet = file %}
|
||||
{% include 'import-stylesheet-code.md.njk' %}
|
||||
|
||||
To use all of Web Awesome Native styles, follow the [instructions on the Native Styles overview page](../).
|
||||
|
||||
{% endmarkdown %}
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
||||
@@ -1,26 +0,0 @@
|
||||
---
|
||||
layout: page-outline
|
||||
---
|
||||
{% set forTag = forTag or (page.url | split('/') | last) %}
|
||||
{% if description %}
|
||||
<div class="index-summary">{{ description | markdown | safe }}</div>
|
||||
{% endif %}
|
||||
|
||||
<div id="block-filter">
|
||||
<wa-input type="search" placeholder="Search {{ title }}" with-clear autofocus>
|
||||
<wa-icon slot="prefix" name="search"></wa-icon>
|
||||
</wa-input>
|
||||
</div>
|
||||
|
||||
{% set allPages = allPages or collections[forTag] %}
|
||||
{% if allPages and allPages.length > 0 %}
|
||||
{% include "grouped-pages.njk" %}
|
||||
{% endif %}
|
||||
|
||||
<link href="/assets/styles/filter.css" rel="stylesheet">
|
||||
<script type="module" src="/assets/scripts/filter.js"></script>
|
||||
|
||||
{% if content | trim %}
|
||||
<wa-divider style="--spacing: var(--wa-space-3xl)"></wa-divider> {# Temp fix for spacing issue #}
|
||||
{{ content | safe }}
|
||||
{% endif %}
|
||||
@@ -1,309 +0,0 @@
|
||||
{% set hasSidebar = true %}
|
||||
{% set hasOutline = true %}
|
||||
{% set paletteId = page.fileSlug %}
|
||||
{% set tints = ["95", "90", "80", "70", "60", "50", "40", "30", "20", "10", "05"] %}
|
||||
|
||||
{% extends '../_includes/base.njk' %}
|
||||
|
||||
{% block head %}
|
||||
<style>@import url('/dist/styles/color/{{ paletteId }}.css') layer(palette.{{ paletteId }});</style>
|
||||
<link href="{{ page.url }}../tweak.css" rel="stylesheet">
|
||||
<script type="module" src="{{ page.url }}../tweak.js"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block header %}
|
||||
<div id="palette-app" data-palette-id="{{ paletteId }}">
|
||||
<div
|
||||
:class="{
|
||||
tweaking: tweaking.chroma,
|
||||
'tweaking-chroma': tweaking.chroma,
|
||||
'tweaking-hue': tweaking.chroma,
|
||||
'tweaking-gray-chroma': tweaking.grayChroma,
|
||||
'tweaked-chroma': tweaked?.chroma,
|
||||
'tweaked-hue': tweaked?.hue,
|
||||
'tweaked-any': tweaked
|
||||
}"
|
||||
:style="{
|
||||
'--chroma-scale': chromaScale,
|
||||
'--gray-chroma': tweaked?.grayChroma ? grayChroma : '',
|
||||
}">
|
||||
|
||||
{% include 'breadcrumbs.njk' %}
|
||||
|
||||
<h1 class="title">
|
||||
<span v-content="title">{{ title }}</span>
|
||||
<template v-if="saved || tweaked">
|
||||
<wa-icon-button name="pencil" label="Rename palette" @click="rename"></wa-icon-button>
|
||||
<wa-icon-button v-if="saved" class="delete" name="trash" label="Delete palette" @click="deleteSaved"></wa-icon-button>
|
||||
<wa-button @click="save()" :disabled="!unsavedChanges"
|
||||
:variant="unsavedChanges ? 'success' : 'neutral'" size="small" :appearance="unsavedChanges ? 'accent' : 'outlined'">
|
||||
<span slot="prefix" class="icon-modifier">
|
||||
<wa-icon name="sidebar" variant="regular"></wa-icon>
|
||||
<wa-icon name="circle-plus" class="modifier" style="color: light-dark(var(--wa-color-green-70), var(--wa-color-green-60));"></wa-icon>
|
||||
</span>
|
||||
<span v-content="unsavedChanges ? 'Save' : 'Saved'">Save</span>
|
||||
</wa-button>
|
||||
</template>
|
||||
</h1>
|
||||
|
||||
<div class="block-info">
|
||||
<code class="class">.wa-palette-{{ paletteId }}</code>
|
||||
{% include '../_includes/status.njk' %}
|
||||
{% if not isPro %}
|
||||
<wa-badge class="pro" v-if="tweaked">PRO</wa-badge>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if description %}
|
||||
<p class="summary">
|
||||
{{ description | inlineMarkdown | safe }}
|
||||
</p>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block afterContent %}
|
||||
|
||||
{% set maxChroma = 0 %}
|
||||
|
||||
<wa-callout size="small" class="tweaked-callout" variant="warning">
|
||||
<wa-icon name="sliders-simple" slot="icon" variant="regular"></wa-icon>
|
||||
This palette has been tweaked.
|
||||
<div class="wa-cluster wa-gap-xs">
|
||||
<wa-tag v-for="tweakHumanReadable, param in tweaksHumanReadable" with-remove @wa-remove="reset(param)" v-content="tweakHumanReadable"></wa-tag>
|
||||
</div>
|
||||
|
||||
<wa-button @click="reset()" appearance="outlined" variant="danger">
|
||||
<span slot="prefix" class="icon-modifier">
|
||||
<wa-icon name="circle-xmark" variant="regular"></wa-icon>
|
||||
</span>
|
||||
Reset
|
||||
</wa-button>
|
||||
</wa-callout>
|
||||
|
||||
<table class="colors main wa-palette-{{ paletteId }}">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th class="core-column">Core tint</th>
|
||||
{% for tint in tints -%}
|
||||
<th>{{ tint }}</th>
|
||||
{%- endfor %}
|
||||
</tr>
|
||||
</thead>
|
||||
{# Initialize to last hue before gray #}
|
||||
{%- set hueBefore = hues[hues|length - 2] -%}
|
||||
{% for hue in hues -%}
|
||||
{% set coreTint = palettes[paletteId][hue].maxChromaTint %}
|
||||
{%- set coreColor = palettes[paletteId][hue][coreTint] -%}
|
||||
{%- set maxChroma = coreColor.c if coreColor.c > maxChroma else maxChroma -%}
|
||||
{% if hue === 'gray' %}
|
||||
<tr data-hue="{{ hue }}" class="color-scale"
|
||||
:class="{tweaking: tweaking.grayChroma, tweaked: tweaked.grayChroma || tweaked.grayColor }">
|
||||
{% else %}
|
||||
<tr data-hue="{{ hue }}" class="color-scale"
|
||||
:class="{tweaking: tweaking.{{ hue }}, tweaked: hueShifts.{{ hue }} }"
|
||||
:style="{ '--hue-shift': hueShifts.{{ hue }} || '' }">
|
||||
{% endif %}
|
||||
<th>
|
||||
{{ hue | capitalize }}
|
||||
</th>
|
||||
<td class="core-column"
|
||||
style="--color: var(--wa-color-{{ hue }})"
|
||||
:style="{
|
||||
'--color-tweaked': colors.{{ hue }}[{{ coreTint }}],
|
||||
'--color-gray-undertone': colors[grayColor][{{coreTint}}],
|
||||
'--color-tweaked-no-gray-chroma': colorsMinusGrayChroma.{{ hue }}[{{ coreTint }}],
|
||||
}">
|
||||
<wa-dropdown>
|
||||
<div slot="trigger" id="core-{{ hue }}-swatch" data-tint="core" class="color swatch"
|
||||
style="background-color: var(--wa-color-{{ hue }}); color: var(--wa-color-{{ hue }}-{{ '05' if palettes[paletteId][hue].maxChromaTint > 60 else '95' }});"
|
||||
>
|
||||
{{ palettes[paletteId][hue].maxChromaTint }}
|
||||
<wa-icon name="sliders-simple" class="tweak-icon"></wa-icon>
|
||||
</div>
|
||||
<div class="popup">
|
||||
{% if hue === 'gray' %}
|
||||
<swatch-select label="Gray undertone" shape="circle" :values="hues" v-model="grayColor"></swatch-select>
|
||||
|
||||
<div class="decorated-slider gray-chroma-slider" :style="{'--max': maxGrayChroma}">
|
||||
<wa-slider name="gray-chroma" v-model="grayChroma" ref="grayChromaSlider"
|
||||
value="0" min="0" :max="maxGrayChroma" step="0.01"
|
||||
@input="tweaking.grayChroma = true" @change="tweaking.grayChroma = false">
|
||||
<div slot="label">
|
||||
Gray colorfulness
|
||||
<wa-icon-button @click="grayChroma = originalGrayChroma" class="clear-button" name="circle-xmark" library="system" variant="regular" label="Reset"></wa-icon-button>
|
||||
</div>
|
||||
</wa-slider>
|
||||
<div class="label-min">Neutral</div>
|
||||
<div class="label-max" v-content="moreHue[grayColor]">Warmer/Cooler</div>
|
||||
</div>
|
||||
{% else %}
|
||||
{%- set hueAfter = hues[loop.index0 + 1] -%}
|
||||
{%- set hueAfter = hues[0] if hueAfter == 'gray' else hueAfter -%}
|
||||
{%- set minShift = hueRanges[hue].min - coreColor.h | round -%}
|
||||
{%- set maxShift = hueRanges[hue].max - coreColor.h | round -%}
|
||||
|
||||
<div class="decorated-slider hue-shift-slider" style="--min: {{ minShift }}; --max: {{ maxShift }};">
|
||||
<wa-slider name="{{ hue }}-shift" v-model="hueShifts.{{ hue }}" value="0"
|
||||
min="{{ minShift }}" max="{{ maxShift }}" step="1"
|
||||
@input="tweaking.hue = tweaking.{{hue}} = true"
|
||||
@change="tweaking.hue = tweaking.{{ hue }} = false">
|
||||
<div slot="label">
|
||||
Tweak {{ hue }} hue
|
||||
<wa-icon-button @click="hueShifts.{{ hue }} = 0" class="clear-button" name="circle-xmark" library="system" variant="regular" label="Reset"></wa-icon-button>
|
||||
</div>
|
||||
</wa-slider>
|
||||
<div class="label-min">More {{hueBefore}}</div>
|
||||
<div class="label-max">More {{hueAfter}}</div>
|
||||
</div>
|
||||
{%- set hueBefore = hue -%}
|
||||
{% endif %}
|
||||
<div class="wa-gap-s">
|
||||
<code>--wa-color-{{ hue }}</code>
|
||||
<wa-copy-button value="--wa-color-{{ hue }}" copy-label="--wa-color-{{ hue }}"></wa-copy-button>
|
||||
</div>
|
||||
</div>`
|
||||
</wa-dropdown>
|
||||
</td>
|
||||
{% for tint in tints -%}
|
||||
{%- set color = palettes[paletteId][hue][tint] -%}
|
||||
<td data-tint="{{ tint }}" style="--color: var(--wa-color-{{ hue }}-{{ tint }})"
|
||||
:style="{
|
||||
'--color-tweaked': colors.{{ hue }}[{{ tint }}],
|
||||
'--color-tweaked-no-gray-chroma': colorsMinusGrayChroma.{{ hue }}[{{ tint }}],
|
||||
}">
|
||||
<div class="color swatch" style="--color: var(--wa-color-{{ hue }}-{{ tint }})">
|
||||
<wa-copy-button value="--wa-color-{{ hue }}-{{ tint }}" copy-label="--wa-color-{{ hue }}-{{ tint }}"></wa-copy-button>
|
||||
</div>
|
||||
</td>
|
||||
{%- endfor -%}
|
||||
</tr>
|
||||
{%- endfor %}
|
||||
</table>
|
||||
|
||||
{% set chromaScaleBounds = [
|
||||
(0.08 / maxChroma) | number({maximumFractionDigits: 2}),
|
||||
(0.3 / maxChroma]) | number({maximumFractionDigits: 2}) -%}
|
||||
<div class="decorated-slider chroma-scale-slider wa-palette-{{ paletteId }}"
|
||||
:class="{ tweaked: chromaScale !== 1 }"
|
||||
style="--min: {{ chromaScaleBounds[0] }}; --max: {{ chromaScaleBounds[1] }};">
|
||||
<wa-slider name="chroma-scale" ref="chromaScaleSlider"
|
||||
v-model="chromaScale" value="1" step="0.01"
|
||||
min="{{ chromaScaleBounds[0] }}" max="{{ chromaScaleBounds[1] }}"
|
||||
@input="tweaking.chroma = true"
|
||||
@change="tweaking.chroma = false">
|
||||
<div slot="label">
|
||||
Overall colorfulness
|
||||
<wa-icon-button @click="chromaScale = 1" class="clear-button" name="circle-xmark" library="system" variant="regular" label="Reset"></wa-icon-button>
|
||||
</div>
|
||||
</wa-slider>
|
||||
<div class="label-min">More muted</div>
|
||||
<div class="label-max">More vibrant</div>
|
||||
</div>
|
||||
|
||||
<h2>Used By</h2>
|
||||
|
||||
<section class="index-grid">
|
||||
{% for page in collections.theme %}
|
||||
{%- if page.data.palette == paletteId -%}
|
||||
{% include "page-card.njk" %}
|
||||
{%- endif -%}
|
||||
{% endfor %}
|
||||
</section>
|
||||
|
||||
{% markdown %}
|
||||
## Color Contrast
|
||||
|
||||
Web Awesome color scales are designed to guarantee certain contrast ratios,
|
||||
both per [WCAG 2.1 success criteria](https://www.w3.org/TR/WCAG21/#contrast-minimum)
|
||||
as well as the emergent APCA specification _(planned)_,
|
||||
so you can ensure that text is both legible to all users, and legally conformant.
|
||||
|
||||
### Level 1
|
||||
|
||||
A difference of `40` ensures a minimum **3:1** contrast ratio, suitable for large text and icons (AA).
|
||||
{% endmarkdown %}
|
||||
|
||||
{% set difference = 40 %}
|
||||
{% set minContrast = 3 %}
|
||||
{% include "contrast-table.njk" %}
|
||||
|
||||
{% markdown %}
|
||||
|
||||
This also goes for a difference of `45`:
|
||||
|
||||
{% endmarkdown %}
|
||||
|
||||
{% set difference = 45 %}
|
||||
{% include "contrast-table.njk" %}
|
||||
|
||||
{% markdown %}
|
||||
|
||||
### Level 2
|
||||
|
||||
A difference of `50` ensures a minimum **4.5:1** contrast ratio, suitable for normal text (AA) and large text (AAA)
|
||||
{% endmarkdown %}
|
||||
|
||||
{% set difference = 50 %}
|
||||
{% set minContrast = 4.5 %}
|
||||
{% include "contrast-table.njk" %}
|
||||
|
||||
{% markdown %}
|
||||
|
||||
This also goes for a difference of `55`:
|
||||
|
||||
{% endmarkdown %}
|
||||
|
||||
{% set difference = 55 %}
|
||||
{% include "contrast-table.njk" %}
|
||||
|
||||
{% markdown %}
|
||||
### Level 3
|
||||
|
||||
A difference of `60` ensures a minimum **7:1** contrast ratio, suitable for all text (AAA)
|
||||
{% endmarkdown %}
|
||||
|
||||
{% set difference = 60 %}
|
||||
{% set minContrast = 7 %}
|
||||
{% include "contrast-table.njk" %}
|
||||
|
||||
{% markdown %}
|
||||
|
||||
This also goes for a difference of `65`:
|
||||
|
||||
{% endmarkdown %}
|
||||
|
||||
{% set difference = 65 %}
|
||||
{% include "contrast-table.njk" %}
|
||||
|
||||
{% markdown %}
|
||||
## How to use this palette { #usage }
|
||||
|
||||
If you are using a Web Awesome theme that uses this palette, it will already be included.
|
||||
To use a different palette than a theme default, or to use it in a custom theme, you can import this palette directly from the Web Awesome CDN.
|
||||
|
||||
{% set stylesheet = 'styles/color/' + page.fileSlug + '.css' %}
|
||||
<wa-tab-group class="import-stylesheet-code">
|
||||
<wa-tab panel="html">In HTML</wa-tab>
|
||||
<wa-tab panel="css">In CSS</wa-tab>
|
||||
<wa-tab-panel name="html">
|
||||
|
||||
Add the following code to the `<head>` of your page:
|
||||
```html { v-content:html="code.html.highlighted" }
|
||||
<link rel="stylesheet" href="{% cdnUrl stylesheet %}" />
|
||||
```
|
||||
</wa-tab-panel>
|
||||
<wa-tab-panel name="css">
|
||||
|
||||
Add the following code at the top of your CSS file:
|
||||
```css { v-content:html="code.css.highlighted" }
|
||||
@import url('{% cdnUrl stylesheet %}');
|
||||
```
|
||||
</wa-tab-panel>
|
||||
</wa-tab-group>
|
||||
|
||||
|
||||
{% endmarkdown %}
|
||||
</div></div> {# end palette app #}
|
||||
{% 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" %}
|
||||
@@ -1 +0,0 @@
|
||||
{% extends '../_layouts/block.njk' %}
|
||||
@@ -1,185 +0,0 @@
|
||||
{% set hasSidebar = true %}
|
||||
{% set hasOutline = true %}
|
||||
{# {% set forceTheme = page.fileSlug %} #}
|
||||
|
||||
{% extends '../_includes/base.njk' %}
|
||||
|
||||
{% block head %}
|
||||
<link href="{{ page.url }}../remix.css" rel="stylesheet">
|
||||
<script type="module" src="{{ page.url }}../edit/index.js"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block header %}
|
||||
<script>
|
||||
if (location.pathname.endsWith('/custom/') && !location.search) {
|
||||
location.href = "../edit/";
|
||||
}
|
||||
</script>
|
||||
<div id="theme-app" data-theme-id="{{ page.fileSlug }}">
|
||||
|
||||
<iframe ref="preview" :src="'{{ page.url }}demo.html' + urlParams" src='{{ page.url }}demo.html' id="demo"></iframe>
|
||||
|
||||
{% if page.fileSlug !== 'custom' %}
|
||||
<wa-details id="mix_and_match" class="wa-gap-m" :open="saved || unsavedChanges">
|
||||
<h4 slot="summary" data-no-anchor data-no-outline id="remix">
|
||||
<wa-icon name="arrows-rotate"></wa-icon>
|
||||
Remix this theme
|
||||
<wa-icon id="what-is-remixing" href="#remixing" name="circle-question" slot="suffix" variant="regular"></wa-icon>
|
||||
<wa-tooltip for="what-is-remixing">Customize this theme by changing its colors and/or remixing it with design elements from other themes!</wa-tooltip>
|
||||
</h4>
|
||||
|
||||
<wa-select name="palette" label="Color palette" with-clear v-model="theme.palette">
|
||||
<wa-icon name="swatchbook" slot="prefix" variant="regular"></wa-icon>
|
||||
<wa-option v-for="(palette, paletteId) in palettes" :label="palette.title" :value="paletteId === baseTheme.palette ? '' : paletteId">
|
||||
<palette-card :palette="paletteId" size="small">
|
||||
<template #extra>
|
||||
<wa-badge v-if="paletteId === baseTheme.palette" variant="neutral" appearance="outlined">Theme default</wa-badge>
|
||||
</template>
|
||||
</palette-card>
|
||||
</wa-option>
|
||||
</wa-select>
|
||||
|
||||
<color-select :model-value="computed.brand" @update:model-value="value => theme.brand = value" label="Brand color"
|
||||
:values="hues"></color-select>
|
||||
|
||||
<wa-select name="colors" class="theme-colors-select" label="Color contrast from…" value="" with-clear v-model="theme.colors">
|
||||
<wa-icon name="palette" slot="prefix" variant="regular"></wa-icon>
|
||||
<template v-for="(themeMeta, themeId) in themes">
|
||||
<wa-option v-if="themeId !== 'custom'" :label="themeMeta.title" :value="themeId === computed.colors ? '' : themeId">
|
||||
<theme-card :theme="themeId" type="colors" :rest="{base: computed.base, palette: computed.palette, brand: computed.brand}" size="small">
|
||||
<template #extra>
|
||||
<wa-badge v-if="themeId === theme.base" variant="neutral" appearance="outlined">This theme</wa-badge>
|
||||
</template>
|
||||
</theme-card>
|
||||
</wa-option>
|
||||
</template>
|
||||
</wa-select>
|
||||
|
||||
<wa-select name="typography" label="Typography from…" with-clear v-model="theme.typography">
|
||||
<wa-icon name="font-case" slot="prefix"></wa-icon>
|
||||
|
||||
<wa-option v-for="(themeMeta, themeId) in themes" :label="themeMeta.title" :value="themeId === theme.base ? '' : themeId">
|
||||
<fonts-card :theme="themeId" size="small">
|
||||
<template #extra>
|
||||
<wa-badge v-if="themeId === theme.base" variant="neutral" appearance="outlined">This theme</wa-badge>
|
||||
</template>
|
||||
</fonts-card>
|
||||
</wa-option>
|
||||
</wa-select>
|
||||
</wa-details>
|
||||
{% endif %}
|
||||
<h2>Color</h2>
|
||||
|
||||
<div class="index-grid">
|
||||
{% if page.fileSlug === 'custom' %}
|
||||
<palette-card :palette="computed.palette" subtitle="Color palette"></palette-card>
|
||||
{% else %}
|
||||
{% set themePage = page %}
|
||||
{% set paletteURL = '/docs/palettes/' + palette + '/' %}
|
||||
{% set page = paletteURL | getCollectionItemFromUrl %}
|
||||
{% set pageSubtitle = "Default color palette" %}
|
||||
{% include 'page-card.njk' %}
|
||||
{% set page = themePage %}
|
||||
{% endif %}
|
||||
<wa-card class="wa-palette-{{ palette }}" style="--header-background: var(--wa-color-{{ brand }})"
|
||||
:class="`wa-palette-${computed.palette}`" :style="{'--header-background': palettes[computed.palette]?.colors[computed.brand]?.key}">
|
||||
<div slot="header"></div>
|
||||
<div class="page-name" v-content="capitalize(computed.brand)">{{ brand | capitalize }}</div>
|
||||
<div class="wa-caption-s">{{ 'Brand color' if page.fileSlug === 'custom' else 'Default brand color' }}</div>
|
||||
</wa-card>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block afterContent %}
|
||||
|
||||
<h2 id="usage">How to use this theme</h2>
|
||||
|
||||
{% markdown %}
|
||||
You can import this theme from the Web Awesome CDN.
|
||||
|
||||
{% set stylesheet = 'styles/themes/' + page.fileSlug + '.css' %}
|
||||
|
||||
<wa-tab-group class="import-stylesheet-code">
|
||||
<wa-tab panel="html">In HTML</wa-tab>
|
||||
<wa-tab panel="css">In CSS</wa-tab>
|
||||
<wa-tab-panel name="html">
|
||||
|
||||
Add the following code to the `<head>` of your page:
|
||||
```html { v-content:html="code.html.highlighted" }
|
||||
<link rel="stylesheet" href="{% cdnUrl stylesheet %}" />
|
||||
```
|
||||
</wa-tab-panel>
|
||||
<wa-tab-panel name="css">
|
||||
|
||||
Add the following code at the top of your CSS file:
|
||||
```css { v-content:html="code.css.highlighted" }
|
||||
@import url('{% cdnUrl stylesheet %}');
|
||||
```
|
||||
</wa-tab-panel>
|
||||
</wa-tab-group>
|
||||
|
||||
## Dark mode
|
||||
|
||||
To activate the dark color scheme of the theme on any element and its contents, apply the class `wa-dark` to it.
|
||||
This means you can use different color schemes throughout the page.
|
||||
Here, we use the default theme with a dark sidebar:
|
||||
|
||||
```html
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="path/to/web-awesome/dist/styles/themes/default.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<nav class="wa-dark">
|
||||
<!-- dark-themed sidebar -->
|
||||
</nav>
|
||||
|
||||
<!-- light-themed content -->
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
You can apply the class to the `<html>` element on your page to activate the dark color scheme for the entire page.
|
||||
|
||||
```html
|
||||
<html class="wa-dark">
|
||||
<head>
|
||||
<link rel="stylesheet" href="path/to/web-awesome/dist/styles/themes/{{ page.fileSlug }}.css" />
|
||||
<!-- other links, scripts, and metadata -->
|
||||
</head>
|
||||
<body>
|
||||
<!-- page content -->
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
### Detecting Color Scheme Preference
|
||||
|
||||
Web Awesome's themes have both light and dark styles built in.
|
||||
However, Web Awesome doesn't try to auto-detect the user's light/dark mode preference.
|
||||
This should be done at the application level.
|
||||
|
||||
As a best practice, to provide a dark theme in your app, you should:
|
||||
|
||||
- Check for [`prefers-color-scheme`](https://stackoverflow.com/a/57795495/567486) and use its value by default
|
||||
- Allow the user to override the setting in your app
|
||||
- Remember the user's preference and restore it on subsequent logins
|
||||
|
||||
Web Awesome avoids using the `prefers-color-scheme` media query because not all apps support dark mode, and it would break things for the ones that don't.
|
||||
|
||||
Assuming the user's preference is in a variable called `colorScheme` (values: `auto`, `light`, `dark`),
|
||||
you can use the following JS snippet to apply the `wa-dark` class to the `<html>` element accordingly:
|
||||
|
||||
```js
|
||||
const systemDark = window.matchMedia('(prefers-color-scheme: dark)');
|
||||
const applyDark = function (event = systemDark) {
|
||||
const isDark = colorScheme === 'auto' ? event.matches : colorScheme === 'dark';
|
||||
document.documentElement.classList.toggle('wa-dark', isDark);
|
||||
};
|
||||
systemDark.addEventListener('change', applyDark);
|
||||
applyDark();
|
||||
```
|
||||
|
||||
</div> {# end theme app #}
|
||||
{% endmarkdown %}
|
||||
{% endblock %}
|
||||
@@ -1,19 +0,0 @@
|
||||
{% extends '../_layouts/block.njk' %}
|
||||
|
||||
{% block afterContent %}
|
||||
{% if file %}
|
||||
{% markdown %}
|
||||
## Opting In
|
||||
|
||||
If you want to use this utility **only** without [all others](../), you can include the following CSS file from the Web Awesome CDN.
|
||||
|
||||
{% set stylesheet = file %}
|
||||
{% include 'import-stylesheet-code.md.njk' %}
|
||||
|
||||
Want them all?
|
||||
Follow the [instructions on the Utilities overview page](../) to get all Web Awesome utilities.
|
||||
|
||||
{% endmarkdown %}
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
||||
@@ -1,4 +1,5 @@
|
||||
/* eslint-disable no-invalid-this */
|
||||
import { readFileSync } from 'fs';
|
||||
import { mkdir, writeFile } from 'fs/promises';
|
||||
import lunr from 'lunr';
|
||||
import { parse } from 'node-html-parser';
|
||||
@@ -23,19 +24,22 @@ export function searchPlugin(options = {}) {
|
||||
...options,
|
||||
};
|
||||
|
||||
// Hoist above so that it can "cache" properly for incremental builds.
|
||||
return function (eleventyConfig) {
|
||||
const pagesToIndex = new Map();
|
||||
let pagesToIndex = new Map();
|
||||
|
||||
eleventyConfig.addPreprocessor('exclude-unlisted-from-search', '*', function (data, content) {
|
||||
if (data.unlisted) {
|
||||
// no-op
|
||||
pagesToIndex.delete(data.page.inputPath);
|
||||
} else {
|
||||
pagesToIndex.set(data.page.inputPath, {});
|
||||
pagesToIndex.set(data.page.inputPath, true);
|
||||
}
|
||||
|
||||
return content;
|
||||
});
|
||||
|
||||
// With incremental builds we need this to be last in case stuff was added from metadata. _BUT_ in incremental builds, not every page is added to the "transform".
|
||||
eleventyConfig.addTransform('search', function (content) {
|
||||
if (!pagesToIndex.has(this.page.inputPath)) {
|
||||
return content;
|
||||
@@ -67,11 +71,30 @@ export function searchPlugin(options = {}) {
|
||||
return content;
|
||||
});
|
||||
|
||||
eleventyConfig.on('eleventy.after', ({ directories }) => {
|
||||
eleventyConfig.on('eleventy.after', async ({ directories }) => {
|
||||
const { output } = directories;
|
||||
const outputFilename = path.resolve(join(output, 'search.json'));
|
||||
const cachedPages = path.resolve(join(output, 'cached_pages.json'));
|
||||
|
||||
function getCachedPages() {
|
||||
let content = { pages: [] };
|
||||
try {
|
||||
content = JSON.parse(readFileSync(cachedPages));
|
||||
} catch (e) {}
|
||||
|
||||
const cachedPagesMap = new Map(content.pages);
|
||||
for (const [key, value] of cachedPagesMap.entries()) {
|
||||
// A page uses a cached value if `true` and it didnt get its value set in the "transform" hook. This is to get around the limitation of incremental builds not going over every file in transform.
|
||||
if (pagesToIndex.get(key) === true) {
|
||||
pagesToIndex.set(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const map = [];
|
||||
const searchIndex = lunr(async function () {
|
||||
|
||||
getCachedPages();
|
||||
const searchIndex = lunr(function () {
|
||||
let index = 0;
|
||||
|
||||
this.ref('id');
|
||||
@@ -84,9 +107,11 @@ export function searchPlugin(options = {}) {
|
||||
map[index] = { title: page.title, description: page.description, url: page.url };
|
||||
index++;
|
||||
}
|
||||
await mkdir(dirname(outputFilename), { recursive: true });
|
||||
await writeFile(outputFilename, JSON.stringify({ searchIndex, map }), 'utf-8');
|
||||
});
|
||||
|
||||
await mkdir(dirname(outputFilename), { recursive: true });
|
||||
await writeFile(outputFilename, JSON.stringify({ searchIndex, map }), 'utf-8');
|
||||
await writeFile(cachedPages, JSON.stringify({ pages: [...pagesToIndex.entries()] }, null, 2));
|
||||
});
|
||||
};
|
||||
}
|
||||
81
packages/webawesome/docs/_transformers/anchor-headings.js
Normal file
81
packages/webawesome/docs/_transformers/anchor-headings.js
Normal file
@@ -0,0 +1,81 @@
|
||||
import { parse } from 'node-html-parser';
|
||||
import slugify from 'slugify';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
|
||||
function createId(text) {
|
||||
let slug = slugify(String(text), {
|
||||
remove: /[^\w|\s]/g,
|
||||
lower: true,
|
||||
});
|
||||
|
||||
// ids must start with a letter
|
||||
if (!/^[a-z]/i.test(slug)) {
|
||||
slug = `wa_${slug}`;
|
||||
}
|
||||
|
||||
return slug;
|
||||
}
|
||||
|
||||
/**
|
||||
* Eleventy plugin to add anchors to headings to content.
|
||||
*/
|
||||
export function anchorHeadingsTransformer(options = {}) {
|
||||
options = {
|
||||
container: 'body',
|
||||
headingSelector: 'h2, h3, h4, h5, h6',
|
||||
anchorLabel: 'Jump to heading',
|
||||
...options,
|
||||
};
|
||||
|
||||
/** doc is a parsed HTML document */
|
||||
return function (doc) {
|
||||
const container = doc.querySelector(options.container);
|
||||
|
||||
if (!container) {
|
||||
return doc;
|
||||
}
|
||||
|
||||
// Look for headings
|
||||
let selector = `:is(${options.headingSelector}):not([data-no-anchor], [data-no-anchor] *)`;
|
||||
container.querySelectorAll(selector).forEach(heading => {
|
||||
const hasAnchor = heading.querySelector('a');
|
||||
const existingId = heading.getAttribute('id');
|
||||
const clone = parse(heading.outerHTML);
|
||||
|
||||
// Create a clone of the heading so we can remove [data-no-anchor] elements from the text content
|
||||
clone.querySelectorAll('[data-no-anchor]').forEach(el => el.remove());
|
||||
|
||||
if (hasAnchor) {
|
||||
return;
|
||||
}
|
||||
|
||||
let id = existingId;
|
||||
if (!id) {
|
||||
const slug = createId(clone.textContent ?? '') ?? uuid().slice(-12);
|
||||
id = slug;
|
||||
let suffix = 1;
|
||||
|
||||
// Make sure the slug is unique in the document
|
||||
while (doc.getElementById(id) !== null) {
|
||||
id = `${slug}-${++suffix}`;
|
||||
}
|
||||
}
|
||||
|
||||
// Create the anchor
|
||||
const anchor = parse(`
|
||||
<a href="#${encodeURIComponent(id)}">
|
||||
<span class="wa-visually-hidden"></span>
|
||||
<span aria-hidden="true">#</span>
|
||||
</a>
|
||||
`);
|
||||
anchor.querySelector('.wa-visually-hidden').textContent = options.anchorLabel;
|
||||
|
||||
// Update the heading
|
||||
if (!existingId) {
|
||||
heading.setAttribute('id', id);
|
||||
}
|
||||
heading.classList.add('anchor-heading');
|
||||
heading.appendChild(anchor);
|
||||
});
|
||||
};
|
||||
}
|
||||
91
packages/webawesome/docs/_transformers/code-examples.js
Normal file
91
packages/webawesome/docs/_transformers/code-examples.js
Normal file
@@ -0,0 +1,91 @@
|
||||
import { parse } from 'node-html-parser';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import { copyCode } from './copy-code.js';
|
||||
import { highlightCode } from './highlight-code.js';
|
||||
|
||||
/**
|
||||
* Eleventy plugin to turn `<code class="example">` blocks into live examples.
|
||||
*/
|
||||
export function codeExamplesTransformer(options = {}) {
|
||||
options = {
|
||||
container: 'body',
|
||||
...options,
|
||||
};
|
||||
|
||||
return function (doc) {
|
||||
const container = doc.querySelector(options.container);
|
||||
|
||||
if (!container) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Look for external links
|
||||
container.querySelectorAll('code.example').forEach(code => {
|
||||
let pre = code.closest('pre');
|
||||
const hasButtons = !code.classList.contains('no-buttons');
|
||||
const isOpen = code.classList.contains('open') || !hasButtons;
|
||||
const noEdit = code.classList.contains('no-edit');
|
||||
const id = `code-example-${uuid().slice(-12)}`;
|
||||
let preview = pre.textContent;
|
||||
|
||||
const langClass = [...code.classList.values()].find(val => val.startsWith('language-'));
|
||||
const lang = langClass ? langClass.replace(/^language-/, '') : 'plain';
|
||||
|
||||
code.innerHTML = highlightCode(code.textContent ?? '', lang);
|
||||
|
||||
// Run preview scripts as modules to prevent collisions
|
||||
const root = parse(preview, { blockTextElements: { script: true } });
|
||||
root.querySelectorAll('script').forEach(script => script.setAttribute('type', 'module'));
|
||||
preview = root.toString();
|
||||
|
||||
copyCode(code);
|
||||
|
||||
const codeExample = parse(`
|
||||
<div class="code-example ${isOpen ? 'open' : ''}">
|
||||
<div class="code-example-preview">
|
||||
<div>
|
||||
${preview}
|
||||
</div>
|
||||
<div class="code-example-resizer" aria-hidden="true">
|
||||
<wa-icon name="grip-lines-vertical"></wa-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="code-example-source" id="${id}">
|
||||
${pre.outerHTML}
|
||||
</div>
|
||||
${
|
||||
hasButtons
|
||||
? `
|
||||
<div class="code-example-buttons">
|
||||
<button
|
||||
class="code-example-toggle"
|
||||
type="button"
|
||||
aria-expanded="${isOpen ? 'true' : 'false'}"
|
||||
aria-controls="${id}"
|
||||
>
|
||||
Code
|
||||
<wa-icon name="chevron-down"></wa-icon>
|
||||
</button>
|
||||
|
||||
${
|
||||
noEdit
|
||||
? ''
|
||||
: `
|
||||
<button class="code-example-pen" type="button">
|
||||
<wa-icon name="pen-to-square"></wa-icon>
|
||||
Edit
|
||||
</button>
|
||||
`
|
||||
}
|
||||
|
||||
`
|
||||
: ''
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
`);
|
||||
|
||||
pre.replaceWith(codeExample);
|
||||
});
|
||||
};
|
||||
}
|
||||
38
packages/webawesome/docs/_transformers/copy-code.js
Normal file
38
packages/webawesome/docs/_transformers/copy-code.js
Normal file
@@ -0,0 +1,38 @@
|
||||
export function copyCode(code) {
|
||||
const pre = code.closest('pre');
|
||||
let preId = pre.getAttribute('id') || `code-block-${crypto.randomUUID()}`;
|
||||
let codeId = code.getAttribute('id') || `${preId}-inner`;
|
||||
|
||||
if (!code.getAttribute('id')) {
|
||||
code.setAttribute('id', codeId);
|
||||
}
|
||||
if (!pre.getAttribute('id')) {
|
||||
pre.setAttribute('id', preId);
|
||||
}
|
||||
|
||||
// Add a copy button
|
||||
pre.innerHTML += `<wa-copy-button from="${codeId}" class="copy-button wa-dark"></wa-copy-button>`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Eleventy plugin to add copy buttons to code blocks.
|
||||
*/
|
||||
export function copyCodeTransformer(options = {}) {
|
||||
options = {
|
||||
container: 'body',
|
||||
...options,
|
||||
};
|
||||
|
||||
return function (doc) {
|
||||
const container = doc.querySelector(options.container);
|
||||
|
||||
if (!container) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Look for code blocks
|
||||
container.querySelectorAll('pre > code').forEach(code => {
|
||||
copyCode(code);
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -24,30 +24,25 @@ function normalize(pathname) {
|
||||
/**
|
||||
* Eleventy plugin to decorate current links with a custom class.
|
||||
*/
|
||||
export function currentLink(options = {}) {
|
||||
export function currentLinkTransformer(options = {}) {
|
||||
options = {
|
||||
container: 'body',
|
||||
className: 'current',
|
||||
...options,
|
||||
};
|
||||
|
||||
return function (eleventyConfig) {
|
||||
eleventyConfig.addTransform('current-link', function (content) {
|
||||
const doc = parse(content);
|
||||
const container = doc.querySelector(options.container);
|
||||
return function (doc) {
|
||||
const container = doc.querySelector(options.container);
|
||||
|
||||
if (!container) {
|
||||
return content;
|
||||
if (!container) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Compare the href attribute to 11ty's page URL
|
||||
container.querySelectorAll('a[href]').forEach(a => {
|
||||
if (normalize(a.getAttribute('href')) === normalize(this.page.url)) {
|
||||
a.classList.add(options.className);
|
||||
}
|
||||
|
||||
// Compare the href attribute to 11ty's page URL
|
||||
container.querySelectorAll('a[href]').forEach(a => {
|
||||
if (normalize(a.getAttribute('href')) === normalize(this.page.url)) {
|
||||
a.classList.add(options.className);
|
||||
}
|
||||
});
|
||||
|
||||
return doc.toString();
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -37,36 +37,31 @@ export function highlightCode(code, language = 'plain') {
|
||||
* Eleventy plugin to highlight code blocks with the `language-*` attribute using Prism.js. Unlike most plugins, this
|
||||
* works on the entire document — not just markdown content.
|
||||
*/
|
||||
export function highlightCodePlugin(options = {}) {
|
||||
export function highlightCodeTransformer(options = {}) {
|
||||
options = {
|
||||
container: 'body',
|
||||
...options,
|
||||
};
|
||||
|
||||
return function (eleventyConfig) {
|
||||
eleventyConfig.addTransform('highlight-code', content => {
|
||||
const doc = parse(content, { blockTextElements: { code: true } });
|
||||
const container = doc.querySelector(options.container);
|
||||
return function (doc) {
|
||||
const container = doc.querySelector(options.container);
|
||||
|
||||
if (!container) {
|
||||
return content;
|
||||
}
|
||||
if (!container) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Look for <code class="language-*"> and highlight each one
|
||||
container.querySelectorAll('code[class*="language-"]').forEach(code => {
|
||||
const langClass = [...code.classList.values()].find(val => val.startsWith('language-'));
|
||||
const lang = langClass ? langClass.replace(/^language-/, '') : 'plain';
|
||||
// Look for <code class="language-*"> and highlight each one
|
||||
container.querySelectorAll('code[class*="language-"]').forEach(code => {
|
||||
const langClass = [...code.classList.values()].find(val => val.startsWith('language-'));
|
||||
const lang = langClass ? langClass.replace(/^language-/, '') : 'plain';
|
||||
|
||||
try {
|
||||
code.innerHTML = highlightCode(code.textContent ?? '', lang);
|
||||
} catch (err) {
|
||||
if (!options.ignoreMissingLangs) {
|
||||
throw new Error(err.message);
|
||||
}
|
||||
try {
|
||||
code.innerHTML = highlightCode(code.textContent ?? '', lang);
|
||||
} catch (err) {
|
||||
if (!options.ignoreMissingLangs) {
|
||||
throw new Error(err.message);
|
||||
}
|
||||
});
|
||||
|
||||
return doc.toString();
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
64
packages/webawesome/docs/_transformers/outline.js
Normal file
64
packages/webawesome/docs/_transformers/outline.js
Normal file
@@ -0,0 +1,64 @@
|
||||
import { parse } from 'node-html-parser';
|
||||
|
||||
/**
|
||||
* Eleventy plugin to add an outline (table of contents) to the page. Headings must have an id, otherwise they won't be
|
||||
* included in the outline. An unordered list containing links will be appended to the target element.
|
||||
*
|
||||
* If no headings are found for the outline, the `ifEmpty()` function will be called with a `node-html-parser` object as
|
||||
* the first argument. This can be used to toggle classes or remove elements when the outline is empty.
|
||||
*
|
||||
* See the `node-html-parser` docs for more details: https://www.npmjs.com/package/node-html-parser
|
||||
*/
|
||||
export function outlineTransformer(options = {}) {
|
||||
options = {
|
||||
container: 'body',
|
||||
target: '.outline',
|
||||
selector: 'h2,h3',
|
||||
ifEmpty: () => null,
|
||||
...options,
|
||||
};
|
||||
|
||||
return function (doc) {
|
||||
const container = doc.querySelector(options.container);
|
||||
const ul = parse('<ul></ul>');
|
||||
let numLinks = 0;
|
||||
|
||||
if (!container) {
|
||||
return;
|
||||
}
|
||||
|
||||
container.querySelectorAll(options.selector).forEach(heading => {
|
||||
const id = heading.getAttribute('id');
|
||||
const level = heading.tagName.slice(1);
|
||||
const clone = parse(heading.outerHTML);
|
||||
|
||||
if (heading.closest('[data-no-outline]')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a clone of the heading so we can remove links and [data-no-outline] elements from the text content
|
||||
clone.querySelectorAll('.wa-visually-hidden, [hidden], [aria-hidden="true"]').forEach(el => el.remove());
|
||||
clone.querySelectorAll('[data-no-outline]').forEach(el => el.remove());
|
||||
|
||||
// Generate the link
|
||||
const li = parse(`<li data-level="${level}"><a></a></li>`);
|
||||
const a = li.querySelector('a');
|
||||
a.setAttribute('href', `#${encodeURIComponent(id)}`);
|
||||
a.textContent = clone.textContent.trim().replace(/#$/, '');
|
||||
|
||||
// Add it to the list
|
||||
ul.firstChild.appendChild(li);
|
||||
numLinks++;
|
||||
});
|
||||
|
||||
if (numLinks > 0) {
|
||||
// Append the list to all matching targets
|
||||
doc.querySelectorAll(options.target).forEach(target => {
|
||||
target.appendChild(parse(ul.outerHTML));
|
||||
});
|
||||
} else {
|
||||
// Remove if empty
|
||||
options.ifEmpty(doc);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
import { parse } from 'node-html-parser';
|
||||
import slugify from 'slugify';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
|
||||
function createId(text) {
|
||||
let slug = slugify(String(text), {
|
||||
remove: /[^\w|\s]/g,
|
||||
lower: true,
|
||||
});
|
||||
|
||||
// ids must start with a letter
|
||||
if (!/^[a-z]/i.test(slug)) {
|
||||
slug = `wa_${slug}`;
|
||||
}
|
||||
|
||||
return slug;
|
||||
}
|
||||
|
||||
/**
|
||||
* Eleventy plugin to add anchors to headings to content.
|
||||
*/
|
||||
export function anchorHeadingsPlugin(options = {}) {
|
||||
options = {
|
||||
container: 'body',
|
||||
headingSelector: 'h2, h3, h4, h5, h6',
|
||||
anchorLabel: 'Jump to heading',
|
||||
...options,
|
||||
};
|
||||
|
||||
return function (eleventyConfig) {
|
||||
eleventyConfig.addTransform('anchor-headings', content => {
|
||||
const doc = parse(content);
|
||||
const container = doc.querySelector(options.container);
|
||||
|
||||
if (!container) {
|
||||
return content;
|
||||
}
|
||||
|
||||
// Look for headings
|
||||
let selector = `:is(${options.headingSelector}):not([data-no-anchor], [data-no-anchor] *)`;
|
||||
container.querySelectorAll(selector).forEach(heading => {
|
||||
const hasAnchor = heading.querySelector('a');
|
||||
const existingId = heading.getAttribute('id');
|
||||
const clone = parse(heading.outerHTML);
|
||||
|
||||
// Create a clone of the heading so we can remove [data-no-anchor] elements from the text content
|
||||
clone.querySelectorAll('[data-no-anchor]').forEach(el => el.remove());
|
||||
|
||||
if (hasAnchor) {
|
||||
return;
|
||||
}
|
||||
|
||||
let id = existingId;
|
||||
if (!id) {
|
||||
const slug = createId(clone.textContent ?? '') ?? uuid().slice(-12);
|
||||
id = slug;
|
||||
let suffix = 1;
|
||||
|
||||
// Make sure the slug is unique in the document
|
||||
while (doc.getElementById(id) !== null) {
|
||||
id = `${slug}-${++suffix}`;
|
||||
}
|
||||
}
|
||||
|
||||
// Create the anchor
|
||||
const anchor = parse(`
|
||||
<a href="#${encodeURIComponent(id)}">
|
||||
<span class="wa-visually-hidden"></span>
|
||||
<span aria-hidden="true">#</span>
|
||||
</a>
|
||||
`);
|
||||
anchor.querySelector('.wa-visually-hidden').textContent = options.anchorLabel;
|
||||
|
||||
// Update the heading
|
||||
if (!existingId) {
|
||||
heading.setAttribute('id', id);
|
||||
}
|
||||
heading.classList.add('anchor-heading');
|
||||
heading.appendChild(anchor);
|
||||
});
|
||||
|
||||
return doc.toString();
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
import { parse } from 'node-html-parser';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import { markdown } from '../_utils/markdown.js';
|
||||
|
||||
/**
|
||||
* Eleventy plugin to turn `<code class="example">` blocks into live examples.
|
||||
*/
|
||||
export function codeExamplesPlugin(options = {}) {
|
||||
options = {
|
||||
container: 'body',
|
||||
...options,
|
||||
};
|
||||
|
||||
return function (eleventyConfig) {
|
||||
eleventyConfig.addTransform('code-examples', content => {
|
||||
const doc = parse(content, { blockTextElements: { code: true } });
|
||||
const container = doc.querySelector(options.container);
|
||||
|
||||
if (!container) {
|
||||
return content;
|
||||
}
|
||||
|
||||
// Look for external links
|
||||
container.querySelectorAll('code.example').forEach(code => {
|
||||
const pre = code.closest('pre');
|
||||
const hasButtons = !code.classList.contains('no-buttons');
|
||||
const isOpen = code.classList.contains('open') || !hasButtons;
|
||||
const isViewportDemo = code.classList.contains('viewport');
|
||||
const noEdit = code.classList.contains('no-edit');
|
||||
const id = `code-example-${uuid().slice(-12)}`;
|
||||
let preview = pre.textContent;
|
||||
|
||||
// Run preview scripts as modules to prevent collisions
|
||||
const root = parse(preview, { blockTextElements: { script: true } });
|
||||
root.querySelectorAll('script').forEach(script => script.setAttribute('type', 'module'));
|
||||
preview = root.toString();
|
||||
|
||||
const escapedHtml = markdown.utils.escapeHtml(`
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Web Awesome Demo</title>
|
||||
<link rel="stylesheet" href="https://early.webawesome.com/webawesome@[version]/dist/styles/themes/default.css" />
|
||||
<link rel="stylesheet" href="https://early.webawesome.com/webawesome@[version]/dist/styles/webawesome.css" />
|
||||
<script type="module" src="https://early.webawesome.com/webawesome@[version]/dist/webawesome.loader.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
${preview}
|
||||
</body>
|
||||
</html>
|
||||
`);
|
||||
|
||||
const codeExample = parse(`
|
||||
<div class="code-example ${isOpen ? 'open' : ''} ${isViewportDemo ? 'is-viewport-demo' : ''}">
|
||||
<div class="code-example-preview">
|
||||
|
||||
${isViewportDemo ? ` <wa-viewport-demo><iframe srcdoc="${escapedHtml}"></iframe></wa-viewport-demo>` : preview}
|
||||
|
||||
<div class="code-example-resizer" aria-hidden="true">
|
||||
<wa-icon name="grip-lines-vertical"></wa-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="code-example-source" id="${id}">
|
||||
${pre.outerHTML}
|
||||
</div>
|
||||
${
|
||||
hasButtons
|
||||
? `
|
||||
<div class="code-example-buttons">
|
||||
<button
|
||||
class="code-example-toggle"
|
||||
type="button"
|
||||
aria-expanded="${isOpen ? 'true' : 'false'}"
|
||||
aria-controls="${id}"
|
||||
>
|
||||
Code
|
||||
<wa-icon name="chevron-down"></wa-icon>
|
||||
</button>
|
||||
|
||||
${
|
||||
noEdit
|
||||
? ''
|
||||
: `
|
||||
<button class="code-example-pen" type="button">
|
||||
<wa-icon name="pen-to-square"></wa-icon>
|
||||
Edit
|
||||
</button>
|
||||
`
|
||||
}
|
||||
|
||||
`
|
||||
: ''
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
`);
|
||||
|
||||
pre.replaceWith(codeExample);
|
||||
});
|
||||
|
||||
return doc.toString();
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
import { parse } from 'node-html-parser';
|
||||
|
||||
/**
|
||||
* Eleventy plugin to add copy buttons to code blocks.
|
||||
*/
|
||||
export function copyCodePlugin(eleventyConfig, options = {}) {
|
||||
options = {
|
||||
container: 'body',
|
||||
...options,
|
||||
};
|
||||
|
||||
let codeCount = 0;
|
||||
eleventyConfig.addTransform('copy-code', content => {
|
||||
const doc = parse(content, { blockTextElements: { code: true } });
|
||||
const container = doc.querySelector(options.container);
|
||||
|
||||
if (!container) {
|
||||
return content;
|
||||
}
|
||||
|
||||
// Look for code blocks
|
||||
container.querySelectorAll('pre > code').forEach(code => {
|
||||
const pre = code.closest('pre');
|
||||
let preId = pre.getAttribute('id') || `code-block-${++codeCount}`;
|
||||
let codeId = code.getAttribute('id') || `${preId}-inner`;
|
||||
|
||||
if (!code.getAttribute('id')) {
|
||||
code.setAttribute('id', codeId);
|
||||
}
|
||||
if (!pre.getAttribute('id')) {
|
||||
pre.setAttribute('id', preId);
|
||||
}
|
||||
|
||||
// Add a copy button
|
||||
pre.innerHTML += `<wa-icon-button href="#${preId}" class="block-link-icon" name="link"></wa-icon-button>
|
||||
<wa-copy-button from="${codeId}" class="copy-button"></wa-copy-button>`;
|
||||
});
|
||||
|
||||
return doc.toString();
|
||||
});
|
||||
}
|
||||
@@ -1,410 +0,0 @@
|
||||
import { parse } from 'path';
|
||||
|
||||
export function stripExtension(string) {
|
||||
return parse(string).name;
|
||||
}
|
||||
|
||||
export function stripPrefix(content) {
|
||||
return content.replace(/^wa-/, '');
|
||||
}
|
||||
|
||||
// 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.
|
||||
export function trimPipes(content) {
|
||||
return typeof content === 'string' ? content.replace(/^(\s|\|)/g, '').replace(/(\s|\|)$/g, '') : content;
|
||||
}
|
||||
|
||||
export function keys(obj) {
|
||||
return Object.keys(obj);
|
||||
}
|
||||
|
||||
export function log(firstArg, ...rest) {
|
||||
console.log(firstArg, ...rest);
|
||||
return firstArg;
|
||||
}
|
||||
|
||||
function getCollection(name) {
|
||||
// From https://github.com/11ty/eleventy/blob/d3d24ccddb804e6e14773501d8c4e07e2c4b9c2b/src/Filters/GetLocaleCollectionItem.js#L39-L43
|
||||
return this.collections?.[name] || this.ctx?.collections?.[name] || this.context?.environments?.collections?.[name];
|
||||
}
|
||||
|
||||
export function getCollectionItemFromUrl(url, collection) {
|
||||
if (!url) {
|
||||
return null;
|
||||
}
|
||||
collection ??= getCollection.call(this, 'all') || [];
|
||||
return collection.find(item => item.url === url);
|
||||
}
|
||||
|
||||
export function getTitleFromUrl(url, collection) {
|
||||
const item = getCollectionItemFromUrl.call(this, url, collection);
|
||||
return item?.data.title || '';
|
||||
}
|
||||
|
||||
export function split(text, separator) {
|
||||
return (text + '').split(separator).filter(Boolean);
|
||||
}
|
||||
|
||||
export function ancestors(url, { withCurrent = false, withRoot = false } = {}) {
|
||||
let ret = [];
|
||||
let currentUrl = url;
|
||||
let currentItem = getCollectionItemFromUrl.call(this, url);
|
||||
|
||||
if (!currentItem) {
|
||||
// Might have eleventyExcludeFromCollections, jump to parent
|
||||
let parentUrl = this.ctx.parentUrl;
|
||||
if (parentUrl) {
|
||||
url = parentUrl;
|
||||
}
|
||||
}
|
||||
|
||||
for (let item; (item = getCollectionItemFromUrl.call(this, url)); url = item.data.parentUrl) {
|
||||
ret.unshift(item);
|
||||
}
|
||||
|
||||
if (!withRoot && ret[0]?.page.url === '/') {
|
||||
// Remove root
|
||||
ret.shift();
|
||||
}
|
||||
|
||||
if (!withCurrent && ret.at(-1)?.page.url === currentUrl) {
|
||||
// Remove current page
|
||||
ret.pop();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
export function isObject(value) {
|
||||
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
||||
}
|
||||
|
||||
export function isList(value) {
|
||||
return Array.isArray(value) || value instanceof Set;
|
||||
}
|
||||
|
||||
/** Get an Array or Set */
|
||||
export function toList(value) {
|
||||
return isList(value) ? value : [value];
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert any value to something that can be iterated over with a for key, value loop.
|
||||
* Arrays and sets will be converted to a Map of value -> undefined
|
||||
*/
|
||||
export function dict(value) {
|
||||
if (value instanceof Map || isObject(value)) {
|
||||
return value;
|
||||
}
|
||||
|
||||
let list = toList(value);
|
||||
return new Map([...list].map(item => [item, undefined]));
|
||||
}
|
||||
|
||||
export function deepValue(obj, key) {
|
||||
key = Array.isArray(key) ? key : key.split('.');
|
||||
return key.reduce((subObj, property) => subObj?.[property], obj);
|
||||
}
|
||||
|
||||
export function number(value, options) {
|
||||
if (typeof value !== 'number' && isNaN(value)) {
|
||||
return value;
|
||||
}
|
||||
|
||||
let lang = options?.lang ?? 'en';
|
||||
if (options?.lang) {
|
||||
delete options.lang;
|
||||
}
|
||||
|
||||
if (!options || Object.keys(options).length === 0) {
|
||||
options = { maximumSignificantDigits: 3 };
|
||||
}
|
||||
|
||||
return Number(value).toLocaleString(lang, options);
|
||||
}
|
||||
|
||||
export function isNumeric(value) {
|
||||
return typeof value === 'number' || (typeof value === 'string' && !isNaN(value));
|
||||
}
|
||||
|
||||
export function isString(value) {
|
||||
return typeof value === 'string';
|
||||
}
|
||||
|
||||
export function isEmpty(value) {
|
||||
return value === null || value === undefined || value === '';
|
||||
}
|
||||
|
||||
function compare(a, b) {
|
||||
let isEmptyA = isEmpty(a);
|
||||
let isEmptyB = isEmpty(b);
|
||||
|
||||
if (isEmptyA) {
|
||||
if (isEmptyB) {
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
} else if (isEmptyB) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Both strings, and at least one non-numeric
|
||||
if (isNumeric(a) || isNumeric(b)) {
|
||||
return a - b;
|
||||
}
|
||||
|
||||
return (a + '').localeCompare(b);
|
||||
}
|
||||
|
||||
/** Sort an array of objects by one or more of their properties */
|
||||
export function sort(arr, by = { 'data.order': 1, 'data.title': '' }) {
|
||||
let keys = Array.isArray(by) ? by : Object.keys(by);
|
||||
|
||||
return arr.sort((a, b) => {
|
||||
let aValues = keys.map(key => deepValue(a, key) ?? by[key]);
|
||||
let bValues = keys.map(key => deepValue(b, key) ?? by[key]);
|
||||
|
||||
for (let i = 0; i < aValues.length; i++) {
|
||||
let aVal = aValues[i];
|
||||
let bVal = bValues[i];
|
||||
let result = compare(aVal, bVal);
|
||||
|
||||
// They are not equal in terms of comparison OR we're at the last key
|
||||
if (result !== 0 || i === aValues.length - 1) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Group an 11ty collection (or any array of objects with a `data.tags` property) by certain tags.
|
||||
* @param {object[]} collection
|
||||
* @param { Object<string, string> | string[]} [options] Options object or array of tags to group by.
|
||||
* @param {string[] | true} [options.tags] Tags to group by. If true, groups by all tags.
|
||||
* If not provided/empty, defaults to grouping by page hierarchy, with any pages with more than 1 children becoming groups.
|
||||
* @param {string[]} [options.groups] The groups to use if only a subset or a specific order is desired. Defaults to `options.tags`.
|
||||
* @param {string[]} [options.titles] Any title overrides for groups.
|
||||
* @param {string | false} [options.other="Other"] The title to use for the "Other" group. If `false`, the "Other" group is removed..
|
||||
* @returns { Object.<string, object[]> } An object of group ids to arrays of page objects.
|
||||
*/
|
||||
export function groupPages(collection, options = {}, page) {
|
||||
if (!collection) {
|
||||
console.error(`Empty collection passed to groupPages() to group by ${JSON.stringify(options)}`);
|
||||
}
|
||||
|
||||
if (Array.isArray(options)) {
|
||||
options = { tags: options };
|
||||
}
|
||||
|
||||
let { tags, groups, titles = {}, other = 'Other' } = options;
|
||||
|
||||
if (groups === undefined && Array.isArray(tags)) {
|
||||
groups = tags;
|
||||
}
|
||||
|
||||
let grouping;
|
||||
|
||||
if (tags) {
|
||||
grouping = {
|
||||
isGroup: item => undefined,
|
||||
getCandidateGroups: item => item.data.tags,
|
||||
getGroupMeta: group => ({}),
|
||||
};
|
||||
} else {
|
||||
grouping = {
|
||||
isGroup: item => (item.data.children.length >= 2 ? item.page.url : undefined),
|
||||
getCandidateGroups: item => {
|
||||
let parentUrl = item.data.parentUrl;
|
||||
if (page?.url === parentUrl) {
|
||||
return [];
|
||||
}
|
||||
return [parentUrl];
|
||||
},
|
||||
getGroupMeta: group => {
|
||||
let item = byUrl[group] || getCollectionItemFromUrl.call(this, group);
|
||||
return {
|
||||
title: item?.data.title,
|
||||
url: group,
|
||||
item,
|
||||
};
|
||||
},
|
||||
sortGroups: groups => sort(groups.map(url => byUrl[url]).filter(Boolean)).map(item => item.page.url),
|
||||
};
|
||||
}
|
||||
|
||||
let byUrl = {};
|
||||
let byParentUrl = {};
|
||||
|
||||
for (let item of collection) {
|
||||
let url = item.page.url;
|
||||
let parentUrl = item.data.parentUrl;
|
||||
|
||||
byUrl[url] = item;
|
||||
|
||||
if (parentUrl) {
|
||||
byParentUrl[parentUrl] ??= [];
|
||||
byParentUrl[parentUrl].push(item);
|
||||
}
|
||||
}
|
||||
|
||||
let urlToGroups = {};
|
||||
|
||||
for (let item of collection) {
|
||||
let url = item.page.url;
|
||||
let parentUrl = item.data.parentUrl;
|
||||
|
||||
if (grouping.isGroup(item)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let parentItem = byUrl[parentUrl];
|
||||
if (parentItem && !grouping.isGroup(parentItem)) {
|
||||
// Their parent is also here and is not a group
|
||||
continue;
|
||||
}
|
||||
|
||||
let candidateGroups = grouping.getCandidateGroups(item);
|
||||
|
||||
if (groups) {
|
||||
candidateGroups = candidateGroups.filter(group => groups.includes(group));
|
||||
}
|
||||
|
||||
urlToGroups[url] ??= [];
|
||||
|
||||
for (let group of candidateGroups) {
|
||||
urlToGroups[url].push(group);
|
||||
}
|
||||
}
|
||||
|
||||
let ret = {};
|
||||
|
||||
for (let url in urlToGroups) {
|
||||
let groups = urlToGroups[url];
|
||||
let item = byUrl[url];
|
||||
|
||||
if (groups.length === 0) {
|
||||
// Not filtered out but also not categorized
|
||||
groups = ['other'];
|
||||
}
|
||||
|
||||
for (let group of groups) {
|
||||
ret[group] ??= [];
|
||||
ret[group].push(item);
|
||||
|
||||
if (!ret[group].meta) {
|
||||
if (group === 'other') {
|
||||
ret[group].meta = { title: other };
|
||||
} else {
|
||||
ret[group].meta = grouping.getGroupMeta(group);
|
||||
ret[group].meta.title = titles[group] ?? ret[group].meta.title ?? capitalize(group);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (other === false) {
|
||||
delete ret.other;
|
||||
}
|
||||
|
||||
// Sort
|
||||
let sortedGroups = groups ?? grouping.sortGroups?.(Object.keys(ret));
|
||||
|
||||
if (sortedGroups) {
|
||||
ret = sortObject(ret, sortedGroups);
|
||||
} else {
|
||||
// At least make sure other is last
|
||||
if (ret.other) {
|
||||
let otherGroup = ret.other;
|
||||
delete ret.other;
|
||||
ret.other = otherGroup;
|
||||
}
|
||||
}
|
||||
|
||||
Object.defineProperty(ret, 'meta', {
|
||||
value: {
|
||||
groupCount: Object.keys(ret).length,
|
||||
},
|
||||
enumerable: false,
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort an object by its keys
|
||||
* @param {*} obj
|
||||
* @param {function | string[]} order
|
||||
*/
|
||||
function sortObject(obj, order) {
|
||||
let ret = {};
|
||||
let sortedKeys = Array.isArray(order) ? order : Object.keys(obj).sort(order);
|
||||
|
||||
for (let key of sortedKeys) {
|
||||
if (key in obj) {
|
||||
ret[key] = obj[key];
|
||||
}
|
||||
}
|
||||
|
||||
// Add any keys that weren't in the order
|
||||
for (let key in obj) {
|
||||
if (!(key in ret)) {
|
||||
ret[key] = obj[key];
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
function capitalize(str) {
|
||||
str += '';
|
||||
return str.charAt(0).toUpperCase() + str.slice(1);
|
||||
}
|
||||
|
||||
const IDENTITY = x => x;
|
||||
|
||||
/**
|
||||
* Helper to print out one or more HTML attributes, especially conditional ones.
|
||||
* Usage in 11ty:
|
||||
* - Single attribute: `<foo{{ value | attr(name) }}>`
|
||||
* - Multiple attributes: `<foo{{ { name1: value1, name2: value2 } | attr }}>`
|
||||
*
|
||||
* @overload
|
||||
* @param {any} value - The attribute value If falsey, the attribute is not printed. If `true` the attribute is printed without a value.
|
||||
* @param {string} name - The name of the attribute
|
||||
*
|
||||
* @overload
|
||||
* @param {Object<string, any>} obj - Map of attribute names to values
|
||||
*
|
||||
* @returns {string} The attribute string. No `| safe` is needed.
|
||||
*/
|
||||
export function attr(value, name) {
|
||||
const safe = this?.env.filters.safe ?? IDENTITY;
|
||||
|
||||
if (arguments.length === 1 && value && typeof value === 'object') {
|
||||
// Called with a single object argument of names to values
|
||||
let ret = Object.entries(obj)
|
||||
.map(([name, value]) => attr(value, name))
|
||||
.join('');
|
||||
return safe(ret);
|
||||
}
|
||||
|
||||
if (!value) {
|
||||
// false, "", null, undefined
|
||||
return '';
|
||||
}
|
||||
|
||||
let ret = ' ' + name + (value === true ? '' : `="${value}"`);
|
||||
|
||||
return safe(ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format an object as JSON, with formatting & indentation (unlike the default `dump` filter)
|
||||
* @param {*} value
|
||||
* @returns {string}
|
||||
*/
|
||||
export function json(value) {
|
||||
return JSON.stringify(value, null, 2);
|
||||
}
|
||||
71
packages/webawesome/docs/_utils/manifest.js
Normal file
71
packages/webawesome/docs/_utils/manifest.js
Normal file
@@ -0,0 +1,71 @@
|
||||
import { readFileSync } from 'fs';
|
||||
import { dirname, join, resolve } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
/**
|
||||
* @returns Fetches components from custom-elements.json and returns them in more sane format.
|
||||
*/
|
||||
export function getComponents() {
|
||||
const distDir = process.env.UNBUNDLED_DIST_DIRECTORY || resolve(__dirname, '../../dist');
|
||||
const manifest = JSON.parse(readFileSync(join(distDir, 'custom-elements.json'), 'utf-8'));
|
||||
const components = [];
|
||||
|
||||
manifest.modules?.forEach(module => {
|
||||
module.declarations?.forEach(declaration => {
|
||||
if (declaration.customElement) {
|
||||
// Generate the dist path based on the src path and attach it to the component
|
||||
declaration.path = module.path.replace(/^src\//, 'dist/').replace(/\.ts$/, '.js');
|
||||
|
||||
// Remove private members and those that lack a description
|
||||
const members = declaration.members?.filter(member => member.description && member.privacy !== 'private');
|
||||
const methods = members?.filter(prop => prop.kind === 'method' && prop.privacy !== 'private');
|
||||
const properties = members?.filter(prop => {
|
||||
// Look for a corresponding attribute
|
||||
const attribute = declaration.attributes?.find(attr => attr.fieldName === prop.name);
|
||||
if (attribute) {
|
||||
prop.attribute = attribute.name || attribute.fieldName;
|
||||
}
|
||||
|
||||
return prop.kind === 'field' && prop.privacy !== 'private';
|
||||
});
|
||||
components.push({
|
||||
...declaration,
|
||||
methods,
|
||||
properties,
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Build dependency graphs
|
||||
components.forEach(component => {
|
||||
const dependencies = [];
|
||||
|
||||
// Recursively fetch sub-dependencies
|
||||
function getDependencies(tag) {
|
||||
const cmp = components.find(c => c.tagName === tag);
|
||||
if (!cmp || !Array.isArray(component.dependencies)) {
|
||||
return;
|
||||
}
|
||||
|
||||
cmp.dependencies?.forEach(dependentTag => {
|
||||
if (!dependencies.includes(dependentTag)) {
|
||||
dependencies.push(dependentTag);
|
||||
}
|
||||
getDependencies(dependentTag);
|
||||
});
|
||||
}
|
||||
|
||||
getDependencies(component.tagName);
|
||||
|
||||
component.dependencies = dependencies.sort();
|
||||
});
|
||||
|
||||
// Sort by name
|
||||
return components.sort((a, b) => {
|
||||
if (a.name < b.name) return -1;
|
||||
if (a.name > b.name) return 1;
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
import { parse } from 'node-html-parser';
|
||||
|
||||
/**
|
||||
* Eleventy plugin to add an outline (table of contents) to the page. Headings must have an id, otherwise they won't be
|
||||
* included in the outline. An unordered list containing links will be appended to the target element.
|
||||
*
|
||||
* If no headings are found for the outline, the `ifEmpty()` function will be called with a `node-html-parser` object as
|
||||
* the first argument. This can be used to toggle classes or remove elements when the outline is empty.
|
||||
*
|
||||
* See the `node-html-parser` docs for more details: https://www.npmjs.com/package/node-html-parser
|
||||
*/
|
||||
export function outlinePlugin(options = {}) {
|
||||
options = {
|
||||
container: 'body',
|
||||
target: '.outline',
|
||||
selector: 'h2,h3',
|
||||
ifEmpty: () => null,
|
||||
...options,
|
||||
};
|
||||
|
||||
return function (eleventyConfig) {
|
||||
eleventyConfig.addTransform('outline', content => {
|
||||
const doc = parse(content);
|
||||
const container = doc.querySelector(options.container);
|
||||
const ul = parse('<ul></ul>');
|
||||
let numLinks = 0;
|
||||
|
||||
if (!container) {
|
||||
return content;
|
||||
}
|
||||
|
||||
container.querySelectorAll(options.selector).forEach(heading => {
|
||||
const id = heading.getAttribute('id');
|
||||
const level = heading.tagName.slice(1);
|
||||
const clone = parse(heading.outerHTML);
|
||||
|
||||
if (heading.closest('[data-no-outline]')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a clone of the heading so we can remove links and [data-no-outline] elements from the text content
|
||||
clone.querySelectorAll('.wa-visually-hidden, [hidden], [aria-hidden="true"]').forEach(el => el.remove());
|
||||
clone.querySelectorAll('[data-no-outline]').forEach(el => el.remove());
|
||||
|
||||
// Generate the link
|
||||
const li = parse(`<li data-level="${level}"><a></a></li>`);
|
||||
const a = li.querySelector('a');
|
||||
a.setAttribute('href', `#${encodeURIComponent(id)}`);
|
||||
a.textContent = clone.textContent.trim().replace(/#$/, '');
|
||||
|
||||
// Add it to the list
|
||||
ul.firstChild.appendChild(li);
|
||||
numLinks++;
|
||||
});
|
||||
|
||||
if (numLinks > 0) {
|
||||
// Append the list to all matching targets
|
||||
doc.querySelectorAll(options.target).forEach(target => {
|
||||
target.appendChild(parse(ul.outerHTML));
|
||||
});
|
||||
} else {
|
||||
// Remove if empty
|
||||
options.ifEmpty(doc);
|
||||
}
|
||||
|
||||
return doc.toString();
|
||||
});
|
||||
};
|
||||
}
|
||||
18
packages/webawesome/docs/_utils/simulate-webawesome-app.js
Normal file
18
packages/webawesome/docs/_utils/simulate-webawesome-app.js
Normal file
@@ -0,0 +1,18 @@
|
||||
import nunjucks from 'nunjucks';
|
||||
|
||||
/**
|
||||
* This function simulates what a server would do running "on top" of eleventy.
|
||||
*/
|
||||
export function SimulateWebAwesomeApp(str) {
|
||||
return nunjucks.renderString(str, {
|
||||
// Stub the server EJS shortcodes.
|
||||
currentUser: {
|
||||
hasPro: false,
|
||||
},
|
||||
server: {
|
||||
head: '',
|
||||
loginOrAvatar: '',
|
||||
flashes: '',
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -1,172 +0,0 @@
|
||||
/**
|
||||
* Low-level utility to encapsulate a bit of HTML (mainly to apply certain stylesheets to it without them leaking to the rest of the page)
|
||||
* Usage: <wa-scoped><template><!-- your HTML here --></template></wa-scoped>
|
||||
*/
|
||||
import { discover } from '/dist/webawesome.js';
|
||||
|
||||
const imports = new Set();
|
||||
const fontFaceRules = new Set();
|
||||
|
||||
export default class WaScoped extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
this.attachShadow({ mode: 'open' });
|
||||
|
||||
this.observer = new MutationObserver(records => this.render(records));
|
||||
this.observer.observe(this, { childList: true, subtree: true, characterData: true });
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
this.render();
|
||||
this.ownerDocument.documentElement.addEventListener('wa-color-scheme-change', e =>
|
||||
this.#applyDarkMode(e.detail.dark),
|
||||
);
|
||||
}
|
||||
|
||||
render(records) {
|
||||
this.observer.takeRecords();
|
||||
this.observer.disconnect();
|
||||
|
||||
this.shadowRoot.innerHTML = '';
|
||||
|
||||
// To avoid mutating this.childNodes while iterating over it
|
||||
let nodes = [];
|
||||
|
||||
for (let template of this.childNodes) {
|
||||
if (!(template instanceof HTMLTemplateElement)) {
|
||||
if (template.nodeType === Node.ELEMENT_NODE) {
|
||||
console.warn('<wa-scoped> can only contain <template> elements');
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (template.content.childNodes.length > 0) {
|
||||
nodes.push(template.content.cloneNode(true));
|
||||
} else if (template.childNodes.length > 0) {
|
||||
// Fake template, suck its children out of the light DOM
|
||||
nodes.push(...template.childNodes);
|
||||
}
|
||||
}
|
||||
|
||||
this.shadowRoot.append(...nodes);
|
||||
|
||||
this.#fixStyles();
|
||||
this.#applyDarkMode();
|
||||
|
||||
discover(this.shadowRoot);
|
||||
|
||||
this.observer.observe(this, { childList: true, subtree: true, characterData: true });
|
||||
}
|
||||
|
||||
#applyDarkMode(isDark = getComputedStyle(this).colorScheme === 'dark') {
|
||||
// Hack to make dark mode work
|
||||
// NOTE If any child nodes actually have .wa-dark, this will override it
|
||||
for (let node of this.shadowRoot.children) {
|
||||
node.classList.toggle('wa-dark', isDark);
|
||||
}
|
||||
this.classList.toggle('wa-dark', isDark);
|
||||
}
|
||||
|
||||
/**
|
||||
* @font-face does not work in shadow DOM in Chrome & FF, as of March 2025 https://issues.chromium.org/issues/41085401
|
||||
* This works around this issue by traversing the shadow DOM CSS looking
|
||||
* for @font-face rules or CSS imports to known font providers and copies them to the main document
|
||||
*/
|
||||
async #fixStyles() {
|
||||
let styleElements = [...this.shadowRoot.querySelectorAll('link[rel="stylesheet"], style')];
|
||||
|
||||
let loadStates = styleElements.map(element => {
|
||||
try {
|
||||
if (element.sheet?.cssRules) {
|
||||
// Already loaded
|
||||
return Promise.resolve(element.sheet);
|
||||
}
|
||||
} catch (e) {
|
||||
// CORS
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
element.addEventListener('load', e => resolve(element.sheet));
|
||||
element.addEventListener('error', e => reject(null));
|
||||
});
|
||||
});
|
||||
|
||||
await Promise.allSettled(loadStates);
|
||||
|
||||
let fontRules = findFontFaceRules(...this.shadowRoot.styleSheets);
|
||||
|
||||
if (!fontRules.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
let doc = this.ownerDocument;
|
||||
// Why not adoptedStyleSheets? Can't have @import in those yet
|
||||
let id = `wa-scoped-hoisted-fonts`;
|
||||
let style = doc.head.querySelector('style#' + id);
|
||||
if (!style) {
|
||||
style = Object.assign(doc.createElement('style'), { id, textContent: ' ' });
|
||||
doc.head.append(style);
|
||||
}
|
||||
let sheet = style.sheet;
|
||||
|
||||
for (let rule of fontRules) {
|
||||
let cssText = rule.cssText;
|
||||
if (rule.type === CSSRule.FONT_FACE_RULE) {
|
||||
if (fontFaceRules.has(cssText)) {
|
||||
continue;
|
||||
}
|
||||
fontFaceRules.add(cssText);
|
||||
sheet.insertRule(cssText);
|
||||
} else if (rule.type === CSSRule.IMPORT_RULE) {
|
||||
if (imports.has(rule.href)) {
|
||||
continue;
|
||||
}
|
||||
imports.add(rule.href);
|
||||
sheet.insertRule(cssText, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static observedAttributes = [];
|
||||
}
|
||||
|
||||
customElements.define('wa-scoped', WaScoped);
|
||||
|
||||
export const WEB_FONT_HOSTS = [
|
||||
'fonts.googleapis.com',
|
||||
'fonts.gstatic.com',
|
||||
'use.typekit.net',
|
||||
'fonts.adobe.com',
|
||||
'kit.fontawesome.com',
|
||||
'pro.fontawesome.com',
|
||||
'cdn.materialdesignicons.com',
|
||||
];
|
||||
|
||||
function findFontFaceRules(...stylesheets) {
|
||||
let ret = [];
|
||||
|
||||
for (let sheet of stylesheets) {
|
||||
let rules;
|
||||
try {
|
||||
rules = sheet.cssRules;
|
||||
} catch (e) {
|
||||
// CORS
|
||||
continue;
|
||||
}
|
||||
|
||||
for (let rule of rules) {
|
||||
if (rule.type === CSSRule.FONT_FACE_RULE) {
|
||||
ret.push(rule);
|
||||
} else if (rule.type === CSSRule.IMPORT_RULE) {
|
||||
if (WEB_FONT_HOSTS.some(host => rule.href.includes(host))) {
|
||||
ret.push(rule);
|
||||
} else if (rule.styleSheet) {
|
||||
ret.push(...findFontFaceRules(rule.styleSheet));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
/**
|
||||
* Data related to palettes and colors.
|
||||
* Must work in both browser and Node.js
|
||||
*/
|
||||
|
||||
export const tints = ['05', '10', '20', '30', '40', '50', '60', '70', '80', '90', '95'];
|
||||
|
||||
export const hueRanges = {
|
||||
red: { min: 5, max: 35 }, // 30
|
||||
orange: { min: 35, max: 60 }, // 25
|
||||
yellow: { min: 60, max: 112 }, // 45
|
||||
green: { min: 112, max: 170 }, // 55
|
||||
cyan: { min: 170, max: 220 }, // 50
|
||||
blue: { min: 220, max: 265 }, // 45
|
||||
indigo: { min: 265, max: 290 }, // 25
|
||||
purple: { min: 290, max: 320 }, // 30
|
||||
pink: { min: 320, max: 365 }, // 45
|
||||
};
|
||||
|
||||
export const hues = Object.keys(hueRanges);
|
||||
export const allHues = [...hues, 'gray'];
|
||||
|
||||
export const moreHue = {
|
||||
red: 'Redder',
|
||||
orange: 'More orange', // https://www.reddit.com/r/grammar/comments/u9n0uo/is_it_oranger_or_more_orange/
|
||||
yellow: 'Yellower',
|
||||
green: 'Greener',
|
||||
cyan: 'More cyan',
|
||||
blue: 'Bluer',
|
||||
indigo: 'More indigo',
|
||||
pink: 'Pinker',
|
||||
};
|
||||
|
||||
/**
|
||||
* Max gray chroma (% of chroma of undertone) per hue
|
||||
*/
|
||||
export const maxGrayChroma = {
|
||||
red: 0.2,
|
||||
orange: 0.2,
|
||||
yellow: 0.25,
|
||||
green: 0.25,
|
||||
cyan: 0.3,
|
||||
blue: 0.35,
|
||||
indigo: 0.35,
|
||||
purple: 0.3,
|
||||
pink: 0.25,
|
||||
};
|
||||
@@ -1,65 +0,0 @@
|
||||
import { deepEntries } from '../scripts/util/deep.js';
|
||||
import { themeConfig } from './theming.js';
|
||||
import themes from '/assets/data/themes.js';
|
||||
|
||||
/**
|
||||
* Map of font pairings (body + heading) to the first theme that uses them.
|
||||
*/
|
||||
export const pairings = {};
|
||||
|
||||
// NOTE Do not use Symbols, we want these to be enumerable when used as keys
|
||||
export const sameAs = { body: '$body' };
|
||||
|
||||
export const fontNames = {
|
||||
'system-ui': 'OS Default',
|
||||
'ui-serif': 'OS Default Serif',
|
||||
'ui-sans-serif': 'OS Default Sans Serif',
|
||||
'ui-monospace': 'OS Default Code Font',
|
||||
'ui-monospace': 'OS Default Code Font',
|
||||
};
|
||||
|
||||
export function defaultTitle(fonts) {
|
||||
let { body, heading = sameAs.body } = fonts;
|
||||
let names = [body];
|
||||
|
||||
if (heading !== sameAs.body) {
|
||||
names.unshift(heading);
|
||||
}
|
||||
|
||||
return names.map(name => fontNames[name] ?? name).join(' • ');
|
||||
}
|
||||
|
||||
for (let id in themes) {
|
||||
let theme = themes[id];
|
||||
let { fonts } = theme;
|
||||
|
||||
if (fonts) {
|
||||
let { body, heading = sameAs.body } = fonts;
|
||||
|
||||
pairings[body] ??= {};
|
||||
pairings[body][heading] ??= {
|
||||
id, // First theme that uses this pairing
|
||||
ids: new Set([id]), // All themes that use this pairing
|
||||
url: themeConfig.typography.url(id), // Stylesheet URL
|
||||
fonts,
|
||||
get title() {
|
||||
return defaultTitle(this.fonts);
|
||||
},
|
||||
};
|
||||
pairings[body][heading].ids.add(id);
|
||||
}
|
||||
}
|
||||
|
||||
export const pairingsEntries = deepEntries(pairings, {
|
||||
descend(value, key, parent, path) {
|
||||
if (value?.fonts) {
|
||||
return false; // Don't recurse into pairing objects
|
||||
}
|
||||
},
|
||||
filter(value, key, parent, path) {
|
||||
// Only keep 2 levels (body → heading → pairing)
|
||||
return path.length === 1;
|
||||
},
|
||||
});
|
||||
|
||||
export const pairingsList = pairingsEntries.map(arg => arg.at(-1));
|
||||
@@ -1,7 +0,0 @@
|
||||
export const iconLibraries = {
|
||||
default: {
|
||||
title: 'Font Awesome',
|
||||
family: ['classic', 'sharp', 'duotone', 'sharp-duotone'],
|
||||
style: ['solid', 'regular', 'light', 'thin'],
|
||||
},
|
||||
};
|
||||
@@ -1,6 +0,0 @@
|
||||
export * from './colors.js';
|
||||
// export * from './fonts.js';
|
||||
export * from './icons.js';
|
||||
export * from './theming.js';
|
||||
|
||||
export const cdnUrl = globalThis.document ? document.documentElement.dataset.cdnUrl : '/dist/';
|
||||
@@ -1,57 +0,0 @@
|
||||
---
|
||||
layout: null
|
||||
permalink: '/assets/data/palettes.js'
|
||||
eleventyExcludeFromCollections: true
|
||||
---
|
||||
import Color from 'https://colorjs.io/dist/color.js';
|
||||
|
||||
const palettes = {
|
||||
{%- for palette in collections.palette | sort %}
|
||||
{%- if not palette.data.unlisted %}
|
||||
{% set paletteId = palette.fileSlug -%}
|
||||
{%- set colors = palettes[paletteId] -%}
|
||||
'{{ paletteId }}': {
|
||||
id: '{{ paletteId }}',
|
||||
title: '{{ palette.data.title }}',
|
||||
colors: {
|
||||
{% for hue, tints in colors -%}
|
||||
'{{ hue }}': {
|
||||
{% for tint, value in tints -%}
|
||||
{%- if tint != '05' -%}
|
||||
'{{ '05' if tint == '5' else tint }}': '{{ value | safe }}',
|
||||
{%- endif %}
|
||||
{% endfor %}
|
||||
|
||||
get key() {
|
||||
return this[this.maxChromaTint];
|
||||
}
|
||||
},
|
||||
{% endfor -%} // end colors
|
||||
}
|
||||
}, // end palette
|
||||
{%- endif -%}
|
||||
{% endfor %}
|
||||
};
|
||||
|
||||
// Create Color instances for each color
|
||||
for (let palette in palettes) {
|
||||
for (let hue in palettes[palette].colors) {
|
||||
let scale = palettes[palette].colors[hue];
|
||||
|
||||
for (let tint in scale) {
|
||||
let color = scale[tint];
|
||||
try {
|
||||
if (Array.isArray(color)) {
|
||||
scale[tint] = new Color('oklch', color);
|
||||
}
|
||||
else if (typeof color === 'string' && isNaN(color)) {
|
||||
scale[tint] = new Color(color);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default palettes;
|
||||
@@ -1,27 +0,0 @@
|
||||
---
|
||||
layout: null
|
||||
permalink: '/assets/data/themes.js'
|
||||
eleventyExcludeFromCollections: true
|
||||
---
|
||||
|
||||
export default {
|
||||
{%- for theme in collections.theme | sort %}
|
||||
{%- if not theme.data.unlisted and theme.fileSlug !== 'edit' and theme.fileSlug !== 'custom' %}
|
||||
{% set themeId = theme.fileSlug -%}
|
||||
{%- set themeMeta = themes[themeId] -%}
|
||||
'{{ themeId }}': {
|
||||
id: '{{ themeId }}',
|
||||
title: '{{ theme.data.title }}',
|
||||
palette: '{{ themeMeta.palette }}',
|
||||
brand: '{{ themeMeta.brand }}',
|
||||
isPro: {{ theme.data.isPro or 'pro' in theme.data.tags }},
|
||||
fonts: {{ (theme.data.fonts | json or 'null') | safe }},
|
||||
icons: {{ (themeMeta.icons | json or 'null') | safe }},
|
||||
rounding: {{ themeMeta.rounding }},
|
||||
spacing: {{ themeMeta.spacing }},
|
||||
borderWidth: {{ themeMeta.borderWidth }},
|
||||
dimension: {{ (theme.data.dimension or themeMeta.dimension or false) | json | safe }},
|
||||
},
|
||||
{%- endif %}
|
||||
{% endfor %}
|
||||
};
|
||||
@@ -1,100 +0,0 @@
|
||||
import { deepEach, isPlainObject } from '../scripts/util/deep.js';
|
||||
|
||||
/**
|
||||
* Data related to themes, theme remixing
|
||||
* Must work in both browser and Node.js
|
||||
*/
|
||||
export const cdnUrl = globalThis.document ? document.documentElement.dataset.cdnUrl : '/dist/';
|
||||
|
||||
// This should eventually replace all uses of `urls` and `themeParams`
|
||||
export const themeConfig = {
|
||||
base: { url: id => `styles/themes/${id}.css`, default: 'default' },
|
||||
colors: {
|
||||
url: id => `styles/themes/${id}/color.css`,
|
||||
docs: '/docs/themes/',
|
||||
icon: 'palette',
|
||||
default() {
|
||||
return this.base;
|
||||
},
|
||||
},
|
||||
palette: {
|
||||
url: id => `styles/color/${id}.css`,
|
||||
docs: '/docs/palette/',
|
||||
icon: 'swatchbook',
|
||||
default(baseTheme) {
|
||||
return baseTheme?.palette;
|
||||
},
|
||||
},
|
||||
brand: {
|
||||
url: id => `styles/brand/${id}.css`,
|
||||
icon: 'droplet',
|
||||
default(baseTheme) {
|
||||
return baseTheme?.brand;
|
||||
},
|
||||
},
|
||||
typography: {
|
||||
url: id => `styles/themes/${id}/typography.css`,
|
||||
docs: '/docs/themes/',
|
||||
icon: 'font-case',
|
||||
default() {
|
||||
return this.base;
|
||||
},
|
||||
},
|
||||
rounding: {
|
||||
cssProperty: '--wa-border-radius-scale',
|
||||
default(baseTheme) {
|
||||
return baseTheme?.rounding ?? 1;
|
||||
},
|
||||
},
|
||||
spacing: {
|
||||
cssProperty: '--wa-space-scale',
|
||||
default(baseTheme) {
|
||||
return baseTheme?.spacing ?? 1;
|
||||
},
|
||||
},
|
||||
borderWidth: {
|
||||
cssProperty: '--wa-border-width-scale',
|
||||
default(baseTheme) {
|
||||
return baseTheme?.borderWidth ?? 1;
|
||||
},
|
||||
},
|
||||
dimensionality: {
|
||||
url: id => `styles/themes/${id}/dimension.css`,
|
||||
docs: '/docs/themes/',
|
||||
icon: 'cube',
|
||||
default() {
|
||||
return this.base;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export function getPath(key) {
|
||||
if (key.startsWith('icon-')) {
|
||||
// TODO detect what the nested prefixes are from theme config metadata
|
||||
return ['icon', ...key.slice(5)];
|
||||
}
|
||||
}
|
||||
|
||||
// Shallow remixing params in correct order
|
||||
// base must be first. brand needs to come after palette, which needs to come after colors.
|
||||
export const themeParams = Object.keys(themeConfig).filter(aspect => themeConfig[aspect].url);
|
||||
|
||||
export const urls = themeParams.reduce((acc, aspect) => {
|
||||
acc[aspect] = themeConfig[aspect].url;
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
export const themeDefaults = { ...themeConfig };
|
||||
|
||||
deepEach(themeDefaults, (value, key, parent, path) => {
|
||||
if (isPlainObject(value)) {
|
||||
// Replace w/ default value or shallow clone
|
||||
return value.default ?? { ...value };
|
||||
}
|
||||
});
|
||||
|
||||
export const selectors = {
|
||||
palette: id =>
|
||||
[':where(:root)', ':host', ":where([class^='wa-theme-'], [class*=' wa-theme-'])", `.wa-palette-${id}`].join(',\n'),
|
||||
theme: id => [':where(:root)', ':host', `.wa-theme-${id}`].join(',\n'),
|
||||
};
|
||||
@@ -1,287 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en" class="wa-cloak">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Web Awesome Page Demo 1</title>
|
||||
<link rel="stylesheet" href="/dist/styles/themes/default.css" />
|
||||
<link rel="stylesheet" href="/dist/styles/webawesome.css" />
|
||||
<script type="module" src="/dist/webawesome.loader.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<wa-page mobile-breakpoint="920">
|
||||
<div slot="banner" class="wa-body-s">
|
||||
<a href="#" class="wa-cluster wa-align-items-baseline wa-gap-xs" style="flex-wrap: nowrap">
|
||||
<wa-icon name="gift"></wa-icon>
|
||||
<span>Give a Hoot for the Holidays: Donate now and double your impact.</span>
|
||||
</a>
|
||||
</div>
|
||||
<header slot="header" class="wa-split">
|
||||
<div class="wa-cluster">
|
||||
<wa-icon name="feather-pointed" style="color: var(--wa-color-brand-fill-loud); font-size: 1.5em"></wa-icon>
|
||||
<span id="brand-name" class="wa-heading-s wa-desktop-only">Audubon Worldwide</span>
|
||||
<a href="#">Our Work</a>
|
||||
<a href="#">About Us</a>
|
||||
<a href="#">Discover</a>
|
||||
<a href="#">Get Involved</a>
|
||||
</div>
|
||||
<div class="wa-cluster wa-gap-xs">
|
||||
<wa-button size="small" variant="brand" appearance="outlined">Find Your Local Audubon</wa-button>
|
||||
<wa-button size="small" variant="brand">Donate</wa-button>
|
||||
</div>
|
||||
</header>
|
||||
<nav slot="subheader">
|
||||
<div class="wa-cluster" style="flex-wrap: nowrap">
|
||||
<wa-icon-button data-toggle-nav name="bars" label="Menu"></wa-icon-button>
|
||||
<wa-breadcrumb style="font-size: var(--wa-font-size-s)">
|
||||
<wa-breadcrumb-item>Field Guides</wa-breadcrumb-item>
|
||||
<wa-breadcrumb-item>Owls</wa-breadcrumb-item>
|
||||
<wa-breadcrumb-item>Great Horned Owl</wa-breadcrumb-item>
|
||||
</wa-breadcrumb>
|
||||
</div>
|
||||
<wa-input id="search" class="wa-desktop-only" placeholder="Search" size="small" style="max-inline-size: 12rem">
|
||||
<wa-icon slot="prefix" name="magnifying-glass"></wa-icon>
|
||||
</wa-input>
|
||||
</nav>
|
||||
<nav slot="navigation-header">
|
||||
<div class="wa-flank">
|
||||
<wa-avatar image="https://images.unsplash.com/photo-1544648720-132573cb590d?q=20" label=""></wa-avatar>
|
||||
<div class="wa-stack wa-gap-3xs">
|
||||
<span class="wa-heading-s">Great Horned Owl</span>
|
||||
<span class="wa-caption-s" lang="la"><em>Bubo virginianus</em></span>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<nav slot="navigation">
|
||||
<a href="#identification">Identification</a>
|
||||
<a href="#range">Range and Habitat</a>
|
||||
<a href="#behavior">Behavior</a>
|
||||
<a href="#conservation">Conservation</a>
|
||||
</nav>
|
||||
<nav slot="navigation-footer">
|
||||
<a href="#" class="wa-flank">
|
||||
<wa-icon name="camera"></wa-icon>
|
||||
<span>Photo Gallery</span>
|
||||
</a>
|
||||
<a href="#" class="wa-flank">
|
||||
<wa-icon name="map-location-dot"></wa-icon>
|
||||
<span>Interactive Range Map</span>
|
||||
</a>
|
||||
</nav>
|
||||
<header slot="main-header">
|
||||
<div
|
||||
class="wa-flank:end wa-border-radius-l wa-dark"
|
||||
style="
|
||||
background-color: var(--wa-color-surface-lowered);
|
||||
--content-percentage: 35%;
|
||||
padding: var(--wa-space-m);
|
||||
"
|
||||
>
|
||||
<div class="wa-stack" style="margin: var(--wa-space-2xl)">
|
||||
<h1>Great Horned Owl</h1>
|
||||
<wa-divider></wa-divider>
|
||||
<div class="wa-cluster wa-gap-xs">
|
||||
<wa-tag size="small">Owls</wa-tag>
|
||||
<wa-tag size="small">Birds of Prey</wa-tag>
|
||||
<wa-tag size="small">Pleistocene Birds</wa-tag>
|
||||
</div>
|
||||
<div class="wa-flank">
|
||||
<wa-icon name="ruler"></wa-icon>
|
||||
<span class="wa-caption-m">L 21.5" | WS 48.5"</span>
|
||||
</div>
|
||||
<div class="wa-flank">
|
||||
<wa-icon name="earth-americas"></wa-icon>
|
||||
<span class="wa-caption-m"
|
||||
>North America (Widespread), Central America (Limited), South America (Limited)</span
|
||||
>
|
||||
</div>
|
||||
<div class="wa-flank">
|
||||
<wa-icon name="shield-heart"></wa-icon>
|
||||
<span class="wa-caption-m">Least Concern</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="wa-frame" style="border-radius: var(--wa-border-radius-l); max-inline-size: 40ch">
|
||||
<img src="https://images.unsplash.com/photo-1544648720-132573cb590d?q=20" />
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<main class="wa-body-l">
|
||||
<h2 id="identification">Identification</h2>
|
||||
<p>
|
||||
Lorem ipsum odor amet, consectetuer adipiscing elit. Eget habitant scelerisque lectus ultrices nascetur
|
||||
aliquet sapien primis. Cursus sapien fusce semper nulla elit sociosqu lectus per sem. Sem ad porttitor dictum
|
||||
nisl pharetra tortor convallis. Sit molestie hendrerit porta dictum tortor posuere euismod magna. Mauris
|
||||
suspendisse pharetra finibus; eleifend etiam ridiculus.
|
||||
</p>
|
||||
<h2 id="range">Range and Habitat</h2>
|
||||
<p>
|
||||
Diam sed ipsum pretium porttitor class cubilia elementum. Blandit felis ligula habitant ultricies vulputate
|
||||
rutrum lacus commodo pulvinar. Nostra semper placerat lectus in dis eu. Sagittis ipsum placerat rhoncus lacus
|
||||
id eget. Erat pharetra aptent enim, augue accumsan ultricies inceptos habitasse. Senectus id maximus
|
||||
parturient tellus; fermentum posuere vulputate luctus. Ac tempus dapibus vehicula ligula ullamcorper sit duis.
|
||||
</p>
|
||||
<h2 id="behavior">Behavior</h2>
|
||||
<p>
|
||||
Erat vitae luctus arcu taciti malesuada pretium arcu justo primis. Cubilia vitae maecenas congue velit id
|
||||
netus arcu. Dictum vel pellentesque taciti fermentum risus consectetur amet. Faucibus commodo habitasse sem
|
||||
maximus praesent purus, dignissim tristique porta. Platea magna justo ipsum ut metus ac facilisi. Imperdiet
|
||||
laoreet pharetra maximus lacus tortor suscipit. Nam quisque iaculis orci porttitor pellentesque rhoncus.
|
||||
Molestie sagittis tincidunt quisque nisi non urna conubia.
|
||||
</p>
|
||||
<h2 id="conservation">Conservation</h2>
|
||||
<p>
|
||||
Nullam magna quam quisque eu varius integer. Inceptos donec facilisi risus himenaeos semper mollis habitasse.
|
||||
Vehicula lacus vivamus euismod pharetra mollis dictum. Ante ex tortor elementum eleifend habitasse orci
|
||||
aliquam. Fames erat senectus fames etiam dapibus cursus.
|
||||
</p>
|
||||
</main>
|
||||
<footer slot="main-footer">
|
||||
<section>
|
||||
<h2 class="wa-heading-m">Sources</h2>
|
||||
<ul class="wa-body-s">
|
||||
<li>
|
||||
<cite
|
||||
><a href="https://www.audubon.org/field-guide/bird/great-horned-owl" target="_blank" rel="noopener"
|
||||
>Great Horned Owl</a
|
||||
></cite
|
||||
>, National Audubon Society. Retrieved 5 December 2024.
|
||||
</li>
|
||||
<li>
|
||||
<cite
|
||||
><a href="https://www.allaboutbirds.org/guide/Great_Horned_Owl/" target="_blank" rel="noopener"
|
||||
>Great Horned Owl</a
|
||||
></cite
|
||||
>, All About Birds by CornellLab. Retrieved 5 December 2024.
|
||||
</li>
|
||||
<li>Armistead, G. L. (2015). <cite>Field guide to birds of Pennsylvania</cite>. Scott & Nix, Inc.</li>
|
||||
</ul>
|
||||
</section>
|
||||
</footer>
|
||||
<aside slot="aside" class="wa-desktop-only">
|
||||
<h2 class="wa-heading-m">Discover More Birds</h2>
|
||||
<wa-card>
|
||||
<div slot="media" class="wa-frame">
|
||||
<img src="https://images.unsplash.com/photo-1635254859323-65b78408dcca?q=20" alt="" />
|
||||
</div>
|
||||
<div class="wa-stack wa-gap-3xs">
|
||||
<span class="wa-heading-s">Long-eared Owl</span>
|
||||
<span class="wa-caption-s" lang="la"><em>Asio otus</em></span>
|
||||
</div>
|
||||
</wa-card>
|
||||
<wa-card>
|
||||
<div slot="media" class="wa-frame">
|
||||
<img src="https://images.unsplash.com/photo-1661350356618-f5915c7b6a3c?q=20" alt="" />
|
||||
</div>
|
||||
<div class="wa-stack wa-gap-3xs">
|
||||
<span class="wa-heading-s">Northen Hawk Owl</span>
|
||||
<span class="wa-caption-s" lang="la"><em>Surnia ulula</em></span>
|
||||
</div>
|
||||
</wa-card>
|
||||
<wa-card>
|
||||
<div slot="media" class="wa-frame">
|
||||
<img src="https://images.unsplash.com/photo-1660307777355-f08bced145d3?q=20" alt="" />
|
||||
</div>
|
||||
<div class="wa-stack wa-gap-3xs">
|
||||
<span class="wa-heading-s">Golden Eagle</span>
|
||||
<span class="wa-caption-s" lang="la"><em>Aquila chrysaetos</em></span>
|
||||
</div>
|
||||
</wa-card>
|
||||
</aside>
|
||||
<footer slot="footer" class="wa-grid wa-gap-xl">
|
||||
<div class="wa-cluster" style="flex-wrap: nowrap">
|
||||
<wa-icon name="feather-pointed" style="font-size: 1.5em"></wa-icon>
|
||||
<span class="wa-heading-s">Audubon Worldwide</span>
|
||||
</div>
|
||||
<div class="wa-stack">
|
||||
<h3 class="wa-heading-xs">Our Work</h3>
|
||||
<a href="#">Habitat Restoration</a>
|
||||
<a href="#">Migration Science</a>
|
||||
<a href="#">Advocacy</a>
|
||||
</div>
|
||||
<div class="wa-stack">
|
||||
<h3 class="wa-heading-xs">About Us</h3>
|
||||
<a href="#">Our History</a>
|
||||
<a href="#">Leadership</a>
|
||||
<a href="#">Fiscal Reports</a>
|
||||
</div>
|
||||
<div class="wa-stack">
|
||||
<h3 class="wa-heading-xs">Discover</h3>
|
||||
<a href="#">Field Guides</a>
|
||||
<a href="#">Photo Search</a>
|
||||
<a href="#">Gear and Resources</a>
|
||||
</div>
|
||||
<div class="wa-stack">
|
||||
<h3 class="wa-heading-xs">Get Involved</h3>
|
||||
<a href="#">Adopt a Bird</a>
|
||||
<a href="#">Your Local Audubon</a>
|
||||
<a href="#">Youth Audubon Camps</a>
|
||||
</div>
|
||||
</footer>
|
||||
</wa-page>
|
||||
|
||||
<style>
|
||||
wa-page {
|
||||
--menu-width: 15rem;
|
||||
--aside-width: 15rem;
|
||||
}
|
||||
wa-page[view='desktop'] {
|
||||
[slot*='navigation'] {
|
||||
border-inline-end: var(--wa-border-width-s) var(--wa-border-style) var(--wa-color-surface-border);
|
||||
}
|
||||
}
|
||||
wa-page[view='mobile'] {
|
||||
--menu-width: auto;
|
||||
--aside-width: auto;
|
||||
}
|
||||
|
||||
[slot='banner'] {
|
||||
--wa-color-text-link: var(--wa-color-neutral-on-loud);
|
||||
background-color: var(--wa-color-neutral-fill-loud);
|
||||
}
|
||||
[slot='header'] {
|
||||
--wa-link-decoration-default: none;
|
||||
border-block-end: var(--wa-border-width-s) var(--wa-border-style) var(--wa-color-surface-border);
|
||||
}
|
||||
[slot*='header'] a {
|
||||
font-weight: var(--wa-font-weight-action);
|
||||
}
|
||||
[slot='subheader'] {
|
||||
background-color: var(--wa-color-surface-lowered);
|
||||
border-block-end: var(--wa-border-width-s) var(--wa-border-style) var(--wa-color-surface-border);
|
||||
}
|
||||
[slot='navigation-header'] {
|
||||
border-block-end: var(--wa-border-width-s) var(--wa-border-style) var(--wa-color-surface-border);
|
||||
}
|
||||
|
||||
[slot*='navigation'] a {
|
||||
--wa-color-text-link: var(--wa-color-text-normal);
|
||||
}
|
||||
[slot='navigation-footer'] {
|
||||
border-block-start: var(--wa-border-width-s) var(--wa-border-style) var(--wa-color-surface-border);
|
||||
|
||||
.wa-flank {
|
||||
--flank-size: 1.25em;
|
||||
}
|
||||
}
|
||||
[slot='main-header'],
|
||||
main,
|
||||
[slot='main-footer'] {
|
||||
max-inline-size: 60rem;
|
||||
margin-inline: auto;
|
||||
}
|
||||
[slot='main-footer'] {
|
||||
border-block-start: var(--wa-border-width-s) var(--wa-border-style) var(--wa-color-surface-border);
|
||||
}
|
||||
[slot='footer'] {
|
||||
--wa-color-text-link: var(--wa-color-text-quiet);
|
||||
background-color: var(--wa-color-surface-lowered);
|
||||
font-size: var(--wa-font-size-s);
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
const sectionAnchors = document.querySelectorAll("[slot*='navigation'] a[href*='#']");
|
||||
sectionAnchors.forEach(sectionAnchor => sectionAnchor.setAttribute('data-drawer', 'close'));
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,445 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en" class="wa-cloak">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Web Awesome Page Demo 2</title>
|
||||
<link rel="stylesheet" href="/dist/styles/themes/default.css" />
|
||||
<link rel="stylesheet" href="/dist/styles/webawesome.css" />
|
||||
<script type="module" src="/dist/webawesome.loader.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<wa-page class="wa-dark">
|
||||
<header slot="header">
|
||||
<div class="wa-cluster">
|
||||
<wa-icon-button name="bars" label="Menu" data-toggle-nav></wa-icon-button>
|
||||
<wa-icon name="record-vinyl"></wa-icon>
|
||||
<span class="wa-heading-m">radiogaga</span>
|
||||
</div>
|
||||
<wa-input id="search-header" placeholder="Search" class="wa-desktop-only" style="max-inline-size: 100%">
|
||||
<wa-icon slot="prefix" name="magnifying-glass"></wa-icon>
|
||||
</wa-input>
|
||||
<div class="wa-cluster">
|
||||
<wa-button appearance="outlined">Log In</wa-button>
|
||||
<wa-button>Sign Up</wa-button>
|
||||
</div>
|
||||
</header>
|
||||
<div slot="navigation-header" class="wa-split">
|
||||
<wa-input id="search-nav-drawer" placeholder="Search" style="max-inline-size: 100%" class="wa-mobile-only">
|
||||
<wa-icon slot="prefix" name="magnifying-glass"></wa-icon>
|
||||
</wa-input>
|
||||
<div class="wa-split">
|
||||
<h2 class="wa-heading-s">For You</h2>
|
||||
<wa-icon-button id="settings" name="gear" label="Settings"></wa-icon-button>
|
||||
</div>
|
||||
</div>
|
||||
<nav slot="navigation">
|
||||
<h3 class="wa-heading-xs">Discover</h3>
|
||||
<ul class="wa-stack wa-gap-0">
|
||||
<li>
|
||||
<a href="#" class="wa-flank">
|
||||
<wa-icon name="house"></wa-icon>
|
||||
<span>Home</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="wa-flank">
|
||||
<wa-icon name="star"></wa-icon>
|
||||
<span>New</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="wa-flank">
|
||||
<wa-icon name="tower-broadcast"></wa-icon>
|
||||
<span>Stations</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<h3 class="wa-heading-xs">Library</h3>
|
||||
<ul class="wa-stack wa-gap-0">
|
||||
<li>
|
||||
<a href="#" class="wa-flank">
|
||||
<wa-icon name="heart"></wa-icon>
|
||||
<span>Favorites</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="wa-flank">
|
||||
<wa-icon name="bars-staggered"></wa-icon>
|
||||
<span>Playlists</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="wa-flank">
|
||||
<wa-icon name="microphone-lines"></wa-icon>
|
||||
<span>Artists</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="wa-flank">
|
||||
<wa-icon name="layer-group"></wa-icon>
|
||||
<span>Albums</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="wa-flank">
|
||||
<wa-icon name="podcast"></wa-icon>
|
||||
<span>Podcasts</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<h3 class="wa-heading-xs">Recently Played</h3>
|
||||
<ul id="recent" class="wa-stack wa-gap-0">
|
||||
<li>
|
||||
<a href="#" class="wa-flank">
|
||||
<wa-icon name="radio" style="background: var(--wa-color-red-90); color: var(--wa-color-red-60)"></wa-icon>
|
||||
<span>Lo-Fi Station</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="wa-flank">
|
||||
<wa-icon
|
||||
name="font-awesome"
|
||||
style="background: var(--wa-color-blue-30); color: var(--wa-color-yellow-90)"
|
||||
></wa-icon>
|
||||
<span>Podcast Awesome</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="wa-flank">
|
||||
<wa-icon
|
||||
name="seedling"
|
||||
style="background: var(--wa-color-green-70); color: var(--wa-color-green-90)"
|
||||
></wa-icon>
|
||||
<div class="wa-stack wa-gap-0">
|
||||
<span>Seasons</span>
|
||||
<span class="wa-caption-s">Blister Soul</span>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
<div slot="main-header">
|
||||
<wa-icon-button id="back" name="chevron-left" label="Back"></wa-icon-button>
|
||||
<wa-tooltip for="back" placement="bottom" distance="2">Back</wa-tooltip>
|
||||
<div class="wa-cluster">
|
||||
<wa-icon-button id="favorite" name="heart" variant="regular" label="Favorite"></wa-icon-button>
|
||||
<wa-tooltip for="favorite" placement="bottom" distance="2">Favorite</wa-tooltip>
|
||||
<wa-icon-button id="options" name="ellipsis" label="Options"></wa-icon-button>
|
||||
<wa-tooltip for="options" placement="bottom" distance="2">Options</wa-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<main>
|
||||
<div class="wa-stack wa-gap-3xl">
|
||||
<div class="wa-flank wa-gap-3xl" style="--content-percentage: 40%">
|
||||
<div class="wa-frame wa-border-radius-l" style="max-inline-size: 40ch">
|
||||
<img
|
||||
src="https://images.unsplash.com/photo-1732430579016-8d5e5ebd3c99?q=20"
|
||||
alt="Home for the Holidays album artwork"
|
||||
/>
|
||||
</div>
|
||||
<div class="wa-split:column wa-align-items-start">
|
||||
<div class="wa-stack" style="margin-block: auto">
|
||||
<h1 class="wa-heading-3xl">Home for the Holidays</h1>
|
||||
<a href="#" class="wa-heading-m">The Shire Choir</a>
|
||||
<div class="wa-cluster wa-caption-m wa-gap-2xs">
|
||||
<span>Holiday</span>
|
||||
<span>•</span>
|
||||
<span>2024</span>
|
||||
<span>•</span>
|
||||
<span>12 songs, 41 minutes 9 seconds</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="play-controls" class="wa-split wa-gap-xl">
|
||||
<div class="wa-cluster wa-gap-xl">
|
||||
<wa-icon-button name="play" label="Play"></wa-icon-button>
|
||||
<wa-icon-button name="shuffle" label="Shuffle"></wa-icon-button>
|
||||
</div>
|
||||
<wa-icon-button name="plus" label="Add to Library"></wa-icon-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ol class="wa-stack wa-gap-0">
|
||||
<li class="wa-split">
|
||||
<span class="wa-flank">
|
||||
<wa-icon name="1"></wa-icon>
|
||||
<span>Fa-La-La-Fellowship</span>
|
||||
</span>
|
||||
<span class="wa-cluster">
|
||||
<span class="wa-caption-m">3:27</span>
|
||||
<wa-icon-button name="ellipsis" label="Song Options"></wa-icon-button>
|
||||
</span>
|
||||
</li>
|
||||
<li class="wa-split">
|
||||
<span class="wa-flank">
|
||||
<wa-icon name="2"></wa-icon>
|
||||
<span>Sleigh Ride</span>
|
||||
</span>
|
||||
<span class="wa-cluster">
|
||||
<span class="wa-caption-m">2:36</span>
|
||||
<wa-icon-button name="ellipsis" label="Song Options"></wa-icon-button>
|
||||
</span>
|
||||
</li>
|
||||
<li class="wa-split">
|
||||
<span class="wa-flank">
|
||||
<wa-icon name="3"></wa-icon>
|
||||
<span>All I Want For Christmas Is Stew</span>
|
||||
</span>
|
||||
<span class="wa-cluster">
|
||||
<span class="wa-caption-m">2:51</span>
|
||||
<wa-icon-button name="ellipsis" label="Song Options"></wa-icon-button>
|
||||
</span>
|
||||
</li>
|
||||
<li class="wa-split">
|
||||
<span class="wa-flank">
|
||||
<wa-icon name="4"></wa-icon>
|
||||
<span>Rockin' Around the Christmas Ent</span>
|
||||
</span>
|
||||
<span class="wa-cluster">
|
||||
<span class="wa-caption-m">3:05</span>
|
||||
<wa-icon-button name="ellipsis" label="Song Options"></wa-icon-button>
|
||||
</span>
|
||||
</li>
|
||||
<li class="wa-split">
|
||||
<span class="wa-flank">
|
||||
<wa-icon name="5"></wa-icon>
|
||||
<span>Merry, Did You Know?</span>
|
||||
</span>
|
||||
<span class="wa-cluster">
|
||||
<span class="wa-caption-m">1:56</span>
|
||||
<wa-icon-button name="ellipsis" label="Song Options"></wa-icon-button>
|
||||
</span>
|
||||
</li>
|
||||
<li class="wa-split">
|
||||
<span class="wa-flank">
|
||||
<wa-icon name="6"></wa-icon>
|
||||
<span>Run Run Shadowfax</span>
|
||||
</span>
|
||||
<span class="wa-cluster">
|
||||
<span class="wa-caption-m">3:32</span>
|
||||
<wa-icon-button name="ellipsis" label="Song Options"></wa-icon-button>
|
||||
</span>
|
||||
</li>
|
||||
<li class="wa-split">
|
||||
<span class="wa-flank">
|
||||
<wa-icon name="7"></wa-icon>
|
||||
<span>You're a Mean One, Mr. Grima</span>
|
||||
</span>
|
||||
<span class="wa-cluster">
|
||||
<span class="wa-caption-m">2:46</span>
|
||||
<wa-icon-button name="ellipsis" label="Song Options"></wa-icon-button>
|
||||
</span>
|
||||
</li>
|
||||
<li class="wa-split">
|
||||
<span class="wa-flank">
|
||||
<wa-icon name="8"></wa-icon>
|
||||
<span>O Come, All Ye Faithful</span>
|
||||
</span>
|
||||
<span class="wa-cluster">
|
||||
<span class="wa-caption-m">3:27</span>
|
||||
<wa-icon-button name="ellipsis" label="Song Options"></wa-icon-button>
|
||||
</span>
|
||||
</li>
|
||||
<li class="wa-split">
|
||||
<span class="wa-flank">
|
||||
<wa-icon name="9"></wa-icon>
|
||||
<span>Do You Hear What I Hear</span>
|
||||
</span>
|
||||
<span class="wa-cluster">
|
||||
<span class="wa-caption-m">2:13</span>
|
||||
<wa-icon-button name="ellipsis" label="Song Options"></wa-icon-button>
|
||||
</span>
|
||||
</li>
|
||||
<li class="wa-split">
|
||||
<span class="wa-flank">
|
||||
<span class="wa-cluster wa-gap-3xs">
|
||||
<wa-icon name="1"></wa-icon>
|
||||
<wa-icon name="0"></wa-icon>
|
||||
</span>
|
||||
<span>Carol of the Horns</span>
|
||||
</span>
|
||||
<span class="wa-cluster">
|
||||
<span class="wa-caption-m">2:55</span>
|
||||
<wa-icon-button name="ellipsis" label="Song Options"></wa-icon-button>
|
||||
</span>
|
||||
</li>
|
||||
<li class="wa-split">
|
||||
<span class="wa-flank">
|
||||
<span class="wa-cluster wa-gap-3xs">
|
||||
<wa-icon name="1"></wa-icon>
|
||||
<wa-icon name="1"></wa-icon>
|
||||
</span>
|
||||
<span>Silent Night</span>
|
||||
</span>
|
||||
<span class="wa-cluster">
|
||||
<span class="wa-caption-m">3:10</span>
|
||||
<wa-icon-button name="ellipsis" label="Song Options"></wa-icon-button>
|
||||
</span>
|
||||
</li>
|
||||
<li class="wa-split">
|
||||
<span class="wa-flank">
|
||||
<span class="wa-cluster wa-gap-3xs">
|
||||
<wa-icon name="1"></wa-icon>
|
||||
<wa-icon name="2"></wa-icon>
|
||||
</span>
|
||||
<span>Wizard Wonderland</span>
|
||||
</span>
|
||||
<span class="wa-cluster">
|
||||
<span class="wa-caption-m">3:22</span>
|
||||
<wa-icon-button name="ellipsis" label="Song Options"></wa-icon-button>
|
||||
</span>
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
</main>
|
||||
<div slot="main-footer" class="wa-grid wa-gap-xl wa-align-items-center">
|
||||
<h2 class="wa-heading-2xl">More You Might Like</h2>
|
||||
<div class="wa-stack wa-gap-xs">
|
||||
<div class="wa-frame wa-border-radius-l">
|
||||
<img src="https://images.unsplash.com/photo-1675219119611-40323b738563?q=20" alt="" />
|
||||
</div>
|
||||
<span class="wa-heading-s">Festival of Lights</span>
|
||||
<span class="wa-caption-s">Station</span>
|
||||
</div>
|
||||
<div class="wa-stack wa-gap-xs">
|
||||
<div class="wa-frame wa-border-radius-l">
|
||||
<img src="https://images.unsplash.com/photo-1481930916222-5ec4696fc0f2?q=20" alt="" />
|
||||
</div>
|
||||
<span class="wa-heading-s">Holiday Cheer</span>
|
||||
<span class="wa-caption-s">Essential Playlist</span>
|
||||
</div>
|
||||
<div class="wa-stack wa-gap-xs">
|
||||
<div class="wa-frame wa-border-radius-l">
|
||||
<img src="https://images.unsplash.com/photo-1667514627762-521b1c815a89?q=20" alt="" />
|
||||
</div>
|
||||
<span class="wa-heading-s">Nursery Rhymes from the Shire</span>
|
||||
<span class="wa-caption-s">The Shire Choir</span>
|
||||
</div>
|
||||
</div>
|
||||
</wa-page>
|
||||
|
||||
<style>
|
||||
wa-page {
|
||||
--menu-width: 30ch;
|
||||
--wa-tooltip-arrow-size: 0;
|
||||
background-color: var(--wa-color-surface-lowered);
|
||||
}
|
||||
|
||||
wa-page[view='mobile'] {
|
||||
--menu-width: auto;
|
||||
|
||||
[slot*='main'],
|
||||
main {
|
||||
padding: var(--wa-space-xl);
|
||||
}
|
||||
}
|
||||
|
||||
wa-page,
|
||||
[slot='header'],
|
||||
wa-page[view='desktop'] [slot*='navigation'] {
|
||||
background-color: var(--wa-color-surface-lowered);
|
||||
}
|
||||
wa-page[view='mobile'] [slot*='navigation'] {
|
||||
padding: 0;
|
||||
}
|
||||
wa-page::part(base) {
|
||||
background-color: var(--wa-color-surface-lowered);
|
||||
}
|
||||
[slot='header'] {
|
||||
background: linear-gradient(to bottom, var(--wa-color-surface-raised), var(--wa-color-surface-lowered));
|
||||
}
|
||||
[slot='navigation-header'],
|
||||
[slot='main-header'] {
|
||||
padding-block-end: 0 !important;
|
||||
padding-block-start: var(--wa-space-3xl);
|
||||
}
|
||||
[slot='navigation'] {
|
||||
a {
|
||||
--wa-color-text-link: var(--wa-color-text-normal);
|
||||
--wa-link-decoration-default: none;
|
||||
--wa-link-decoration-hover: none;
|
||||
--flank-size: 2rem;
|
||||
font-weight: var(--wa-font-weight-action);
|
||||
gap: 0.5rem;
|
||||
}
|
||||
ul {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
|
||||
a {
|
||||
border-radius: var(--wa-border-radius-m);
|
||||
padding: var(--wa-space-xs);
|
||||
|
||||
&:hover {
|
||||
background-color: color-mix(in oklab, var(--wa-color-surface-default), var(--wa-color-brand-fill-quiet));
|
||||
}
|
||||
}
|
||||
}
|
||||
wa-icon {
|
||||
align-items: center;
|
||||
aspect-ratio: 1;
|
||||
color: var(--wa-color-brand-fill-loud);
|
||||
display: flex;
|
||||
height: var(--flank-size);
|
||||
justify-content: center;
|
||||
}
|
||||
#recent wa-icon {
|
||||
border-radius: var(--wa-border-radius-s);
|
||||
}
|
||||
}
|
||||
|
||||
[slot='main-header'] {
|
||||
border-block-start: var(--wa-border-width-s) var(--wa-border-style) var(--wa-color-surface-border);
|
||||
border-inline: var(--wa-border-width-s) var(--wa-border-style) var(--wa-color-surface-border);
|
||||
border-radius: var(--wa-border-radius-l) var(--wa-border-radius-l) 0 0;
|
||||
}
|
||||
main,
|
||||
[slot*='main'] {
|
||||
margin-inline: var(--wa-space-m);
|
||||
}
|
||||
main ol li {
|
||||
padding: var(--wa-space-m);
|
||||
|
||||
&:hover {
|
||||
background-color: color-mix(in oklab, var(--wa-color-surface-default), var(--wa-color-brand-fill-quiet));
|
||||
}
|
||||
|
||||
&:not(:first-child) {
|
||||
border-block-start: var(--wa-border-width-s) var(--wa-border-style) var(--wa-color-surface-border);
|
||||
}
|
||||
|
||||
.wa-flank {
|
||||
--flank-size: 2rem;
|
||||
}
|
||||
}
|
||||
main,
|
||||
[slot='main-footer'] {
|
||||
border-inline: var(--wa-border-width-s) var(--wa-border-style) var(--wa-color-surface-border);
|
||||
}
|
||||
main,
|
||||
[slot='main-header'] {
|
||||
background-color: var(--wa-color-surface-raised);
|
||||
}
|
||||
#play-controls wa-icon-button::part(base) {
|
||||
border: var(--wa-border-width-l) var(--wa-border-style) currentColor;
|
||||
border-radius: var(--wa-border-radius-circle);
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
#play-controls wa-icon-button[name='play']::part(base) {
|
||||
background-color: var(--wa-color-brand-fill-loud);
|
||||
border: none;
|
||||
color: var(--wa-color-brand-on-loud);
|
||||
font-size: 3rem;
|
||||
padding: 0.5em 0.45em 0.5em 0.55em;
|
||||
}
|
||||
[slot='main-footer'].wa-grid > * {
|
||||
max-inline-size: 30ch;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
const sectionAnchors = document.querySelectorAll("[slot*='navigation'] a[href*='#']");
|
||||
sectionAnchors.forEach(sectionAnchor => sectionAnchor.setAttribute('data-drawer', 'close'));
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -56,10 +56,8 @@ 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` +
|
||||
`<link rel="stylesheet" href="${cdnUrl}styles/themes/default.css">\n` +
|
||||
`<link rel="stylesheet" href="${cdnUrl}styles/webawesome.css">\n` +
|
||||
`<link rel="stylesheet" href="${cdnUrl}styles/utilities.css">\n\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}';
|
||||
const js = '';
|
||||
|
||||
38
packages/webawesome/docs/assets/scripts/color-scheme.js
Normal file
38
packages/webawesome/docs/assets/scripts/color-scheme.js
Normal file
@@ -0,0 +1,38 @@
|
||||
import { doViewTransition } from '../scripts/view-transitions.js';
|
||||
|
||||
//
|
||||
// Updates the color scheme when a color scheme selector changes
|
||||
//
|
||||
function updateTheme(value) {
|
||||
localStorage.setItem('color-scheme', value);
|
||||
|
||||
const isDark = value === 'dark' || (value === 'auto' && window.matchMedia('(prefers-color-scheme: dark)').matches);
|
||||
|
||||
doViewTransition(() => {
|
||||
document.documentElement.classList.toggle('wa-dark', isDark);
|
||||
});
|
||||
|
||||
// Sync all selectors
|
||||
document.querySelectorAll('.color-scheme-selector').forEach(el => (el.value = value));
|
||||
}
|
||||
|
||||
// Handle changes
|
||||
document.addEventListener('input', e => {
|
||||
if (e.target.matches('.color-scheme-selector')) {
|
||||
updateTheme(e.target.value);
|
||||
}
|
||||
});
|
||||
|
||||
// Handle backslash key toggle
|
||||
document.addEventListener('keydown', e => {
|
||||
if (e.key === '\\' && !e.composedPath().some(el => el.tagName === 'INPUT')) {
|
||||
const current = localStorage.getItem('color-scheme') || 'auto';
|
||||
const isDark =
|
||||
current === 'dark' || (current === 'auto' && window.matchMedia('(prefers-color-scheme: dark)').matches);
|
||||
updateTheme(isDark ? 'light' : 'dark');
|
||||
}
|
||||
});
|
||||
|
||||
// Initialize
|
||||
const saved = localStorage.getItem('color-scheme') || 'auto';
|
||||
updateTheme(saved);
|
||||
@@ -1,35 +0,0 @@
|
||||
function debounce(func, wait) {
|
||||
let timeout;
|
||||
return function (...args) {
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(() => func.apply(this, args), wait);
|
||||
};
|
||||
}
|
||||
|
||||
function updateResults(input) {
|
||||
const filter = input.value.toLowerCase().trim();
|
||||
let filtered = Boolean(filter);
|
||||
|
||||
for (let grid of document.querySelectorAll('.index-grid')) {
|
||||
grid.classList.toggle('filtered', filtered);
|
||||
|
||||
for (let item of grid.querySelectorAll('a:has(> wa-card)')) {
|
||||
let isMatch = true;
|
||||
|
||||
if (filter) {
|
||||
const content = item.textContent.toLowerCase() + ' ' + (item.getAttribute('data-keywords') + ' ');
|
||||
isMatch = content.includes(filter);
|
||||
}
|
||||
|
||||
item.hidden = !isMatch;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const debouncedUpdateResults = debounce(updateResults, 300);
|
||||
|
||||
document.documentElement.addEventListener('input', e => {
|
||||
if (e.target?.matches('#block-filter wa-input')) {
|
||||
debouncedUpdateResults(e.target);
|
||||
}
|
||||
});
|
||||
@@ -2,7 +2,7 @@
|
||||
(async () => {
|
||||
const hostname = new URL(document.baseURI).hostname;
|
||||
|
||||
// Only diff on localhost. We dont need to show hydration errors on main site. Only locally.
|
||||
// Only diff on localhost. We don't need to show hydration errors on main site. Only locally.
|
||||
if (hostname !== 'localhost') {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,172 +0,0 @@
|
||||
const my = (globalThis.my = new EventTarget());
|
||||
export default my;
|
||||
|
||||
class PersistedArray extends Array {
|
||||
constructor(key) {
|
||||
super();
|
||||
this.key = key;
|
||||
|
||||
if (this.key) {
|
||||
this.fromLocalStorage();
|
||||
}
|
||||
|
||||
// Items were updated in another tab
|
||||
addEventListener('storage', event => {
|
||||
if (event.key === this.key || !event.key) {
|
||||
this.fromLocalStorage();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Update data from local storage
|
||||
*/
|
||||
fromLocalStorage() {
|
||||
// First, empty the array
|
||||
this.splice(0, this.length);
|
||||
|
||||
// Then, fill it with the data from local storage
|
||||
let saved = localStorage[this.key] ? JSON.parse(localStorage[this.key]) : null;
|
||||
|
||||
if (saved) {
|
||||
this.push(...saved);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write data to local storage
|
||||
*/
|
||||
toLocalStorage() {
|
||||
if (this.length > 0) {
|
||||
localStorage[this.key] = JSON.stringify(this);
|
||||
} else {
|
||||
delete localStorage[this.key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SavedEntities extends EventTarget {
|
||||
constructor({ key, type, url }) {
|
||||
super();
|
||||
this.key = key;
|
||||
this.type = type;
|
||||
this.url = url ?? type + 's';
|
||||
this.saved = new PersistedArray(key);
|
||||
|
||||
let all = this;
|
||||
this.entityPrototype = {
|
||||
type: this.type,
|
||||
baseUrl: this.baseUrl,
|
||||
|
||||
get url() {
|
||||
return all.getURL(this);
|
||||
},
|
||||
|
||||
get parentUrl() {
|
||||
return all.getParentURL(this);
|
||||
},
|
||||
|
||||
delete() {
|
||||
all.delete(this);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
getUid() {
|
||||
if (this.saved.length === 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
let uids = new Set(this.saved.map(p => p.uid));
|
||||
|
||||
// Find first available number
|
||||
for (let i = 1; i <= this.saved.length + 1; i++) {
|
||||
if (!uids.has(i)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get baseUrl() {
|
||||
return `/docs/${this.url}/`;
|
||||
}
|
||||
|
||||
getURL(entity) {
|
||||
return this.getParentURL(entity) + entity.search;
|
||||
}
|
||||
|
||||
getParentURL(entity) {
|
||||
return this.baseUrl + entity.id + '/';
|
||||
}
|
||||
|
||||
getObject(entity) {
|
||||
let ret = Object.create(this.entityPrototype, Object.getOwnPropertyDescriptors(entity));
|
||||
// debugger;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save an entity, either by updating its existing entry or creating a new one
|
||||
* @param {object} entity
|
||||
*/
|
||||
save(entity) {
|
||||
if (!entity.uid) {
|
||||
// First time saving
|
||||
entity.uid = this.getUid();
|
||||
}
|
||||
|
||||
let savedPalettes = this.saved;
|
||||
let existingIndex = entity.uid ? this.saved.findIndex(p => p.uid === entity.uid) : -1;
|
||||
let newIndex = existingIndex > -1 ? existingIndex : savedPalettes.length;
|
||||
|
||||
this.saved.splice(newIndex, 1, entity);
|
||||
|
||||
this.saved.toLocalStorage();
|
||||
|
||||
this.dispatchEvent(new CustomEvent('save', { detail: this.getObject(entity) }));
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
delete(entity) {
|
||||
let count = this.saved.length;
|
||||
|
||||
if (count === 0 || !entity?.uid) {
|
||||
// No stored entities or this entity has not been saved
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO improve UX of this
|
||||
if (!confirm(`Are you sure you want to delete ${this.type} “${entity.title}”?`)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let index; (index = this.saved.findIndex(p => p.uid === entity.uid)) > -1; ) {
|
||||
this.saved.splice(index, 1);
|
||||
}
|
||||
|
||||
if (this.saved.length === count) {
|
||||
// Nothing was removed
|
||||
return;
|
||||
}
|
||||
|
||||
this.saved.toLocalStorage();
|
||||
|
||||
this.dispatchEvent(new CustomEvent('delete', { detail: this.getObject(entity) }));
|
||||
}
|
||||
|
||||
dispatchEvent(event) {
|
||||
super.dispatchEvent(event);
|
||||
my.dispatchEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
my.palettes = new SavedEntities({
|
||||
key: 'savedPalettes',
|
||||
type: 'palette',
|
||||
});
|
||||
|
||||
my.themes = new SavedEntities({
|
||||
key: 'savedThemes',
|
||||
type: 'theme',
|
||||
});
|
||||
@@ -1,52 +0,0 @@
|
||||
import { domChange, nextFrame, ThemeAspect } from './theme-picker.js';
|
||||
|
||||
const presetTheme = new ThemeAspect({
|
||||
defaultValue: 'default',
|
||||
key: 'presetTheme',
|
||||
picker: 'wa-select.preset-theme-selector',
|
||||
|
||||
applyChange(options = {}) {
|
||||
const oldStylesheets = [...document.querySelectorAll('#theme-stylesheet')];
|
||||
const oldStylesheet = oldStylesheets.pop();
|
||||
|
||||
if (oldStylesheets.length > 0) {
|
||||
// Remove all but the last one
|
||||
for (let stylesheet of oldStylesheets) {
|
||||
stylesheet.remove();
|
||||
}
|
||||
}
|
||||
|
||||
const href = `/dist/styles/themes/${this.value}.css`;
|
||||
|
||||
if (!oldStylesheet || oldStylesheet.getAttribute('href') !== href) {
|
||||
const newStylesheet = document.createElement('link');
|
||||
Object.assign(newStylesheet, { href, id: 'theme-stylesheet', rel: 'preload', as: 'style' });
|
||||
oldStylesheet.after(newStylesheet);
|
||||
|
||||
newStylesheet.addEventListener(
|
||||
'load',
|
||||
e => {
|
||||
domChange(
|
||||
async instant => {
|
||||
// Swap stylesheets
|
||||
newStylesheet.rel = 'stylesheet';
|
||||
|
||||
if (instant) {
|
||||
// If no VT, delay by 1 frame to make it smoother
|
||||
await nextFrame();
|
||||
}
|
||||
|
||||
oldStylesheet.remove();
|
||||
},
|
||||
{ behavior: 'smooth', ...options },
|
||||
);
|
||||
},
|
||||
{ once: true },
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
window.addEventListener('turbo:render', e => {
|
||||
presetTheme.applyChange({ behavior: 'instant' });
|
||||
});
|
||||
File diff suppressed because one or more lines are too long
@@ -1,8 +0,0 @@
|
||||
globalThis.Prism = globalThis.Prism || {};
|
||||
globalThis.Prism.manual = true;
|
||||
|
||||
await import('./prism-downloaded.js');
|
||||
|
||||
Prism.plugins.customClass.prefix('code-');
|
||||
|
||||
export default Prism;
|
||||
82
packages/webawesome/docs/assets/scripts/search-list.js
Normal file
82
packages/webawesome/docs/assets/scripts/search-list.js
Normal file
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* Live search functionality for component lists
|
||||
*
|
||||
* Required HTML structure:
|
||||
* <div class="search-list">
|
||||
* <input class="search-list-input" type="search" placeholder="Search...">
|
||||
*
|
||||
* <h2>Category Name</h2> <!-- Optional heading; h1-h6 all work -->
|
||||
* <section class="search-list-grid">
|
||||
* <a href="...">
|
||||
* <span class="page-name">Component Title</span>
|
||||
* </a>
|
||||
* </section>
|
||||
*
|
||||
* <div class="search-list-empty" hidden>No results found</div>
|
||||
* </div>
|
||||
*
|
||||
* Usage: import './search-list.js'
|
||||
*/
|
||||
export function enableSearchLists() {
|
||||
document.querySelectorAll('.search-list').forEach(container => {
|
||||
const input = container.querySelector('.search-list-input');
|
||||
const emptyState = container.querySelector('.search-list-empty');
|
||||
|
||||
if (!input || !emptyState) return;
|
||||
|
||||
let timeout;
|
||||
|
||||
input.addEventListener('input', e => {
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(() => {
|
||||
const query = e.target.value.toLowerCase().trim();
|
||||
let totalVisible = 0;
|
||||
|
||||
// Handle sections with headings
|
||||
container.querySelectorAll('h1, h2, h3, h4, h5, h6').forEach(heading => {
|
||||
const section = heading.nextElementSibling;
|
||||
if (!section) return;
|
||||
|
||||
let sectionVisible = 0;
|
||||
|
||||
section.querySelectorAll('a').forEach(card => {
|
||||
const title = card.querySelector('.page-name')?.textContent?.toLowerCase() || '';
|
||||
const visible = !query || title.includes(query);
|
||||
card.style.display = visible ? '' : 'none';
|
||||
if (visible) sectionVisible++;
|
||||
});
|
||||
|
||||
heading.style.display = sectionVisible > 0 ? '' : 'none';
|
||||
section.style.display = sectionVisible > 0 ? '' : 'none';
|
||||
totalVisible += sectionVisible;
|
||||
});
|
||||
|
||||
// Handle standalone sections without headings
|
||||
container.querySelectorAll('.search-list-grid').forEach(section => {
|
||||
const prevElement = section.previousElementSibling;
|
||||
const hasHeading = prevElement && /^H[1-6]$/.test(prevElement.tagName);
|
||||
|
||||
if (!hasHeading) {
|
||||
section.querySelectorAll('a').forEach(card => {
|
||||
const title = card.querySelector('.page-name')?.textContent?.toLowerCase() || '';
|
||||
const visible = !query || title.includes(query);
|
||||
card.style.display = visible ? '' : 'none';
|
||||
if (visible) totalVisible++;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
emptyState.hidden = totalVisible > 0;
|
||||
}, 300);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Initialize when DOM is ready
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', enableSearchLists);
|
||||
} else {
|
||||
enableSearchLists();
|
||||
}
|
||||
|
||||
window.addEventListener('turbo:load', enableSearchLists);
|
||||
@@ -1,120 +0,0 @@
|
||||
import my from '/assets/scripts/my.js';
|
||||
|
||||
const sidebar = {
|
||||
addChild(a, parentA) {
|
||||
let parentLi = parentA.closest('li');
|
||||
let ul = parentLi.querySelector(':scope > ul');
|
||||
ul ??= parentLi.appendChild(document.createElement('ul'));
|
||||
let li = document.createElement('li');
|
||||
li.append(a);
|
||||
ul.appendChild(li);
|
||||
|
||||
// If we are on the same page, update the current link
|
||||
let url = location.href.replace(/#.+$/, '');
|
||||
if (url.startsWith(a.href)) {
|
||||
// Remove existing current
|
||||
for (let current of document.querySelectorAll('#sidebar a.current')) {
|
||||
current.classList.remove('current');
|
||||
}
|
||||
|
||||
a.classList.add('current');
|
||||
}
|
||||
|
||||
return a;
|
||||
},
|
||||
|
||||
removeLink(a) {
|
||||
if (!a || !a.isConnected) {
|
||||
// Link doesn't exist or is already removed
|
||||
return;
|
||||
}
|
||||
|
||||
let li = a?.closest('li');
|
||||
let ul = li?.closest('ul');
|
||||
let parentA = ul?.closest('li')?.querySelector(':scope > a');
|
||||
|
||||
li?.remove();
|
||||
if (ul?.children.length === 0) {
|
||||
ul.remove();
|
||||
}
|
||||
|
||||
if (a.classList.contains('current')) {
|
||||
// If the deleted palette was the current one, the current one is now the parent
|
||||
parentA.classList.add('current');
|
||||
}
|
||||
},
|
||||
|
||||
findEntity(entity) {
|
||||
return document.querySelector(`#sidebar a[href^="${entity.baseUrl}"][data-uid="${entity.uid}"]`);
|
||||
},
|
||||
|
||||
renderEntity(entity) {
|
||||
let { url, parentUrl } = entity;
|
||||
|
||||
// Find parent
|
||||
let parentA = document.querySelector(`#sidebar a[href="${parentUrl}"]`);
|
||||
let parentLi = parentA?.closest('li');
|
||||
|
||||
if (!parentLi) {
|
||||
throw new Error(`Cannot find parent url ${parentUrl}`);
|
||||
}
|
||||
|
||||
// Find existing
|
||||
let a = this.findEntity(entity);
|
||||
let alreadyExisted = !!a;
|
||||
|
||||
a ??= document.createElement('a');
|
||||
|
||||
a.textContent = entity.title;
|
||||
a.href = url;
|
||||
|
||||
if (!alreadyExisted) {
|
||||
a.dataset.uid = entity.uid;
|
||||
|
||||
a = sidebar.addChild(a, parentA);
|
||||
|
||||
// This is mainly to port Pro badges
|
||||
let badges = Array.from(parentLi.querySelectorAll(':scope > wa-badge'), badge => badge.cloneNode(true));
|
||||
|
||||
let append = [...badges];
|
||||
|
||||
if (entity.delete) {
|
||||
let deleteButton = Object.assign(document.createElement('wa-icon-button'), {
|
||||
name: 'trash',
|
||||
label: 'Delete',
|
||||
className: 'delete',
|
||||
});
|
||||
deleteButton.addEventListener('click', () => entity.delete());
|
||||
append.push(deleteButton);
|
||||
}
|
||||
|
||||
if (append.length > 0) {
|
||||
a.closest('li').append(' ', ...append);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
render() {
|
||||
for (let type in my) {
|
||||
let controller = my[type];
|
||||
|
||||
if (!controller.saved) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (let entity of controller.saved) {
|
||||
let object = controller.getObject(entity);
|
||||
this.renderEntity(object);
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
globalThis.sidebar = sidebar;
|
||||
|
||||
// Update sidebar when my saved stuff changes
|
||||
my.addEventListener('delete', e => sidebar.removeLink(sidebar.findEntity(e.detail)));
|
||||
my.addEventListener('save', e => sidebar.renderEntity(e.detail));
|
||||
|
||||
sidebar.render();
|
||||
window.addEventListener('turbo:render', () => sidebar.render());
|
||||
@@ -1,37 +0,0 @@
|
||||
/**
|
||||
* Sync iframe height with its content page (for same-origin iframes)
|
||||
* NOT CURRENTLY USED ANYWHERE
|
||||
*/
|
||||
for (let iframe of document.querySelectorAll('iframe')) {
|
||||
if (iframe.contentDocument) {
|
||||
// Already loaded
|
||||
syncIframeHeight(iframe);
|
||||
}
|
||||
|
||||
iframe.onload = () => {
|
||||
console.log('iframe loaded');
|
||||
if (iframe.contentDocument) {
|
||||
// Same origin
|
||||
iframe.contentWindow.iframe = iframe;
|
||||
syncIframeHeight(iframe);
|
||||
const resizeObserver = new ResizeObserver(entries => {
|
||||
for (let entry of entries) {
|
||||
if (entry.target === iframe.contentDocument.body) {
|
||||
syncIframeHeight(iframe);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
resizeObserver.observe(iframe.contentDocument.body);
|
||||
window.addEventListener('turbo:render', syncIframeHeight(iframe));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function syncIframeHeight(iframe) {
|
||||
iframe.style.height = '0px';
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
iframe.style.height = iframe.contentDocument.body.scrollHeight + 'px';
|
||||
});
|
||||
}
|
||||
@@ -1,132 +0,0 @@
|
||||
import { domChange } from './util/dom-change.js';
|
||||
export { domChange };
|
||||
|
||||
export function nextFrame() {
|
||||
return new Promise(resolve => requestAnimationFrame(resolve));
|
||||
}
|
||||
|
||||
export class ThemeAspect {
|
||||
constructor(options) {
|
||||
Object.assign(this, options);
|
||||
this.set();
|
||||
|
||||
// Update when local storage changes.
|
||||
// That way changes in one window will propagate to others (including iframes).
|
||||
window.addEventListener('storage', event => {
|
||||
if (event.key === this.key) {
|
||||
this.set();
|
||||
}
|
||||
});
|
||||
|
||||
// Listen for selections
|
||||
document.addEventListener('change', event => {
|
||||
const picker = event.target.closest(this.picker);
|
||||
if (picker) {
|
||||
this.set(picker.value);
|
||||
}
|
||||
});
|
||||
|
||||
['turbo:before-render', 'turbo:before-stream-render', 'turbo:before-frame-render'].forEach(eventName => {
|
||||
document.addEventListener(eventName, e => {
|
||||
const newElement = e.detail.newBody || e.detail.newFrame || e.detail.newStream;
|
||||
if (newElement) {
|
||||
this.syncUI(newElement);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
get() {
|
||||
return localStorage.getItem(this.key) ?? this.defaultValue;
|
||||
}
|
||||
|
||||
computed = {};
|
||||
|
||||
get computedValue() {
|
||||
if (this.value in this.computed) {
|
||||
return this.computed[this.value];
|
||||
}
|
||||
|
||||
return this.value;
|
||||
}
|
||||
|
||||
set(value = this.get()) {
|
||||
if (value === this.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.value = value;
|
||||
|
||||
if (this.value === this.defaultValue) {
|
||||
localStorage.removeItem(this.key);
|
||||
} else {
|
||||
localStorage.setItem(this.key, this.value);
|
||||
}
|
||||
|
||||
this.applyChange();
|
||||
this.syncUI();
|
||||
}
|
||||
|
||||
syncUI(container = document) {
|
||||
for (let picker of container.querySelectorAll(this.picker)) {
|
||||
picker.setAttribute('value', this.value);
|
||||
picker.value = this.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const colorScheme = new ThemeAspect({
|
||||
defaultValue: 'auto',
|
||||
key: 'colorScheme',
|
||||
picker: 'wa-select.color-scheme-selector',
|
||||
|
||||
computed: {
|
||||
get auto() {
|
||||
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
||||
},
|
||||
},
|
||||
|
||||
applyChange() {
|
||||
// Toggle the dark mode class
|
||||
domChange(() => {
|
||||
let dark = this.computedValue === 'dark';
|
||||
document.documentElement.classList.toggle(`wa-dark`, dark);
|
||||
document.documentElement.dispatchEvent(new CustomEvent('wa-color-scheme-change', { detail: { dark } }));
|
||||
syncViewportDemoColorSchemes();
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
function syncViewportDemoColorSchemes() {
|
||||
const isDark = document.documentElement.classList.contains('wa-dark');
|
||||
|
||||
// Update viewport demo color schemes in code examples
|
||||
document.querySelectorAll('.code-example.is-viewport-demo wa-viewport-demo').forEach(demo => {
|
||||
demo.querySelectorAll('iframe').forEach(iframe => {
|
||||
iframe.contentWindow.document.documentElement?.classList?.toggle('wa-dark', isDark);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Update the color scheme when the preference changes
|
||||
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => colorScheme.set());
|
||||
|
||||
// Toggle color scheme with backslash
|
||||
document.addEventListener('keydown', event => {
|
||||
if (
|
||||
event.key === '\\' &&
|
||||
!event.composedPath().some(el => ['input', 'textarea'].includes(el?.tagName?.toLowerCase()))
|
||||
) {
|
||||
event.preventDefault();
|
||||
colorScheme.set(colorScheme.get() === 'dark' ? 'light' : 'dark');
|
||||
}
|
||||
});
|
||||
|
||||
// When rendering a code example with a viewport demo, set the theme to match initially
|
||||
document.querySelectorAll('.code-example.is-viewport-demo wa-viewport-demo iframe').forEach(iframe => {
|
||||
const isDark = document.documentElement.classList.contains('wa-dark');
|
||||
|
||||
iframe.addEventListener('load', () => {
|
||||
iframe.contentWindow.document.documentElement?.classList?.toggle('wa-dark', isDark);
|
||||
});
|
||||
});
|
||||
81
packages/webawesome/docs/assets/scripts/theme.js
Normal file
81
packages/webawesome/docs/assets/scripts/theme.js
Normal file
@@ -0,0 +1,81 @@
|
||||
import { doViewTransition } from '../scripts/view-transitions.js';
|
||||
|
||||
//
|
||||
// Updates the theme when a theme selector changes
|
||||
//
|
||||
async function updateTheme(value, isInitialLoad = false) {
|
||||
const body = document.body;
|
||||
|
||||
if (!isInitialLoad) {
|
||||
// Add fade-out class
|
||||
body.classList.add('theme-transitioning');
|
||||
|
||||
// Wait for fade-out to complete
|
||||
await new Promise(resolve => {
|
||||
const handleTransitionEnd = event => {
|
||||
if (event.target === body && event.propertyName === 'opacity') {
|
||||
body.removeEventListener('transitionend', handleTransitionEnd);
|
||||
resolve();
|
||||
}
|
||||
};
|
||||
body.addEventListener('transitionend', handleTransitionEnd);
|
||||
});
|
||||
}
|
||||
|
||||
localStorage.setItem('theme', value);
|
||||
|
||||
// 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);
|
||||
|
||||
// Update theme classes
|
||||
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(() => {
|
||||
// Update the theme
|
||||
if (themeStylesheet) {
|
||||
themeStylesheet.href = href;
|
||||
}
|
||||
|
||||
htmlElement.classList.remove(...classesToRemove);
|
||||
|
||||
// Add the new theme, brand, and palette classes
|
||||
htmlElement.classList.add(`wa-theme-${value}`);
|
||||
htmlElement.classList.add(`wa-brand-${brand}`);
|
||||
htmlElement.classList.add(`wa-palette-${palette}`);
|
||||
|
||||
// Sync all theme selectors
|
||||
document.querySelectorAll('.theme-selector').forEach(el => (el.value = value));
|
||||
});
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle changes
|
||||
document.addEventListener('input', event => {
|
||||
if (event.target.matches('.theme-selector')) {
|
||||
updateTheme(event.target.value);
|
||||
}
|
||||
});
|
||||
|
||||
// Initialize
|
||||
const savedTheme = localStorage.getItem('theme') || 'default';
|
||||
updateTheme(savedTheme, true);
|
||||
@@ -1,6 +0,0 @@
|
||||
/**
|
||||
* Get import code for remixed themes and tweaked palettes.
|
||||
*/
|
||||
export { cdnUrl, hueRanges, hues, selectors, tints, urls } from '../data/index.js';
|
||||
export { default as Permalink } from './permalink.js';
|
||||
export { getThemeCode } from './tweak/code.js';
|
||||
@@ -1,91 +0,0 @@
|
||||
/**
|
||||
* Get import code for remixed themes and tweaked palettes.
|
||||
*/
|
||||
import { selectors, themeConfig } from '../../data/theming.js';
|
||||
import { deepEach, deepGet } from '/assets/scripts/util/deep.js';
|
||||
|
||||
export function cssImport(url, options = {}) {
|
||||
let { language = 'html', cdnUrl = '/dist/', attributes } = options;
|
||||
url = cdnUrl + url;
|
||||
|
||||
if (language === 'css') {
|
||||
return `@import url('${url}');`;
|
||||
} else {
|
||||
attributes = attributes ? ` ${attributes}` : '';
|
||||
return `<link rel="stylesheet" href="${url}"${attributes} />`;
|
||||
}
|
||||
}
|
||||
|
||||
export function cssLiteral(value, options = {}) {
|
||||
let { language = 'html' } = options;
|
||||
|
||||
if (language === 'css') {
|
||||
return value;
|
||||
} else {
|
||||
return `<style${options.attributes ?? ''}>\n${value}\n</style>`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get code for a theme, including tweaks
|
||||
* @param {*} theme
|
||||
* @param {*} options
|
||||
* @returns
|
||||
*/
|
||||
export function getThemeCode(theme, options = {}) {
|
||||
let urls = [];
|
||||
let declarations = [];
|
||||
let id = options.id ?? theme.base ?? 'default';
|
||||
|
||||
deepEach(themeConfig, (config, aspect, obj, path) => {
|
||||
if (!config?.default) {
|
||||
// We're not in a config object
|
||||
return;
|
||||
}
|
||||
|
||||
let value = deepGet(theme, [...path, aspect]);
|
||||
|
||||
if (!value && value !== 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (config.url) {
|
||||
// This is implemented by pulling in different CSS files
|
||||
urls.push(config.url(value));
|
||||
} else {
|
||||
if (config.cssProperty) {
|
||||
declarations.push(`${config.cssProperty}: ${value};`);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let ret = urls.map(url => cssImport(url, options)).join('\n');
|
||||
|
||||
if (declarations.length > 0) {
|
||||
let cssCode = cssRule(selectors.theme(id), declarations, options);
|
||||
|
||||
if (theme.icon?.kit) {
|
||||
let faKitAttribute = ` data-fa-kit-code="${theme.icon.kit}"`;
|
||||
options.attributes ??= '';
|
||||
options.attributes += faKitAttribute;
|
||||
cssCode =
|
||||
`/* Note: To use Font Awesome Pro icons,\n set ${faKitAttribute} on the <link> (or any other) element */\n\n` +
|
||||
cssCode;
|
||||
}
|
||||
|
||||
cssCode = cssLiteral(cssCode, options);
|
||||
|
||||
if (ret) {
|
||||
ret += '\n\n' + cssCode;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
export function cssRule(selector, declarations, { indent = ' ' } = {}) {
|
||||
selector = Array.isArray(selector) ? selector.flat().join(',\n') : selector;
|
||||
declarations = Array.isArray(declarations) ? declarations.flat() : declarations;
|
||||
declarations = declarations.map(declaration => indent + declaration.trim()).join('\n');
|
||||
return `${selector} {\n${declarations.trimEnd()}\n}`;
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
export function normalizeAngles(angles) {
|
||||
// First, normalize
|
||||
angles = angles.map(h => ((h % 360) + 360) % 360);
|
||||
|
||||
// Remove top and bottom 25% and find average
|
||||
let averageHue =
|
||||
angles
|
||||
.toSorted((a, b) => a - b)
|
||||
.slice(angles.length / 4, -angles.length / 4)
|
||||
.reduce((a, b) => a + b, 0) / angles.length;
|
||||
|
||||
for (let i = 0; i < angles.length; i++) {
|
||||
let h = angles[i];
|
||||
let prevHue = angles[i - 1];
|
||||
let delta = h - prevHue;
|
||||
|
||||
if (Math.abs(delta) > 180) {
|
||||
let equivalent = [h + 360, h - 360];
|
||||
// Offset hue to minimize difference in the direction that brings it closer to the average
|
||||
let delta = h - averageHue;
|
||||
|
||||
if (Math.abs(equivalent[0] - prevHue) <= Math.abs(equivalent[1] - prevHue)) {
|
||||
angles[i] = equivalent[0];
|
||||
} else {
|
||||
angles[i] = equivalent[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return angles;
|
||||
}
|
||||
|
||||
export function subtractAngles(θ1, θ2) {
|
||||
let [a, b] = normalizeAngles([θ1, θ2]);
|
||||
return a - b;
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
/**
|
||||
* Picks a random element from an array.
|
||||
* @param {any[]} arr
|
||||
*/
|
||||
export function sample(arr) {
|
||||
if (!Array.isArray(arr)) {
|
||||
return arr;
|
||||
}
|
||||
|
||||
if (arr.length < 2) {
|
||||
return arr[0];
|
||||
}
|
||||
|
||||
let index = Math.floor(Math.random() * arr.length);
|
||||
|
||||
return arr[index];
|
||||
}
|
||||
@@ -1,180 +0,0 @@
|
||||
/**
|
||||
* @typedef { string | number | Symbol } Property
|
||||
* @typedef { (value: any, key: Property, parent: object, path: Property[]) => any } EachCallback
|
||||
*/
|
||||
|
||||
export function isPlainObject(obj) {
|
||||
return isObject(obj, 'Object');
|
||||
}
|
||||
|
||||
export function isObject(obj, type) {
|
||||
if (!obj || typeof obj !== 'object') {
|
||||
return false;
|
||||
}
|
||||
|
||||
let proto = Object.getPrototypeOf(obj);
|
||||
return proto.constructor?.name === type;
|
||||
}
|
||||
|
||||
export function deepMerge(target, source, options = {}) {
|
||||
let {
|
||||
emptyValues = [undefined],
|
||||
containers = ['Object', 'EventTarget'],
|
||||
isContainer = value => containers.some(type => isObject(value, type)),
|
||||
} = options;
|
||||
|
||||
if (isContainer(target) && isContainer(source)) {
|
||||
for (let key in source) {
|
||||
if (key in target && isContainer(target[key]) && isContainer(source[key])) {
|
||||
target[key] = deepMerge(target[key], source[key], options);
|
||||
} else if (!emptyValues.includes(source[key])) {
|
||||
target[key] = source[key];
|
||||
}
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
return target ?? source;
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate over a deep array, recursively for plain objects
|
||||
* @param { any } obj The object to iterate over. Can be an array or a plain object, or even a primitive value.
|
||||
* @param { EachCallback } callback. value is === parent[key]
|
||||
* @param { object } [parentObj] The parent object of the current value Mainly used internally to facilitate recursion.
|
||||
* @param { Property } [key] The key of the current value. Mainly used internally to facilitate recursion.
|
||||
* @param { Property[] } [path] Any existing path (not including the key). Mainly used internally to facilitate recursion.
|
||||
*/
|
||||
export function deepEach(obj, callback, parentObj, key, path = []) {
|
||||
if (key !== undefined) {
|
||||
let ret = callback(obj, key, parentObj, path);
|
||||
|
||||
if (ret !== undefined) {
|
||||
if (ret === false) {
|
||||
// Do not descend further
|
||||
return;
|
||||
}
|
||||
|
||||
// Overwrite value
|
||||
parentObj[key] = ret;
|
||||
obj = ret;
|
||||
}
|
||||
}
|
||||
|
||||
let newPath = key !== undefined ? [...path, key] : path;
|
||||
|
||||
if (Array.isArray(obj)) {
|
||||
for (let i = 0; i < obj.length; i++) {
|
||||
deepEach(obj[i], callback, obj, i, newPath);
|
||||
}
|
||||
} else if (isPlainObject(obj)) {
|
||||
for (let key in obj) {
|
||||
deepEach(obj[key], callback, obj, key, newPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a value from a deeply nested object
|
||||
* @param {*} obj
|
||||
* @param {PropertyPath} path
|
||||
* @returns
|
||||
*/
|
||||
export function deepGet(obj, path) {
|
||||
if (path.length === 0) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
let ret = obj;
|
||||
|
||||
for (let key of path) {
|
||||
if (ret === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
ret = ret[key];
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a value in a deep object, creating object literals as needed
|
||||
* @param { * } obj
|
||||
* @param { Property[] } path
|
||||
* @param { any } value
|
||||
*/
|
||||
export function deepSet(obj, path, value) {
|
||||
if (path.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
let key = path.pop();
|
||||
|
||||
let ret = path.reduce((acc, property) => {
|
||||
if (acc[property] === undefined) {
|
||||
acc[property] = {};
|
||||
}
|
||||
|
||||
return acc[property];
|
||||
}, obj);
|
||||
|
||||
ret[key] = value;
|
||||
}
|
||||
|
||||
export function deepClone(obj) {
|
||||
if (!obj) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
let ret = obj;
|
||||
|
||||
if (Array.isArray(obj)) {
|
||||
ret = obj.map(item => deepClone(item));
|
||||
} else if (isPlainObject(obj)) {
|
||||
ret = { ...obj };
|
||||
|
||||
for (let key in obj) {
|
||||
ret[key] = deepClone(obj[key]);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Like Object.entries, but for deeply nested objects.
|
||||
* For shallow objects the output is the same as Object.entries.
|
||||
* @param {*} obj
|
||||
* @param { object } options
|
||||
* @param { EachCallback } each - If this returns false, the entry is not added to the result and the recursion is stopped.
|
||||
* @param { EachCallback } filter - If this returns false, the entry is not added to the result.
|
||||
* @param { EachCallback } descend - If this returns false, recursion is stopped.
|
||||
* @returns {any[][]}
|
||||
*/
|
||||
export function deepEntries(obj, options = {}) {
|
||||
let { each, filter, descend } = options;
|
||||
let entries = [];
|
||||
|
||||
deepEach(obj, (value, key, parent, path) => {
|
||||
let ret = each?.(value, key, parent, path);
|
||||
|
||||
if (ret !== false) {
|
||||
let included = filter?.(value, key, parent, path) ?? true;
|
||||
|
||||
if (included) {
|
||||
entries.push([...path, key, value]);
|
||||
}
|
||||
|
||||
let descendRet = descend?.(value, key, parent, path);
|
||||
if (descendRet === false) {
|
||||
return false; // Stop recursion
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
});
|
||||
|
||||
return entries;
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
let initialPageLoadComplete = document.readyState === 'complete';
|
||||
|
||||
if (!initialPageLoadComplete) {
|
||||
window.addEventListener('load', () => {
|
||||
initialPageLoadComplete = true;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for performing a DOM change using a view transition, wherever supported and reduced motion is not desired.
|
||||
* @param {function} fn - Function to perform the DOM change. If async, must resolve when the change is complete.
|
||||
* @param {object} [options] - Options for the transition
|
||||
* @param {'smooth' | 'instant'} [options.behavior] - Transition behavior. Defaults to 'smooth'. 'instant' will skip the transition.
|
||||
* @param {boolean} [options.ignoreInitialLoad] - If true, will skip the transition on initial page load. Defaults to true.
|
||||
*/
|
||||
export function domChange(fn, { behavior = 'smooth', ignoreInitialLoad = true } = {}) {
|
||||
const canUseViewTransitions =
|
||||
document.startViewTransition && !window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
||||
|
||||
// Skip transitions on initial page load
|
||||
if (!initialPageLoadComplete && ignoreInitialLoad) {
|
||||
fn(false);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (canUseViewTransitions && behavior === 'smooth') {
|
||||
const transition = document.startViewTransition(() => {
|
||||
fn(true);
|
||||
// Wait a brief delay before finishing the transition to prevent jumpiness
|
||||
return new Promise(resolve => setTimeout(resolve, 200));
|
||||
});
|
||||
return transition;
|
||||
} else {
|
||||
fn(false);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export default domChange;
|
||||
@@ -1,42 +0,0 @@
|
||||
/**
|
||||
* Make the first letter of a string uppercase
|
||||
* @param {*} str
|
||||
* @returns
|
||||
*/
|
||||
export function capitalize(str) {
|
||||
str += '';
|
||||
return str[0]?.toUpperCase() + str.slice(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a readable string to a slug.
|
||||
* @param {*} str - Input string. If argument is not a string, it will be stringified.
|
||||
* @returns {string} - The slugified string
|
||||
*/
|
||||
export function slugify(str) {
|
||||
return (str + '')
|
||||
.normalize('NFD')
|
||||
.replace(/[\u0300-\u036f]/g, '') // Convert accented letters to ASCII
|
||||
.replace(/[^\w\s-]/g, '') // Remove remaining non-ASCII characters
|
||||
.trim()
|
||||
.replace(/\s+/g, '-') // Convert whitespace to hyphens
|
||||
.toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a string to camel case.
|
||||
* @param {string} str - The string to convert.
|
||||
* @returns {string} The camel case string.
|
||||
*/
|
||||
export function camelCase(str) {
|
||||
return str.replace(/-([a-z])/g, (_, letter) => letter?.toUpperCase());
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a string to kebab case.
|
||||
* @param {string} str - The string to convert.
|
||||
* @returns {string} The kebab case string.
|
||||
*/
|
||||
export function kebabCase(str) {
|
||||
return str.replace(/([A-Z])/g, '-$1')?.toLowerCase();
|
||||
}
|
||||
27
packages/webawesome/docs/assets/scripts/view-transitions.js
Normal file
27
packages/webawesome/docs/assets/scripts/view-transitions.js
Normal file
@@ -0,0 +1,27 @@
|
||||
let initialPageLoadComplete = document.readyState === 'complete';
|
||||
|
||||
if (!initialPageLoadComplete) {
|
||||
window.addEventListener('load', () => {
|
||||
initialPageLoadComplete = true;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* A wrapper around `document.startViewTransition()` that fails gracefully in unsupportive browsers.
|
||||
*/
|
||||
export async function doViewTransition(callback, { ignoreInitialLoad = true } = {}) {
|
||||
// Skip transitions on initial page load
|
||||
if (!initialPageLoadComplete && ignoreInitialLoad) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
const canUseViewTransitions =
|
||||
document.startViewTransition && !window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
||||
|
||||
if (canUseViewTransitions) {
|
||||
await document.startViewTransition(callback).finished;
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
border: var(--wa-border-style) var(--wa-panel-border-width) var(--wa-color-neutral-border-quiet);
|
||||
border-radius: var(--wa-border-radius-l);
|
||||
color: var(--wa-color-text-normal);
|
||||
margin-block-end: var(--wa-flow-spacing);
|
||||
margin-block-end: var(--wa-content-spacing);
|
||||
isolation: isolate;
|
||||
}
|
||||
|
||||
@@ -100,8 +100,8 @@
|
||||
.code-example-buttons {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
background: var(--wa-color-surface-default) !important; /* TODO - remove after native styles refactor */
|
||||
color: var(--wa-color-text-quiet) !important; /* TODO - remove after native styles refactor */
|
||||
background: var(--wa-color-surface-default);
|
||||
color: var(--wa-color-text-quiet);
|
||||
border-bottom-left-radius: inherit;
|
||||
border-bottom-right-radius: inherit;
|
||||
|
||||
@@ -116,12 +116,6 @@
|
||||
padding: 0.5rem;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
border-left: var(--wa-border-style) var(--wa-panel-border-width) var(--wa-color-neutral-border-quiet) !important; /* TODO - remove after native styles refactor */
|
||||
background: var(--wa-color-surface-default) !important; /* TODO - remove after native styles refactor */
|
||||
color: var(--wa-color-text-quiet) !important; /* TODO - remove after native styles refactor */
|
||||
}
|
||||
|
||||
&:first-of-type {
|
||||
border-left: none;
|
||||
border-bottom-left-radius: var(--wa-border-radius-l);
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
pre {
|
||||
background-color: var(--wa-color-gray-20);
|
||||
/* Only code blocks generated by our docs get these styles */
|
||||
pre[id*='code-block-'] {
|
||||
color-scheme: dark;
|
||||
color: white;
|
||||
background-color: var(--wa-color-neutral-20);
|
||||
|
||||
/* Ensures a discernible background color in dark mode
|
||||
* Useful for themes that use gray-20 as --wa-color-surface-default */
|
||||
@@ -8,6 +10,7 @@ pre {
|
||||
background-color: var(--wa-color-surface-lowered);
|
||||
}
|
||||
}
|
||||
|
||||
.code-comment,
|
||||
.code-prolog,
|
||||
.code-doctype,
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/* Prep our code blocks to host the copy button */
|
||||
pre[id*='code-block-']:has(code) {
|
||||
position: relative;
|
||||
padding: 0;
|
||||
white-space: normal;
|
||||
|
||||
& code {
|
||||
display: block;
|
||||
font-size: 1em;
|
||||
background-color: transparent;
|
||||
padding: var(--wa-space-m);
|
||||
white-space: pre;
|
||||
overflow-x: auto;
|
||||
}
|
||||
}
|
||||
|
||||
wa-copy-button.copy-button {
|
||||
--background-color: var(--wa-color-gray-20);
|
||||
--background-color-hover: color-mix(in oklab, var(--background-color), white 5%);
|
||||
@@ -9,8 +25,15 @@ wa-copy-button.copy-button {
|
||||
border-radius: var(--wa-border-radius-m);
|
||||
padding: 0.25rem;
|
||||
|
||||
&:hover {
|
||||
color: white;
|
||||
&::part(button) {
|
||||
background: transparent;
|
||||
cursor: copy;
|
||||
}
|
||||
|
||||
@media (hover: hover) {
|
||||
&:hover {
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
@@ -27,19 +50,3 @@ wa-copy-button.copy-button {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.block-link-icon {
|
||||
position: absolute;
|
||||
inset-block-start: 0;
|
||||
inset-inline-end: calc(100% + var(--wa-space-s));
|
||||
|
||||
transition: var(--wa-transition-slow);
|
||||
|
||||
&:not(:hover, :focus) {
|
||||
opacity: 50%;
|
||||
}
|
||||
|
||||
:not(:hover, :focus-within) > & {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user