Compare commits

..

2 Commits

Author SHA1 Message Date
Kelsey Jackson
ed10f36b28 renamed some files 2024-12-18 20:40:35 -06:00
Kelsey Jackson
a13be817db switching to change input label styling 2024-12-18 20:11:41 -06:00
450 changed files with 12380 additions and 11132 deletions

View File

@@ -31,7 +31,7 @@ jobs:
- name: Lint
run: npm run prettier
- name: Build
run: npm run build:alpha
run: npm run build
- name: Install Playwright
run: npx playwright install --with-deps
- name: Run CSR tests

View File

@@ -30,7 +30,7 @@ jobs:
run: npm run prettier
- name: Build
run: npm run build:alpha
run: npm run build
- name: Install Playwright
run: npx playwright install --with-deps

View File

@@ -59,8 +59,6 @@ To generate a production build, run the following command.
npm run build
```
You can also run `npm run build:serve` to start an [`http-server`](https://www.npmjs.com/package/http-server) instance on `http://localhost:4000` after the build completes, so you can preview the production build.
### Creating New Components
To scaffold a new component, run the following command, replacing `wa-tag-name` with the desired tag name.

View File

@@ -20,15 +20,6 @@ const packageData = JSON.parse(await readFile('./package.json', 'utf-8'));
const isAlpha = process.argv.includes('--alpha');
const isDev = process.argv.includes('--develop');
const globalData = {
package: packageData,
isAlpha,
layout: 'page.njk',
};
const passThroughExtensions = ['js', 'css', 'png', 'svg', 'jpg', 'mp4'];
const passThrough = [...passThroughExtensions.map(ext => 'docs/**/*.' + ext)];
export default function (eleventyConfig) {
// NOTE - alpha setting removes certain pages
if (isAlpha) {
@@ -41,9 +32,8 @@ export default function (eleventyConfig) {
}
// Add template data
for (let name in globalData) {
eleventyConfig.addGlobalData(name, globalData[name]);
}
eleventyConfig.addGlobalData('package', packageData);
eleventyConfig.addGlobalData('isAlpha', isAlpha);
// Template filters - {{ content | filter }}
eleventyConfig.addFilter('inlineMarkdown', content => markdown.renderInline(content || ''));
@@ -53,14 +43,20 @@ export default function (eleventyConfig) {
eleventyConfig.addFilter(name, filters[name]);
}
eleventyConfig.addFilter('sort', (arr, key = 'data.title') => {
key = key.split('.');
return arr.sort((a, b) => {
let aVal = key.reduce((obj, i) => obj?.[i], a);
let bVal = key.reduce((obj, i) => obj?.[i], b);
return aVal.localeCompare(bVal);
});
});
// Shortcodes - {% shortCode arg1, arg2 %}
eleventyConfig.addShortcode('cdnUrl', location => {
return `https://early.webawesome.com/webawesome@${packageData.version}/dist/` + location.replace(/^\//, '');
});
// Paired shortcodes - {% shortCode %}content{% endShortCode %}
eleventyConfig.addPairedShortcode('markdown', content => markdown.render(content || ''));
// Helpers
// Remove elements that have [data-alpha="remove"]
@@ -158,18 +154,9 @@ export default function (eleventyConfig) {
// eleventyConfig.addPlugin(formatCodePlugin());
// }
eleventyConfig.addPassthroughCopy({
'docs/assets': 'assets',
});
for (let glob of passThrough) {
eleventyConfig.addPassthroughCopy(glob);
}
return {
markdownTemplateEngine: 'njk',
dir: {
input: 'docs',
includes: '_includes',
layouts: '_layouts',
},

View File

@@ -1 +0,0 @@
["red", "yellow", "green", "teal", "blue", "indigo", "violet", "gray"]

View File

@@ -1 +0,0 @@
export { default as default } from '../../src/styles/color/palettes.js';

File diff suppressed because one or more lines are too long

View File

@@ -1,25 +1,91 @@
<!DOCTYPE html>
<html lang="en" data-fa-kit-code="b10bfbde90" data-cdn-url="{% cdnUrl %}">
<head>
{% include 'head.njk' %}
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="{{ description }}">
<meta name="theme-color" content="#f36944">
{% if noindex %}<meta name="robots" content="noindex">{% endif %}
<title>{{ title }}</title>
<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">
<script type="module" src="https://cdn.jsdelivr.net/npm/@hotwired/turbo@8.0.10/+esm"></script>
<script type="module" src="/assets/scripts/code-examples.js"></script>
<script type="module" src="/assets/scripts/color-scheme.js"></script>
<script type="module" src="/assets/scripts/copy-code.js"></script>
<script type="module" src="/assets/scripts/preset-theme.js"></script>
<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/outline.js"></script>
<script defer data-domain="backers.webawesome.com" src="https://plausible.io/js/script.js"></script>
{# Web Awesome #}
<script type="module" src="/dist/webawesome.ssr-loader.js"></script>
<link rel="stylesheet" id="theme-stylesheet" href="/dist/styles/themes/default.css" />
<link rel="stylesheet" href="/dist/styles/applied.css" />
<link id="color-stylesheet" rel="stylesheet" href="/dist/styles/utilities.css" />
<link rel="stylesheet" href="/dist/styles/forms.css" />
{# Docs styles #}
<link rel="stylesheet" href="/assets/styles/docs.css" />
{# Set the theme to prevent flashing #}
<script>
function getColorScheme() {
return localStorage.getItem('colorScheme') || 'auto';
}
function getPresetTheme () {
return localStorage.getItem('presetTheme') || 'default';
}
function isDark() {
const colorScheme = getColorScheme()
if (colorScheme === 'auto') {
return window.matchMedia('(prefers-color-scheme: dark)').matches;
}
return colorScheme === 'dark';
}
const oldStylesheet = document.querySelector("#theme-stylesheet")
const newStylesheet = document.createElement("link")
let preset = getPresetTheme()
newStylesheet.href = `/dist/styles/themes/${preset}.css`
newStylesheet.rel = "preload"
newStylesheet.as = "style"
document.head.append(newStylesheet)
function updateStylesheet () {
newStylesheet.rel = "stylesheet"
newStylesheet.id = "theme-stylesheet"
requestAnimationFrame(() => oldStylesheet.remove())
}
newStylesheet.addEventListener("load", updateStylesheet)
document.documentElement.classList.toggle(
`wa-theme-${preset}-dark`,
isDark()
);
</script>
</head>
<body class="layout-{{ layout | stripExtension }}">
<!-- use view="desktop" as default to reduce layout jank on desktop site. -->
<wa-page view="desktop" disable-navigation-toggle="">
<header slot="header" class="wa-split">
<header slot="header">
{# Logo #}
<div id="docs-branding">
{# Nav toggle #}
@@ -28,16 +94,16 @@
</wa-button>
<a href="/" aria-label="Web Awesome">
<span class="wa-desktop-only">{% include "logo.njk" %}</span>
<span class="wa-mobile-only">{% include "logo-simple.njk" %}</span>
<span class="only-desktop">{% include "logo.njk" %}</span>
<span class="only-mobile">{% include "logo-simple.njk" %}</span>
</a>
<small id="version-number" class="only-desktop">{{ package.version }}</small>
<wa-badge variant="warning" appearance="filled" class="only-desktop">Alpha</wa-badge>
</div>
<div id="docs-toolbar" class="wa-cluster wa-gap-xs">
<div id="docs-toolbar">
{# Desktop selectors #}
<div class="only-desktop wa-cluster wa-gap-xs">
<div class="only-desktop">
{% include "preset-theme-selector.njk" %}
{% include "color-scheme-selector.njk" %}
</div>
@@ -54,8 +120,8 @@
{# Sidebar #}
{% if hasSidebar %}
{# Mobile selectors #}
<div class="wa-mobile-only" slot="navigation-header">
<div class="wa-cluster wa-gap-xs">
<div class="only-mobile" slot="navigation-header">
<div style="display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));">
{% include "preset-theme-selector.njk" %}
{% include "color-scheme-selector.njk" %}
</div>
@@ -83,12 +149,22 @@
</details>
</nav>
{% block header %}
{% include 'breadcrumbs.njk' %}
<h1 class="title">{{ title }}</h1>
{% endblock %}
{% set breadcrumbs = page.url | breadcrumbs %}
{% if breadcrumbs.length > 0 %}
<wa-breadcrumb id="docs-breadcrumbs">
{% for crumb in breadcrumbs %}
<wa-breadcrumb-item href="{{ crumb.url }}">{{ crumb.title }}</wa-breadcrumb-item>
{% endfor %}
<wa-breadcrumb-item>{# Current page #}</wa-breadcrumb-item>
</wa-breadcrumb>
{% else %}
{% endif %}
{% block beforeContent %}{% endblock %}
{% block content %}
<h1 class="title">{{ title }}</h1>
{{ content | safe }}
{% endblock %}

View File

@@ -1,10 +0,0 @@
{% set breadcrumbs = page.url | breadcrumbs %}
{% if breadcrumbs.length > 0 %}
<wa-breadcrumb id="docs-breadcrumbs">
{% for crumb in breadcrumbs %}
<wa-breadcrumb-item href="{{ crumb.url }}">{{ crumb.title }}</wa-breadcrumb-item>
{% endfor %}
<wa-breadcrumb-item>{# Current page #}</wa-breadcrumb-item>
</wa-breadcrumb>
{% else %}
{% endif %}

View File

@@ -1,19 +1,15 @@
{# 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-option value="light">
<wa-icon slot="prefix" name="sun" variant="regular"></wa-icon>
Light
</wa-option>
<wa-option value="dark">
<wa-icon slot="prefix" name="moon" variant="regular"></wa-icon>
Dark
</wa-option>
<wa-divider></wa-divider>
<wa-option value="auto">
<wa-dropdown id="color-scheme-selector">
<wa-button slot="trigger" appearance="filled" size="small" pill caret 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>
System
</wa-option>
</wa-select>
<span class="only-light">Light</span>
<span class="only-dark">Dark</span>
</wa-button>
<wa-menu>
<wa-menu-item type="checkbox" value="light">Light</wa-menu-item>
<wa-menu-item type="checkbox" value="dark">Dark</wa-menu-item>
<wa-divider></wa-divider>
<wa-menu-item type="checkbox" value="auto">System</wa-menu-item>
</wa-menu>
</wa-dropdown>

View File

@@ -1,40 +0,0 @@
<table class="colors wa-palette-{{ paletteId }}">
<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>
<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 %}
<td>
<div class="color swatch" style="background-color: var(--wa-color-{{ hue }}-{{ tint_bg }}); color: var(--wa-color-{{ hue }}-{{ tint_fg }})">
{% set contrast_wcag = '' %}
{% if color_fg and color_bg %}
{% set contrast_wcag = color_bg.contrast(color_fg, 'WCAG21') %}
{% endif %}
{% if contrast_wcag %}
{{ contrast_wcag | number({maximumSignificantDigits: 2}) }}
{% else %}
{{ tint_fg }} on {{ tint_bg }}
{% endif %}
</div>
</td>
{% endif %}
{%- endfor -%}
{%- endfor -%}
</tr>
{%- endfor %}
</table>

View File

@@ -1,12 +0,0 @@
{# Cards for pages listed by category #}
<section id="grid" class="index-grid">
{% for category, pages in allPages | groupByTags(categories) -%}
<h2 class="index-category">{{ category | getCategoryTitle(categories) }}</h2>
{%- for page in pages -%}
{%- if not page.data.parent or listChildren -%}
{% include "page-card.njk" %}
{%- endif -%}
{%- endfor -%}
{%- endfor -%}
</section>

View File

@@ -1,49 +0,0 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="{{ description }}">
{% if noindex %}<meta name="robots" content="noindex">{% endif %}
<title>{{ title }}</title>
{# Dark mode #}
<script>
let colorScheme = localStorage.colorScheme;
let isDark = localStorage.colorScheme === "dark";
if (!colorScheme || colorScheme === "auto") {
isDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
}
document.documentElement.classList.toggle('wa-dark', isDark);
</script>
<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">
<script type="module" src="https://cdn.jsdelivr.net/npm/@hotwired/turbo@8.0.10/+esm"></script>
{# Web Awesome #}
<script type="module" src="/dist/webawesome.ssr-loader.js"></script>
<script type="module" src="/assets/scripts/theme-picker.js"></script>
{# Preset Theme #}
{% if 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 rel="stylesheet" href="/dist/styles/webawesome.css" />
<link id="color-stylesheet" rel="stylesheet" href="/dist/styles/utilities.css" />
<link rel="stylesheet" href="/dist/styles/forms.css" />

View File

@@ -1,18 +0,0 @@
<wa-tab-group>
<wa-tab panel="html">In HTML</wa-tab>
<wa-tab panel="css">In CSS</wa-tab>
<wa-tab-panel name="html">
Simply 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">
Simply add the following code at the top of your CSS file:
```css
@import url('{% cdnUrl stylesheet %}');
```
</wa-tab-panel>
</wa-tab-group>

View File

@@ -1,10 +0,0 @@
{%- if not page.data.unlisted -%}
<a href="{{ page.url }}"{{ page.data.keywords | attr('data-keywords') }}>
<wa-card with-header>
<div slot="header">
{% include "svgs/" + (page.data.icon or "thumbnail-placeholder") + ".njk" %}
</div>
<span class="page-name">{{ page.data.title }}</span>
</wa-card>
</a>
{% endif %}

View File

@@ -1,5 +1,5 @@
<div id="page_slots_demo">
<link rel="stylesheet" href="./demo.css">
<link rel="stylesheet" href="/assets/examples/page-demo/demo.css">
{% set slots = components.page.slots %}
<fieldset id="page_slots_fieldset">
@@ -22,5 +22,5 @@
<script type="module">
const cacheBust = new Date().toString()
import(`./demo.js?${cacheBust}`)
import(`/assets/examples/page-demo/demo.js?${cacheBust}`)
</script>

View File

@@ -1,9 +1,18 @@
{# 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 }}"{{ ' data-alpha="remove"' if theme.noAlpha }}>
{{ theme.data.title }}
</wa-option>
{% endfor %}
</wa-select>
<wa-dropdown id="preset-theme-selector">
<wa-button slot="trigger" appearance="filled" size="small" pill caret>
<wa-icon slot="prefix" name="paintbrush" variant="regular"></wa-icon>
<span id="preset-theme-selector__text">Default</span>
</wa-button>
<wa-menu>
<wa-menu-item type="checkbox" value="default">Default</wa-menu-item>
<wa-menu-item type="checkbox" value="classic">Classic</wa-menu-item>
<wa-menu-item data-alpha="remove" type="checkbox" value="fa">Font Awesome</wa-menu-item>
<wa-menu-item data-alpha="remove" type="checkbox" value="active">Active</wa-menu-item>
<wa-menu-item data-alpha="remove" type="checkbox" value="brutalist">Brutalism</wa-menu-item>
<wa-menu-item data-alpha="remove" type="checkbox" value="glassy">Glassy</wa-menu-item>
<wa-menu-item data-alpha="remove" type="checkbox" value="migration">Migration</wa-menu-item>
<wa-menu-item data-alpha="remove" type="checkbox" value="playful">Playful</wa-menu-item>
<wa-menu-item data-alpha="remove" type="checkbox" value="premium">Premium</wa-menu-item>
</wa-menu>
</wa-dropdown>

View File

@@ -1,22 +0,0 @@
{# Some collections (like "patterns") will not have any items in the alpha build for example. So this checks to make sure the collection exists. #}
{% if collections[tag] -%}
{% set groupUrl %}/docs/{{ tag }}/{% endset %}
<wa-details {{ ((tag in (tags or [])) or (groupUrl in page.url)) | attr('open') }}>
<h2 slot="summary">
{% if groupUrl | getCollectionItemFromUrl %}
<a href="{{ groupUrl }}" title="Overview">{{ title or (tag | capitalize) }}
<wa-icon name="grid-2"></wa-icon>
</a>
{% else %}
{{ title or (tag | capitalize) }}
{% endif %}
</h2>
<ul>
{% for page in collections[tag] | sort %}
{% if not page.data.parent -%}
{% include 'sidebar-link.njk' %}
{%- endif %}
{% endfor %}
</ul>
</wa-details>
{%- endif %}

View File

@@ -1,16 +0,0 @@
{% if not (isAlpha and page.data.noAlpha) and page.fileSlug != tag and not page.data.unlisted -%}
<li>
<a href="{{ page.url }}">{{ page.data.title }}</a>
{% if page.data.status == 'experimental' %}<wa-icon name="flask"></wa-icon>{% endif %}
{% if page.data.isPro %}<wa-badge class="pro">PRO</wa-badge>{% endif %}
{% set children = page.data.children %}
{% if children.length > 0 %}
<ul>
{% for page in children %}
{% include 'sidebar-link.njk' %}
{% endfor %}
</ul>
{% endif %}
</li>
{%- endif %}

View File

@@ -3,6 +3,7 @@
<ul>
<li><a href="/docs/installation">Installation</a></li>
<li><a href="/docs/usage">Usage</a></li>
<li><a href="/docs/themes">Themes</a></li>
<li><a href="/docs/customizing">Customizing</a></li>
<li><a href="/docs/form-controls">Form Controls</a></li>
<li><a href="/docs/localization">Localization</a></li>
@@ -18,15 +19,132 @@
<li><a href="/docs/resources/changelog">Changelog</a></li>
</ul>
{% for tag, title in {
'themes': 'Themes',
'components': 'Components',
'native': 'Native Styles',
'utilities': 'Style Utilities',
'layout': 'Layout',
'patterns': 'Patterns',
'palettes': 'Color Palettes',
'tokens': 'Design Tokens'
} %}
{% include 'sidebar-group.njk' %}
{% endfor %}
{# Components #}
<wa-details{{ ' open' if '/components/' in page.url }}>
<h2 slot=summary>
<a href="/docs/components/" title="Overview">Components
<wa-icon name="grid-2"></wa-icon>
</a>
</h2>
<ul>
{% for component in collections.component | sort %}
{% if not component.data.parent and not (isAlpha and component.data.isAlpha) %}
<li>
<a href="/docs/components/{{ component.fileSlug }}">{{ component.data.title }}</a>
{% if components[component.fileSlug].status == 'experimental' %}<wa-icon name="flask"></wa-icon>{% endif %}
{% if component.data.isPro %}<wa-badge class="pro">PRO</wa-badge>{% endif %}
<ul>
{% for child in collections.component | sort %}
{% if child.data.parent == component.fileSlug and not (isAlpha and child.data.isAlpha) %}
<li>
<a href="/docs/components/{{ child.fileSlug }}">{{ child.data.title }}</a>
</li>
{% endif %}
{% endfor %}
</ul>
</li>
{% endif %}
{% endfor %}
</ul>
</wa-details>
<wa-details{{ ' open' if '/natives/' in page.url }}>
<h2 slot=summary>
<a href="/docs/natives/" title="Overview">Natives
<wa-icon name="grid-2"></wa-icon>
</a>
</h2>
<ul>
{% for page in collections.natives | sort %}
<li>
<a href="/docs/natives/{{ page.fileSlug }}">{{ page.data.title }}</a>
</li>
{% endfor %}
</ul>
</wa-details>
{# Layout #}
{% if not isAlpha %}
<h2>
<a href="/docs/layout" title="Overview">Layout
<wa-icon name="grid-2"></wa-icon>
</a>
</h2>
<ul>
<li><a href="/docs/components/page">Page</a></li>
<li><a href="/docs/layout/cluster">Cluster</a></li>
<li><a href="/docs/layout/flank">Flank</a></li>
<li><a href="/docs/layout/frame">Frame</a></li>
<li><a href="/docs/layout/grid">Grid</a></li>
<li><a href="/docs/layout/split">Split</a></li>
<li><a href="/docs/layout/stack">Stack</a></li>
</ul>
{% endif %}
{# Patterns #}
{% if not isAlpha %}
<h2>Patterns</h2>
<ul>
<li><a href="/docs/patterns/app">Web App</a></li>
<li><a href="/docs/patterns/ecommerce">E-commerce</a>
<ul>
<li><a href="/docs/patterns/ecommerce-product-review">Product Reviews</a></li>
<li><a href="/docs/patterns/ecommerce-product-list">Product Lists</a></li>
<li><a href="/docs/patterns/ecommerce-category-preview">Category Previews</a></li>
<li><a href="/docs/patterns/ecommerce-shopping-cart">Shopping Carts</a></li>
<li><a href="/docs/patterns/ecommerce-category-filter">Category Filters</a></li>
<li><a href="/docs/patterns/ecommerce-product-detail">Product Detail</a></li>
<li><a href="/docs/patterns/ecommerce-order-summary">Order Summaries</a></li>
<li><a href="/docs/patterns/ecommerce-order-history">Order History</a></li>
</ul>
</li>
<li><a href="/docs/patterns/blog">Blog</a></li>
<li><a href="/docs/patterns/news">News</a></li>
</ul>
{% endif %}
{# Theming #}
<h2>
<a href="/docs/theming" title="Overview">Theming
<wa-icon name="grid-2"></wa-icon>
</a>
</h2>
<ul>
<li>
<a href="/docs/theming/color">Color</a>
</li>
<li>
<a href="/docs/theming/typography">Typography</a>
</li>
<li>
<a href="/docs/theming/space">Space</a>
</li>
<li>
<a href="/docs/theming/borders">Borders</a>
</li>
<li>
<a href="/docs/theming/focus">Focus</a>
</li>
<li>
<a href="/docs/theming/shadows">Shadows</a>
</li>
<li>
<a href="/docs/theming/transitions">Transitions</a>
</li>
<li>
<a href="/docs/theming/component-groups">Component Groups</a>
</li>
</ul>
{# Style Utilities #}
{% if not isAlpha %}
<h2>Style Utilities</h2>
<ul>
<li><a href="/docs/style-utilities/align-items">Align Items</a></li>
<li><a href="/docs/style-utilities/border-radius">Border Radius</a></li>
<li><a href="/docs/style-utilities/gap">Gap</a></li>
<li><a href="/docs/style-utilities/text">Text</a></li>
</ul>
{% endif %}

View File

@@ -1,25 +0,0 @@
{% if since -%}
<wa-badge variant="neutral">Since {{ since }}</wa-badge>
{% endif -%}
{%- if status %}
{%- if status == "wip" %}
<wa-badge variant="danger">
<wa-icon name="pickaxe"></wa-icon>
Work In Progress
</wa-badge>
{%- elif 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 -%}

View File

@@ -1,24 +0,0 @@
{% set paletteId = page.fileSlug %}
{% set tints = [80, 60, 40, 20] %}
{% set width = 20 %}
{% set height = 13 %}
{% set gap_x = 3 %}
{% set gap_y = 3 %}
<svg viewBox="0 0 {{ (width + gap_x) * hues|length }} {{ (height + gap_y) * tints|length }}" fill="none" xmlns="http://www.w3.org/2000/svg" class="wa-palette-{{ paletteId }} palette-icon">
<style>
@import url('/dist/styles/color/{{ paletteId }}.css') layer(palette.{{ paletteId }});
.palette-icon {
height: 8ch;
}
</style>
{% for hue in hues -%}
{% set hueIndex = loop.index0 %}
{% for tint in tints -%}
<rect x="{{ hueIndex * (width + gap_x) }}" y="{{ loop.index0 * (height + gap_y) }}"
width="{{ width }}" height="{{ height }}"
fill="var(--wa-color-{{ hue }}-{{ tint }})" rx="4" />
{%- endfor %}
{% endfor %}
</svg>

View File

Before

Width:  |  Height:  |  Size: 337 B

After

Width:  |  Height:  |  Size: 337 B

View File

Before

Width:  |  Height:  |  Size: 596 B

After

Width:  |  Height:  |  Size: 596 B

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

View File

Before

Width:  |  Height:  |  Size: 502 B

After

Width:  |  Height:  |  Size: 502 B

View File

Before

Width:  |  Height:  |  Size: 790 B

After

Width:  |  Height:  |  Size: 790 B

View File

Before

Width:  |  Height:  |  Size: 697 B

After

Width:  |  Height:  |  Size: 697 B

View File

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -1,144 +0,0 @@
<div class="showcase-examples-wrapper" aria-hidden="true" data-no-outline>
<div class="showcase-examples">
<wa-card with-header with-footer>
<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="--size: 3em; --background-color: var(--wa-color-green-60); --text-color: var(--wa-color-green-95);">
<wa-icon slot="icon" name="sword-laser" family="duotone" style="font-size: 1.5em;"></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="--size: 3em; --background-color: var(--wa-color-teal-60); --text-color: var(--wa-color-teal-95);">
<wa-icon slot="icon" name="robot-astromech" family="duotone" style="font-size: 1.5em;"></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" family="duotone" style="font-size: 1.75em;"></wa-icon>
</wa-avatar>
<p class="wa-body-l" style="margin: 0;">&ldquo;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.&rdquo;</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 with-footer>
<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" read-only></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">&bull;</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>
</div>
</div>

View File

@@ -1,11 +1,62 @@
<!DOCTYPE html>
<html lang="en" data-fa-kit-code="b10bfbde90" data-cdn-url="{% cdnUrl %}">
<head>
{% include 'head.njk' %}
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="{{ description }}">
{% if noindex %}<meta name="robots" content="noindex">{% endif %}
<title>{{ title }}</title>
<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">
<script type="module" src="https://cdn.jsdelivr.net/npm/@hotwired/turbo@8.0.10/+esm"></script>
{# Web Awesome #}
<script type="module" src="/dist/webawesome.ssr-loader.js"></script>
<link rel="stylesheet" id="theme-stylesheet" />
<link rel="stylesheet" href="/dist/styles/applied.css" />
<link id="color-stylesheet" rel="stylesheet" href="/dist/styles/utilities.css" />
<link rel="stylesheet" href="/dist/styles/forms.css" />
{# Set the theme to prevent flashing #}
<script>
function getColorScheme() {
return localStorage.getItem('colorScheme') || 'auto';
}
function getPresetTheme () {
return localStorage.getItem('presetTheme') || 'default';
}
function isDark() {
const colorScheme = getColorScheme()
if (colorScheme === 'auto') {
return window.matchMedia('(prefers-color-scheme: dark)').matches;
}
return colorScheme === 'dark';
}
const stylesheet = document.getElementById("theme-stylesheet")
let preset = getPresetTheme()
stylesheet.href = `/dist/styles/themes/${preset}.css`
document.documentElement.classList.toggle(
`wa-theme-${preset}-dark`,
isDark()
);
</script>
</head>
<body class="layout-{{ layout | stripExtension }}">
{% block header %}{% endblock %}
{% block beforeContent %}{% endblock %}
{% block content %}
{{ content | safe }}

View File

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

View File

@@ -1,17 +1,42 @@
{% extends '../_layouts/block.njk' %}
{% set hasSidebar = true %}
{% set hasOutline = true %}
{% set component = components[page.fileSlug] %}
{% set description = component.summary %}
{% extends '../_includes/base.njk' %}
{# Component header #}
{% block notes %}
{% if native %}
{% block beforeContent %}
<h1 class="title">{{ title }}</h1>
<div class="component-info">
<code class="tag">&lt;{{ component.tagName }}&gt;</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>
{% if isPro %}<wa-badge class="pro">PRO</wa-badge>{% endif %}
</div>
<p class="summary">
{{ component.summary | inlineMarkdown | safe }}
</p>
{% if natives %}
<wa-callout variant="success">
<wa-icon slot="icon" name="lightbulb" variant="regular"></wa-icon>
Just want the styles?
Check out the <a href="/docs/native/{{ native }}/">{{ ('/docs/native/' + native + '/') | getTitleFromUrl }} native styles</a>!
Check out <a href="/docs/natives/{{ natives }}/">{{ ('/docs/natives/' + natives + '/') | getTitleFromUrl }} natives</a>!
</wa-callout>
{% endif %}
{% endblock %}
{# Content #}
{% block content %}
{{ content | safe }}
{% endblock %}
{# Component API #}
{% block afterContent %}
{# Slots #}

View File

@@ -1,23 +1,54 @@
{% extends '../_layouts/block.njk' %}
{% set hasSidebar = true %}
{% set hasOutline = true %}
{% extends '../_includes/base.njk' %}
{# Component header #}
{% block notes %}
{% block beforeContent %}
<h1 class="title">{{ title }}</h1>
<div class="component-info">
{% for tag, url in elements %}
<code class="tag"><a href="{{ url }}">{{ tag }}</a></code>
{% endfor %}
{% if since %}<wa-badge variant="neutral">Since {{ since }}</wa-badge>{% endif %}
{% if status %}
<wa-badge
{% if status == 'stable' %}variant="brand"{% endif %}
{% if status == 'experimental' %}variant="warning"{% endif %}
>
{{ status }}
</wa-badge>
{% endif %}
{% if isPro %}
<wa-badge class="pro">PRO</wa-badge>
{% endif %}
</div>
{% if description -%}
<p class="summary">{{ description | inlineMarkdown | safe }}</p>
{%- endif %}
{% 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) -%}
Check out the {% for name in (component | toArray) -%}
{{ ' and ' if loop.last and not loop.first }}<a href="/docs/components/{{ name }}"><code>&lt;wa-{{ name }}&gt;</code></a>{{ ', ' if not loop.last }}
{%- endfor %} component{{ 's' if (component | isList) }}</a>!
{%- endfor %} component{{ 's' if (component | isArray) }}</a>!
</wa-callout>
{% endif %}
{% endblock %}
{# Content #}
{% block content %}
{{ content | safe }}
{% endblock %}
{# Component API #}
{% block afterContent %}
{# Slots #}
{% if css_file %}
<h2>Using these styles</h2>
<p>If you want to use these styles without using the entirety of Web Awesome Native Styles, you can include the following CSS files:</p>
<p>If you want to use these styles without using the entirety of Web Awesome Natives, you can include the following CSS files:</p>
{% endif %}

View File

@@ -1,25 +0,0 @@
---
layout: page-outline
tags: ["overview"]
---
{% 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 }}" clearable autofocus>
<wa-icon slot="prefix" name="search"></wa-icon>
</wa-input>
</div>
{% set allPages = collections[forTag] %}
{% include "grouped-pages.njk" %}
<link href="/assets/styles/filter.css" rel="stylesheet">
<script type="module" src="/assets/scripts/filter.js"></script>
{% if content | trim %}
<br> {# Temp fix for spacing issue #}
{{ content | safe }}
{% endif %}

View File

@@ -0,0 +1,4 @@
{% set hasSidebar = true %}
{% set hasOutline = true %}
{% extends "../_includes/base-wide.njk" %}

View File

@@ -1,119 +0,0 @@
{% set hasSidebar = true %}
{% set hasOutline = true %}
{# {% set forceTheme = page.fileSlug %} #}
{% extends '../_layouts/block.njk' %}
{% set paletteId = page.fileSlug %}
{% block afterContent %}
<style>@import url('/dist/styles/color/{{ paletteId }}.css') layer(palette.{{ paletteId }});</style>
{% set tints = ["95", "90", "80", "70", "60", "50", "40", "30", "20", "10", "05"] %}
<table class="colors wa-palette-{{ paletteId }}">
<thead>
<tr>
<th></th>
{% for tint in tints -%}
<th>{{ tint }}</th>
{%- endfor %}
</tr>
</thead>
{% for hue in hues -%}
<tr>
<th>{{ hue | capitalize }}</th>
{% for tint in tints -%}
<td>
<div class="color swatch" style="background-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>
<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 %}
{% 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 %}
{% 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 %}
{% 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
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' %}
{% include 'import-stylesheet-code.md.njk' %}
{% endmarkdown %}
{% endblock %}

View File

@@ -0,0 +1,4 @@
{% set hasSidebar = true %}
{% set hasOutline = true %}
{% extends "../_includes/base.njk" %}

View File

@@ -1,150 +0,0 @@
{% set hasSidebar = true %}
{% set hasOutline = true %}
{# {% set forceTheme = page.fileSlug %} #}
{% extends '../_includes/base.njk' %}
{% block header %}
<iframe src='{{ page.url }}demo.html' id="demo"></iframe>
<p id="mix_and_match" class="wa-gap-m">
<strong>
<wa-icon name="merge" slot="prefix"></wa-icon>
Remix
<wa-icon-button href="#remixing" name="circle-question" slot="suffix" variant="regular" label="How to use?"></wa-icon-button>
</strong>
<wa-select name="colors" label="Colors from…" size="small">
<wa-icon name="palette" slot="prefix" variant="regular"></wa-icon>
<wa-option value="">(Theme default)</wa-option>
<wa-divider></wa-divider>
{% for theme in collections.theme | sort %}
{% if theme.fileSlug !== page.fileSlug %}
<wa-option value="{{ theme.fileSlug }}">{{ theme.data.title }}</wa-option>
{% endif %}
{% endfor %}
</wa-select>
<wa-select name="palette" label="Palette" size="small">
<wa-icon name="swatchbook" slot="prefix" variant="regular"></wa-icon>
<wa-option value="">(Theme default)</wa-option>
<wa-divider></wa-divider>
{% for p in collections.palette | sort %}
{% if p.fileSlug !== palette %}
<wa-option value="{{ p.fileSlug }}">{{ p.data.title }}</wa-option>
{% endif %}
{% endfor %}
</wa-select>
<wa-select name="typography" label="Typography from…" size="small">
<wa-icon name="font-case" slot="prefix"></wa-icon>
<wa-option value="">(Theme default)</wa-option>
<wa-divider></wa-divider>
{% for theme in collections.theme | sort %}
{% if theme.fileSlug !== page.fileSlug %}
<wa-option value="{{ theme.fileSlug }}">{{ theme.data.title }}</wa-option>
{% endif %}
{% endfor %}
</wa-select>
</p>
<script>
document.querySelector('#mix_and_match').addEventListener('wa-change', function(event) {
let selects = document.querySelectorAll('#mix_and_match wa-select');
let url = new URL(demo.src);
for (let select of selects) {
url.searchParams.set(select.name, select.value);
}
demo.src = url;
});
</script>
<h2>Default Color Palette</h2>
{% set paletteURL = '/docs/palettes/' + palette + '/' %}
{% set themePage = page %}
{% set page = paletteURL | getCollectionItemFromUrl %}
<div class="index-grid">
{% include 'page-card.njk' %}
</div>
{% set page = themePage %}
{% endblock %}
{% block afterContent %}
{% markdown %}
## How to use this theme
You can import this theme from the Web Awesome CDN.
{% set stylesheet = 'styles/themes/' + page.fileSlug + '.css' %}
{% include 'import-stylesheet-code.md.njk' %}
### Remixing { #remixing }
If you want to combine the **colors** from this theme with another theme, you can import this CSS file *after* the other themes CSS file:
{% set stylesheet = 'styles/themes/' + page.fileSlug + '/color.css' %}
{% include 'import-stylesheet-code.md.njk' %}
To use the **typography** from this theme with another theme, you can import this CSS file *after* the other themes CSS file:
{% set stylesheet = 'styles/themes/' + page.fileSlug + '/typography.css' %}
{% include 'import-stylesheet-code.md.njk' %}
<wa-callout variant="warning">
<wa-icon slot="icon" name="triangle-exclamation" variant="regular"></wa-icon>
Please note that not all combinations will look good — once youre mixing and matching, youre on your own!
</wa-callout>
## 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.
{% endmarkdown %}
{% endblock %}

View File

@@ -38,12 +38,8 @@ export function getTitleFromUrl(url, collection) {
return item?.data.title || '';
}
export function split(text, separator) {
return (text + '').split(separator).filter(Boolean);
}
export function breadcrumbs(url, { withCurrent = false } = {}) {
const parts = split(url, '/');
const parts = url.split('/').filter(Boolean);
const ret = [];
while (parts.length) {
@@ -74,211 +70,10 @@ export function breadcrumbs(url, { withCurrent = false } = {}) {
return ret;
}
export function isObject(value) {
return typeof value === 'object' && value !== null && !Array.isArray(value);
export function isArray(value) {
return 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 | Object<string, string>)[]} [tags] The tags to group by. If not provided/empty, defaults to grouping by all tags.
* @returns { Object.<string, object[]> } An object with keys for each tag, and an array of items for each tag.
*/
export function groupByTags(collection, tags) {
if (!collection) {
console.error(`Empty collection passed to groupByTags() to group by ${JSON.stringify(tags)}`);
}
if (!tags) {
// Default to grouping by union of all tags
tags = Array.from(new Set(collection.flatMap(item => item.data.tags)));
} else if (Array.isArray(tags)) {
// May contain objects of one-off tag -> label mappings
tags = tags.map(tag => (typeof tag === 'object' ? Object.keys(tag)[0] : tag));
} else if (typeof tags === 'object') {
// tags is an object of tags to labels, so we just want the keys
tags = Object.keys(tags);
}
let ret = Object.fromEntries(tags.map(tag => [tag, []]));
ret.other = [];
for (let item of collection) {
let categorized = false;
for (let tag of tags) {
if (item.data.tags.includes(tag)) {
ret[tag].push(item);
categorized = true;
}
}
if (!categorized) {
ret.other.push(item);
}
}
// Remove empty categories
for (let category in ret) {
if (ret[category].length === 0) {
delete ret[category];
}
}
return ret;
}
export function getCategoryTitle(category, categories) {
let title;
if (Array.isArray(categories)) {
// Find relevant entry
// [{id: "Title"}, id2, ...]
title = categories.find(entry => typeof entry === 'object' && entry?.[category])?.[category];
} else if (typeof categories === 'object') {
// {id: "Title", id2: "Title 2", ...}
title = categories[category];
}
if (title) {
return title;
}
// Capitalized
return category.charAt(0).toUpperCase() + category.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);
export function toArray(value) {
return isArray(value) ? value : [value];
}

View File

@@ -1,3 +1,4 @@
await customElements.whenDefined('wa-checkbox');
let container = document.getElementById('page_slots_demo');
let fieldset = container.querySelector('fieldset');
@@ -7,18 +8,9 @@ let stylesheets = Array.from(document.querySelectorAll("link[rel=stylesheet][hre
.join('\n');
let includes = `${stylesheets}
<script src="/dist/webawesome.loader.js" type="module"></script>
<link rel="stylesheet" href="./demo-page.css">`;
async function render() {
await customElements.whenDefined('wa-checkbox');
// Let checkboxes update their "state"
await Promise.allSettled(
Array.from(fieldset.querySelectorAll('wa-checkbox[name=slot]')).map(checkbox => {
return checkbox.updateComplete;
}),
);
<link rel="stylesheet" href="/assets/examples/page-demo/page.css">`;
function render() {
let slots = Array.from(fieldset.querySelectorAll('wa-checkbox[name=slot]:state(checked)'));
let slotsHTML = slots
.map(slot => {
@@ -47,5 +39,5 @@ async function render() {
iframe.srcdoc = `${includes}<wa-page>${slotsHTML}</wa-page>`;
}
}
await render();
fieldset?.addEventListener('input', render);
render();

View File

@@ -26,7 +26,7 @@ wa-page {
.slot-content {
padding: var(--wa-space-m);
border-radius: var(--wa-border-radius-m);
border-radius: var(--wa-border-radius-s);
align-content: center;
justify-content: center;
text-align: center;

View File

Before

Width:  |  Height:  |  Size: 698 KiB

After

Width:  |  Height:  |  Size: 698 KiB

View File

Before

Width:  |  Height:  |  Size: 241 KiB

After

Width:  |  Height:  |  Size: 241 KiB

View File

Before

Width:  |  Height:  |  Size: 786 KiB

After

Width:  |  Height:  |  Size: 786 KiB

View File

Before

Width:  |  Height:  |  Size: 716 KiB

After

Width:  |  Height:  |  Size: 716 KiB

View File

Before

Width:  |  Height:  |  Size: 148 KiB

After

Width:  |  Height:  |  Size: 148 KiB

View File

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 56 KiB

View File

Before

Width:  |  Height:  |  Size: 438 KiB

After

Width:  |  Height:  |  Size: 438 KiB

View File

@@ -19,10 +19,10 @@ document.addEventListener('click', event => {
const html =
`<script 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/applied.css">\n` +
`<link rel="stylesheet" href="${cdnUrl}styles/utilities.css">\n\n` +
`${code.textContent}`;
const css = 'html > body {\n padding: 2rem !important;\n}';
const css = 'html > body {\n font: 16px sans-serif;\n padding: 2rem;\n}';
const js = '';
const form = document.createElement('form');

View File

@@ -0,0 +1,59 @@
//
// Color scheme selector
//
(() => {
function setColorScheme(newColorScheme) {
colorScheme = newColorScheme;
localStorage.setItem('colorScheme', colorScheme);
const presetTheme = window.getPresetTheme();
// Update the UI
updateSelection();
// Toggle the dark mode class
document.documentElement.classList.toggle(`wa-theme-${presetTheme}-dark`, window.isDark());
}
function updateSelection() {
const menu = document.querySelector('#color-scheme-selector wa-menu');
if (!menu) return;
[...menu.querySelectorAll('wa-menu-item')].forEach(async item => {
await customElements.whenDefined(item.localName);
await item.updateComplete;
item.checked = item.getAttribute('value') === colorScheme;
});
}
let colorScheme = window.getColorScheme();
// Selection is not preserved when changing page, so update when opening dropdown
document.addEventListener('wa-show', event => {
const colorSchemeSelector = event.target.closest('#color-scheme-selector');
if (!colorSchemeSelector) return;
updateSelection();
});
// Listen for selections
document.addEventListener('wa-select', event => {
const menu = event.target.closest('#color-scheme-selector wa-menu');
if (!menu) return;
setColorScheme(event.detail.item.value);
});
// Update the color scheme when the preference changes
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => setColorScheme(colorScheme));
// Toggle with backslash
document.addEventListener('keydown', event => {
if (
event.key === '\\' &&
!event.composedPath().some(el => ['input', 'textarea'].includes(el?.tagName?.toLowerCase()))
) {
event.preventDefault();
setColorScheme(window.isDark() ? 'light' : 'dark');
}
});
// Set the initial color scheme and sync the UI
setColorScheme(colorScheme);
})();

View File

@@ -1,25 +0,0 @@
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;
}
}
}
document.documentElement.addEventListener('wa-input', e => {
if (e.target?.matches('#block-filter wa-input')) {
updateResults(e.target);
}
});

View File

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

View File

@@ -0,0 +1,86 @@
//
// Preset theme selector
//
(() => {
function setPresetTheme(newPresetTheme) {
presetTheme = newPresetTheme;
localStorage.setItem('presetTheme', presetTheme);
const stylesheet = document.getElementById('theme-stylesheet');
const newStylesheet = Object.assign(document.createElement('link'), {
href: `/dist/styles/themes/${presetTheme}.css`,
rel: 'preload',
as: 'style',
});
newStylesheet.addEventListener(
'load',
() => {
newStylesheet.rel = 'stylesheet';
newStylesheet.id = stylesheet.id;
requestAnimationFrame(() => {
stylesheet.remove();
});
},
{ once: true },
);
document.head.append(newStylesheet);
// Update the UI
updateSelection();
// Toggle the dark mode class
document.documentElement.classList.toggle(`wa-theme-${presetTheme}-dark`, window.isDark());
}
function updateSelection(container = document) {
const menu = container.querySelector('#preset-theme-selector wa-menu');
if (!menu) return;
[...menu.querySelectorAll('wa-menu-item')].forEach(async item => {
const isChecked = item.getAttribute('value') === presetTheme;
if (isChecked) {
container.querySelector('#preset-theme-selector__text').textContent = item.innerText;
}
await customElements.whenDefined(item.localName);
await item.updateComplete;
item.checked = isChecked;
});
}
let presetTheme = window.getPresetTheme();
// Selection is not preserved when changing page, so update when opening dropdown
document.addEventListener('wa-show', event => {
const presetThemeSelector = event.target.closest('#preset-theme-selector');
if (!presetThemeSelector) return;
updateSelection();
});
// Listen for selections
document.addEventListener('wa-select', event => {
const menu = event.target.closest('#preset-theme-selector wa-menu');
if (!menu) return;
setPresetTheme(event.detail.item.value);
});
// Update the color scheme when the preference changes
window.matchMedia('(prefers-preset-theme: dark)').addEventListener('change', () => setPresetTheme(presetTheme));
updateSelection();
/**
* Without this, there's a flash of the incorrect preset theme.
*/
function updateSelectionBeforeTurboLoad(e) {
const newElement = e.detail.newBody || e.detail.newFrame || e.detail.newStream;
if (!newElement) {
return;
}
updateSelection(newElement);
}
['turbo:before-render', 'turbo:before-stream-render', 'turbo:before-frame-render'].forEach(eventName => {
document.addEventListener(eventName, updateSelectionBeforeTurboLoad);
});
})();

View File

@@ -9,7 +9,6 @@ const icons = {
component: 'puzzle-piece',
document: 'file',
home: 'house',
native: 'code',
theme: 'palette',
};
let searchTimeout;
@@ -167,7 +166,6 @@ async function updateResults(query = '') {
li.setAttribute('data-selected', index === 0 ? 'true' : 'false');
if (page.url === '/') icon = icons.home;
if (page.url.startsWith('/docs/native')) icon = icons.native;
if (page.url.startsWith('/docs/components')) icon = icons.component;
if (page.url.startsWith('/docs/theme') || page.url.startsWith('/docs/restyle')) icon = icons.theme;

View File

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

View File

@@ -1,119 +0,0 @@
// Helper for view transitions
export function domChange(fn, { behavior = 'smooth' } = {}) {
const canUseViewTransitions =
document.startViewTransition && !window.matchMedia('(prefers-reduced-motion: reduce)').matches;
if (canUseViewTransitions && behavior === 'smooth') {
document.startViewTransition(fn);
} else {
fn(true);
}
}
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('wa-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);
});
},
});
// 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(theming.colorScheme.resolvedValue === 'dark' ? 'light' : 'dark');
}
});

View File

@@ -1,6 +1,6 @@
.code-example {
border: var(--wa-border-style) var(--wa-panel-border-width) var(--wa-color-neutral-border-quiet);
border-radius: var(--wa-border-radius-l);
border-radius: var(--wa-border-radius-m);
color: var(--wa-color-text-normal);
margin-block-end: var(--wa-flow-spacing);
}
@@ -41,8 +41,8 @@
border-bottom: none;
pre {
border-bottom-right-radius: var(--wa-border-radius-l);
border-bottom-left-radius: var(--wa-border-radius-l);
border-bottom-right-radius: var(--wa-border-radius-m);
border-bottom-left-radius: var(--wa-border-radius-m);
}
}
@@ -62,11 +62,11 @@
&:first-of-type {
border-left: none;
border-bottom-left-radius: var(--wa-border-radius-l);
border-bottom-left-radius: var(--wa-border-radius-m);
}
&:last-of-type {
border-bottom-right-radius: var(--wa-border-radius-l);
border-bottom-right-radius: var(--wa-border-radius-m);
}
&:focus-visible {

View File

@@ -1,12 +1,6 @@
pre {
background-color: var(--wa-color-gray-20);
color: white;
/* Ensures a discernible background color in dark mode
* Useful for themes that use gray-20 as --wa-color-surface-default */
.wa-dark & {
background-color: var(--wa-color-surface-lowered);
}
}
.code-comment,
.code-prolog,

View File

@@ -6,7 +6,7 @@ wa-copy-button.copy-button {
right: 0.25rem;
font-family: var(--wa-font-family-body);
color: var(--wa-color-gray-80);
border-radius: var(--wa-border-radius-m);
border-radius: var(--wa-border-radius-s);
padding: 0.25rem;
&:hover {

View File

@@ -10,8 +10,8 @@
--wa-brand-grey: #30323b;
}
.wa-dark .only-light,
.only-dark:not(.wa-dark, .wa-dark *) {
html.wa-theme-default-dark .only-light,
html:not(.wa-theme-default-dark) .only-dark {
display: none;
}
@@ -22,7 +22,7 @@ wa-page {
/* Header */
wa-page::part(header) {
background-color: var(--wa-color-surface-default);
border-bottom: var(--wa-border-style) var(--wa-panel-border-width) var(--wa-color-surface-border);
border-bottom: var(--wa-border-style) var(--wa-panel-border-width) var(--wa-color-neutral-border-quiet);
}
wa-page::part(body) {
@@ -34,6 +34,10 @@ wa-page::part(header-actions) {
}
wa-page > header {
flex: 1 1 auto;
display: flex;
align-items: center;
justify-content: space-between;
padding-inline: var(--wa-space-xl);
a[href='/'] {
@@ -77,7 +81,7 @@ wa-page > header {
}
wa-button#search-trigger {
--background-color: var(--wa-form-control-background-color);
--border-color: var(--wa-form-control-border-color);
--border-color: var(--wa-form-control-resting-color);
}
#search-trigger kbd {
font-size: var(--wa-font-size-2xs);
@@ -100,9 +104,17 @@ wa-page > header {
wa-details {
border: none;
background: none;
}
wa-details::part(header) {
padding: 0;
}
wa-details::part(content) {
padding-inline: 0;
padding-block-end: 0;
}
ul {
border-inline-start: var(--wa-border-width-s) solid var(--wa-color-surface-border);
font-size: var(--wa-font-size-s);
@@ -211,20 +223,6 @@ wa-page::part(menu) {
scrollbar-width: thin;
}
wa-page[view='mobile'] :is([slot='navigation-header'], [slot='navigation']) {
padding: 0;
& wa-details::part(icon) {
padding-inline: var(--wa-space-xs); /* aligns details icons with drawer close icon */
}
}
[slot='navigation-header'] wa-menu {
font-family: var(--wa-font-family-body);
font-size: var(--wa-font-size-m);
font-weight: var(--wa-font-weight-normal);
}
/* Main content */
wa-page > main {
max-width: 80ch;
@@ -240,7 +238,7 @@ h1.title wa-badge {
}
}
.block-info {
.component-info {
margin-block-end: var(--wa-flow-spacing);
}
@@ -271,7 +269,7 @@ h1.title wa-badge {
display: flex;
gap: 1rem;
border: var(--wa-border-style) var(--wa-border-width-s);
border-radius: var(--wa-border-radius-l);
border-radius: var(--wa-border-radius-m);
padding: 1rem;
margin-block-end: var(--wa-flow-spacing);
@@ -331,48 +329,8 @@ wa-page > main:has(> .index-grid) {
.index-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(22ch, 100%), 1fr));
grid-template-columns: repeat(auto-fit, minmax(min(20ch, 100%), 1fr));
gap: var(--wa-space-2xl);
margin-block-end: var(--wa-space-3xl);
a {
border-radius: var(--wa-border-radius-l);
text-decoration: none;
}
wa-card {
box-shadow: none;
--spacing: var(--wa-space-m);
inline-size: 100%;
&:hover {
--border-color: var(--wa-color-brand-border-loud);
border-color: var(--border-color);
box-shadow: 0 0 0 var(--wa-border-width-s) var(--border-color);
.page-name {
color: var(--wa-color-brand-on-quiet);
}
}
[slot='header'] {
display: flex;
}
&::part(header) {
background-color: var(--wa-color-neutral-fill-quiet);
border-bottom: none;
display: flex;
align-items: center;
justify-content: center;
min-block-size: calc(6rem + var(--spacing));
}
}
.page-name {
font-size: var(--wa-font-size-s);
font-weight: var(--wa-font-weight-action);
}
}
.index-category {
@@ -381,93 +339,123 @@ wa-page > main:has(> .index-grid) {
margin-block-start: var(--wa-space-2xl);
}
/* Swatches */
.swatch {
position: relative;
.index-grid a {
border-radius: var(--wa-border-radius-m);
}
.index-grid wa-card {
--box-shadow: none;
--spacing: var(--wa-space-m);
inline-size: 100%;
}
.index-grid wa-card:hover {
--border-color: var(--wa-color-brand-border-loud);
--box-shadow: 0 0 0 var(--wa-border-width-s) var(--border-color);
& .page-name {
color: var(--wa-color-brand-on-quiet);
}
}
.index-grid wa-card [slot='header'] {
display: flex;
}
.index-grid wa-card::part(header) {
background-color: var(--wa-color-neutral-fill-quiet);
border-bottom: none;
display: flex;
align-items: center;
justify-content: center;
min-block-size: calc(6rem + var(--spacing));
}
.index-grid .page-name {
font-size: var(--wa-font-size-s);
font-weight: var(--wa-font-weight-action);
}
/* Index Pages */
wa-page > main:has(> .index-grid) {
max-width: 120ch;
margin-inline: auto;
}
.index-summary {
max-inline-size: 80ch;
}
.index-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(20ch, 100%), 1fr));
gap: var(--wa-space-2xl);
}
.index-category {
grid-column: 1 / -1;
margin-block-end: 0;
margin-block-start: var(--wa-space-2xl);
}
.index-grid a {
border-radius: var(--wa-border-radius-m);
}
.index-grid wa-card {
--box-shadow: none;
--spacing: var(--wa-space-m);
inline-size: 100%;
}
.index-grid wa-card:hover {
--border-color: var(--wa-color-brand-border-loud);
--box-shadow: 0 0 0 var(--wa-border-width-s) var(--border-color);
& .page-name {
color: var(--wa-color-brand-on-quiet);
}
}
.index-grid wa-card [slot='header'] {
display: flex;
}
.index-grid wa-card::part(header) {
background-color: var(--wa-color-neutral-fill-quiet);
border-bottom: none;
display: flex;
align-items: center;
justify-content: center;
min-block-size: calc(6rem + var(--spacing));
}
.index-grid .page-name {
font-size: var(--wa-font-size-s);
font-weight: var(--wa-font-weight-action);
}
/* Swatches */
.swatch {
background-color: transparent;
border-color: var(--wa-color-neutral-border-normal);
border-style: var(--wa-border-style);
border-width: var(--wa-border-width-s);
border-radius: var(--wa-border-radius-m);
border-radius: var(--wa-border-radius-s);
box-sizing: border-box;
min-height: 2.5em;
padding: var(--wa-space-xs);
font-weight: var(--wa-font-weight-semibold);
line-height: var(--wa-line-height-condensed);
&.color {
border-color: transparent;
}
wa-copy-button {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: block;
--background-color-hover: transparent;
font-family: var(--wa-font-family-code);
&::part(button) {
display: block;
height: 100%;
width: 100%;
}
&::part(button):hover {
cursor: copy;
}
&::part(copy-icon),
&::part(success-icon),
&::part(error-icon) {
opacity: 0 !important;
}
}
}
table.colors {
thead {
th {
text-align: center;
padding-block: 0;
}
}
tbody {
tr {
border: none;
&:hover {
background: transparent;
}
}
th {
width: 0;
vertical-align: middle;
text-align: right;
}
td {
padding-inline: var(--wa-space-3xs);
padding-block: var(--wa-space-s);
}
}
line-height: 2.5;
height: 2.5em;
padding-inline: var(--wa-space-xs);
}
/* Layout Examples */
.layout-example-boundary {
border: var(--wa-border-width-s) dashed var(--wa-color-neutral-border-normal);
border-radius: var(--wa-border-radius-l);
border-radius: var(--wa-border-radius-m);
padding: var(--wa-space-s);
}
.layout-example-block {
background-color: var(--wa-color-indigo-60);
border-radius: var(--wa-border-radius-m);
border-radius: var(--wa-border-radius-s);
min-block-size: 4rem;
min-inline-size: 4rem;
}
@@ -503,9 +491,14 @@ table.colors {
/** desktop */
@media screen and not (max-width: 768px) {
wa-page [data-toggle-nav],
wa-page .only-mobile {
display: none;
}
/* Navigation sidebar */
wa-page::part(navigation) {
border-right: var(--wa-border-style) var(--wa-panel-border-width) var(--wa-color-surface-border);
border-right: var(--wa-border-style) var(--wa-panel-border-width) var(--wa-color-neutral-border-quiet);
}
wa-page > #sidebar {
@@ -520,39 +513,3 @@ table.colors {
padding: var(--wa-space-3xl);
}
}
.layout-theme {
wa-page > main {
max-width: 140ch;
.max-line-length {
max-width: 80ch;
}
}
iframe {
width: 100%;
min-height: 16lh;
height: 65vh;
max-height: 21lh;
}
#mix_and_match {
strong {
display: flex;
align-items: center;
gap: var(--wa-space-2xs);
margin-top: 1.2em;
}
wa-select::part(label) {
margin-block-end: 0;
}
wa-select[value='']::part(display-input),
wa-option[value=''] {
font-style: italic;
color: var(--wa-color-text-quiet);
}
}
}

View File

@@ -1,37 +0,0 @@
wa-card#drawer-card::part(header) {
--spacing: 0;
justify-content: flex-end;
overflow: hidden;
}
#block-filter {
margin-block-end: var(--wa-space-xl);
}
.index-grid.filtered {
h2 {
/* Hide headings while filtering */
display: none;
}
&:not(:has(> a:not([hidden]))) {
/* Were filtering and there are no results */
&::before {
content: var(--empty-message);
grid-column: 1 / -1;
color: var(--wa-color-on-quiet);
font-style: italic;
font-weight: var(--wa-font-weight-action);
}
/* Show empty state when there's a search filter and no results */
&[data-empty] {
--empty-message: attr(data-empty);
}
&:not([data-empty]) {
--empty-message: 'No results';
}
}
}

View File

@@ -18,7 +18,7 @@
.diff-grid > * {
height: 100%;
}
.diff-dialog {
.diff-dialog::part(dialog) {
max-width: 90vw;
width: 90vw;
}

View File

@@ -31,9 +31,6 @@
}
@media screen and (max-width: 1199px) {
wa-page [slot='aside'] {
display: none;
}
#outline {
padding-block: 0.25rem;
margin-block-end: -1rem;
@@ -45,6 +42,5 @@
#outline-expandable {
display: block;
margin-block-end: var(--wa-space-xl);
}
}

View File

@@ -1,14 +1,13 @@
#site-search {
width: 38rem;
position: absolute;
top: 0;
border-radius: var(--wa-border-radius-l);
padding: 0;
margin: 0 auto;
overflow: hidden;
--width: 38rem;
&::part(base) {
margin-block: 10rem;
&::part(dialog) {
position: absolute;
top: 0;
border-radius: var(--wa-border-radius-m);
padding: 0;
margin: 10rem auto;
overflow: hidden;
}
&::part(body) {
@@ -19,22 +18,21 @@
justify-content: center;
gap: 0.5rem;
}
@media screen and (max-width: 900px) {
max-width: calc(100% - 2rem);
&::part(base) {
margin-block: 1rem;
}
}
}
#site-search-container {
display: flex;
flex-direction: column;
max-height: calc(100vh - 20rem);
}
@media screen and (max-width: 900px) {
@media screen and (max-width: 900px) {
#site-search::part(dialog) {
max-width: calc(100% - 2rem);
margin-block: 1rem;
}
#site-search-container {
max-height: calc(100dvh - 2rem);
}
}
@@ -231,8 +229,8 @@ header {
gap: 2rem;
color: var(--wa-color-text-quiet);
border-top: var(--wa-panel-border-style) var(--wa-panel-border-width) var(--wa-color-neutral-border-quiet);
border-bottom-left-radius: var(--wa-border-radius-l);
border-bottom-right-radius: var(--wa-border-radius-l);
border-bottom-left-radius: var(--wa-border-radius-m);
border-bottom-right-radius: var(--wa-border-radius-m);
padding: 1rem;
kbd {

View File

@@ -1,14 +1,5 @@
/* Theme Builder Headers
* These styles control the appearance of the header of the theme builder,
* allowing each theme to have a unique hero section.
*/
/* #region Default */
html.wa-theme-default .preview-container {
container-name: default-theme;
}
@container default-theme (min-width: 0) {
/* #region Custom Styles */
@container preview (min-width: 0) {
.hero-background {
height: 47rem;
background-color: var(--wa-color-brand-fill-loud);
@@ -31,7 +22,7 @@ html.wa-theme-default .preview-container {
}
.hero wa-button[variant='brand'] {
--background-color: var(--wa-color-neutral-fill-quiet);
--background: var(--wa-color-neutral-fill-quiet);
--text-color: var(--wa-color-neutral-on-normal);
}
@@ -50,733 +41,10 @@ html.wa-theme-default .preview-container {
}
/* responsive */
@container default-theme (min-width: 1040px) {
@container preview (min-width: 1040px) {
.hero-background::after {
background-position: right bottom;
background-size: 90%;
}
}
/* #endregion */
/* #region Tailspin */
html.wa-theme-tailspin .preview-container {
container-name: tailspin-theme;
}
@container tailspin-theme (min-width: 0) {
.project-header {
background: var(--wa-color-surface-default);
}
.hero {
--hero-background-color: var(--wa-color-surface-default);
--hero-lines-color: color-mix(in oklab, var(--wa-color-neutral-fill-normal), transparent 30%);
background: linear-gradient(to top, var(--wa-color-surface-lowered), transparent 40%),
radial-gradient(circle at 10% 70%, color-mix(in oklab, var(--wa-color-red-50) 16%, transparent), transparent 30%),
radial-gradient(
circle at 40% 50%,
color-mix(in oklab, var(--wa-color-indigo-40) 16%, transparent),
transparent 40%
),
radial-gradient(circle at 80% 25%, color-mix(in oklab, var(--wa-color-red-50) 8%, transparent), transparent 20%),
radial-gradient(
circle at 80% 80%,
color-mix(in oklab, var(--wa-color-indigo-40) 16%, transparent),
transparent 40%
),
radial-gradient(circle at 90% 30%, color-mix(in oklab, var(--wa-color-red-60) 8%, transparent), transparent 10%),
linear-gradient(176deg, var(--hero-background-color), transparent 6rem),
linear-gradient(90deg, var(--hero-background-color), transparent),
repeating-linear-gradient(
var(--hero-lines-color),
var(--hero-lines-color) 1px,
transparent 1px,
transparent 10rem,
var(--hero-lines-color) 10rem
),
repeating-linear-gradient(
90deg,
var(--hero-lines-color),
var(--hero-lines-color) 1px,
transparent 1px,
transparent 10rem,
var(--hero-lines-color) 10rem
),
var(--hero-background-color);
& .title {
padding-block: calc(var(--wa-space-3xl) * 2) calc(var(--wa-space-3xl) * 10);
& .hero-title {
font-size: calc((var(--wa-font-size-4xl) * 1.125) * 1.125);
margin-block: 0 var(--wa-space-3xl);
}
}
}
.badge-stock {
position: absolute;
top: var(--wa-space-l);
left: var(--wa-space-l);
}
.message-composer wa-card {
&::part(footer) {
border: none;
}
& wa-icon-button {
color: var(--wa-color-base-50);
}
}
.products {
margin-top: calc(var(--wa-space-3xl) * -8);
}
pre.codeblock {
background-color: var(--wa-color-neutral-fill-normal);
}
}
/* #endregion */
/* #region Brutalist */
html.wa-theme-brutalist .preview-container {
container-name: brutalist-theme;
}
@container brutalist-theme (min-width: 0) {
.overlap {
background: var(--wa-color-surface-lowered) url('/assets/images/themer/brutalist/hero.png') 50% 3% no-repeat;
}
.strata.hero {
height: 42rem;
padding-top: 8rem;
padding-right: 50%;
--wa-font-weight-heading: var(--wa-font-weight-normal);
}
.hero wa-button {
--background: var(--wa-color-neutral-fill-loud);
margin-block-start: var(--wa-space-xl);
font-size: var(--wa-font-size-l);
}
/* imitating the large button styles */
.hero wa-button::part(label) {
padding: 0 var(--wa-space-l);
}
.strata.products {
margin-top: -20rem;
padding-top: 10rem;
}
.product-card .badge-stock {
position: absolute;
top: 0;
left: 0;
}
.product-card::part(footer) {
--wa-shadow-offset-x-s: 0;
--wa-shadow-offset-y-s: 0;
border-width: 0;
text-align: right;
}
.product-card wa-button::part(prefix) {
padding-inline-start: var(--wa-space-xs);
}
.product-card wa-button::part(suffix) {
padding-inline-end: var(--wa-space-xs);
}
.product-card wa-button:not(:last-child) {
margin-inline-end: var(--wa-space-xs);
}
/* imitating the rounded avatar */
.blog wa-avatar {
background-color: transparent;
}
.blog wa-avatar::part(image) {
border: var(--wa-border-width-s) solid var(--wa-color-brand-fill-loud);
border-radius: var(--wa-border-radius-circle);
}
.blog .landscape-frame {
box-shadow: var(--wa-shadow-s);
border: var(--wa-panel-border-width) var(--wa-panel-border-style) var(--wa-color-surface-border);
margin-block-end: var(--wa-shadow-offset-y-s);
}
.post-meta .categories {
display: flex;
flex-direction: column;
}
.message-composer wa-card::part(header) {
background-color: var(--wa-color-neutral-fill-loud);
}
.message-composer .grouped-buttons {
--wa-color-neutral-border-quiet: color-mix(in oklab, var(--wa-color-gray-30), white 40%);
}
.message-composer [slot='header'] wa-icon-button::part(base) {
color: var(--wa-color-neutral-on-loud);
}
.message-composer .grouped-buttons wa-icon-button::part(base):hover {
background-color: var(--wa-color-neutral-fill-normal);
color: var(--wa-color-text-normal);
}
.message-composer wa-tooltip::part(body) {
background-color: var(--wa-color-brand-fill-loud);
color: var(--wa-color-brand-on-loud);
}
.message-composer wa-tooltip::part(base__arrow) {
--arrow-color: var(--wa-color-brand-fill-loud);
}
.message-composer wa-card::part(footer) {
border-width: 0;
--padding: 0 var(--wa-space-s) var(--wa-space-s) var(--wa-space-s);
}
.support-table th {
background-color: var(--wa-color-neutral-fill-loud);
color: var(--wa-color-neutral-on-loud);
}
.support-table th wa-checkbox {
--background: transparent;
--border-color: var(--wa-color-neutral-on-loud);
--border-color-checked: var(--wa-color-neutral-on-loud);
}
.checkout-form {
font-size: var(--wa-font-size-s);
}
.checkout-form .square-frame {
box-shadow: var(--wa-shadow-s);
border: var(--wa-panel-border-width) var(--wa-panel-border-style) var(--wa-color-surface-border);
}
.wa-theme-brutalist.wa-dark {
& p a::before {
background: var(--wa-color-yellow-40);
}
& wa-rating {
--symbol-color: color-mix(in oklab, var(--wa-color-base-80), transparent 50%);
--symbol-color-active: var(--wa-color-gray-80);
}
& wa-alert {
&[variant='brand'] {
--icon-color: var(--wa-color-brand-on-quiet);
}
&[variant='success'] {
--icon-color: var(--wa-color-success-on-quiet);
}
&[variant='warning'] {
--icon-color: var(--wa-color-warning-on-quiet);
}
&[variant='danger'] {
--icon-color: var(--wa-color-danger-on-quiet);
}
&[variant='neutral'] {
--icon-color: var(--wa-color-neutral-on-quiet);
}
}
}
.preview-container pre {
border-inline-start: var(--wa-panel-border-width) var(--wa-panel-border-style) var(--wa-color-neutral-border-quiet);
}
p a,
a.highlite-link {
position: relative;
--wa-link-decoration-default: none;
--wa-link-decoration-hover: none;
&:before {
position: absolute;
z-index: -1;
content: '';
background: var(--wa-color-yellow-80);
width: 100%;
bottom: 0;
height: 50%;
opacity: 0.7;
transition: all var(--wa-transition-fast) ease-in-out;
}
&:hover:before {
height: 100%;
}
}
}
@container brutalist-theme (min-width: 1040px) and (max-width: 1140px) {
.product-card wa-button {
font-size: 80%;
}
}
/* #endregion */
/* #region Playful */
html.wa-theme-playful .preview-container {
container-name: playful-theme;
}
@container playful-theme (min-width: 0) {
.project-header {
color: var(--wa-color-success-on-normal);
}
.hero-background {
position: absolute;
z-index: -1;
top: -60rem;
left: 50%;
width: 250rem;
height: 250rem;
transform: translate(-50%, -50%);
flex-shrink: 0;
border-radius: 50%;
background: linear-gradient(180deg, var(--wa-color-green-90) 69.42%, var(--wa-color-green-80) 100%);
}
.hero-background::after {
background: url(/assets/images/themer/playful/hero.png) 0 50% no-repeat;
content: '';
position: absolute;
width: 520px;
height: 600px;
transform: translateX(-50%);
left: 50%;
bottom: 33rem;
}
.strata.hero {
padding-top: 19rem;
text-align: center;
}
.hero .hero-title {
color: var(--wa-color-success-on-normal);
font-size: var(--wa-font-size-4xl);
}
.hero .hero-cta {
font-size: var(--wa-font-size-l);
}
.product-card .badge-stock {
position: absolute;
top: var(--wa-space-xl);
right: var(--wa-space-xl);
}
.product-card {
--wa-panel-border-radius: var(--wa-border-radius-l);
}
.product-card::part(footer) {
border-width: 0;
}
.product-card wa-button {
margin-inline-end: var(--wa-space-xs);
}
.blog wa-avatar {
--size: 2rem;
}
.blog .landscape-frame {
border-radius: var(--wa-border-radius-l);
box-shadow: var(--wa-shadow-s);
}
.message-composer wa-card {
--background: var(--wa-color-surface-raised);
}
.message-composer wa-card::part(header) {
--wa-color-neutral-border-quiet: var(--wa-color-gray-70);
border-width: 0;
background-color: var(--wa-color-neutral-fill-quiet);
}
&:not(.wa-theme-premium.wa-dark) .message-composer wa-card {
--wa-color-neutral-fill-quiet: var(--wa-color-gray-90);
}
.message-composer wa-icon-button {
color: var(--wa-text-color-normal);
font-size: var(--wa-font-size-l);
}
.message-composer wa-card::part(footer) {
padding-top: 0;
}
.product-detail .price {
font-weight: var(--wa-font-weight-bold);
}
.support-table wa-card {
background: var(--wa-color-surface-raised);
}
.support-table th {
font-size: var(--wa-font-size-s);
font-weight: var(--wa-font-weight-bold);
background-color: var(--wa-color-surface-lowered);
}
.support-table tr {
border-top-color: var(--wa-color-surface-lowered);
}
.support-table .excerpt {
color: var(--wa-color-text-quiet);
}
.order-item img {
border-radius: var(--wa-border-radius-m);
}
.checkout-form {
font-size: var(--wa-font-size-s);
}
.checkout-form h2 {
font-size: var(--wa-font-size-3xl);
}
.checkout-form wa-input::part(form-control-label),
.checkout-form wa-select::part(form-control-label) {
font-size: var(--wa-font-size-s);
padding-block-end: var(--wa-space-xs);
}
.wa-theme-playful.wa-dark {
& .hero-background {
background: linear-gradient(180deg, var(--wa-color-green-60) 69.42%, var(--wa-color-green-30) 100%);
}
& .message-composer wa-card {
border-color: var(--wa-color-gray-30);
}
& .message-composer wa-card::part(header) {
background: var(--wa-color-gray-40);
}
& .message-composer wa-card::part(footer) {
border-top: 0;
}
& .message-composer .tools .grouped-buttons:not(:last-of-type) {
--wa-color-neutral-border-quiet: var(--wa-color-gray-40);
}
& .support-table th {
background: var(--wa-color-surface-lowered);
}
}
}
/* responsive */
@container playful-theme (min-width: 1040px) and (max-width: 1140px) {
.product-card wa-button {
font-size: 80%;
margin-inline-end: 0;
}
}
/* #endregion */
/* #region Active */
html.wa-theme-active .preview-container {
container-name: active-theme;
}
@container active-theme (min-width: 0) {
.hero-background::after {
backdrop-filter: brightness(0.8);
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
.hero-background {
background: linear-gradient(
var(--wa-color-green-80) 20%,
var(--wa-color-primary-80) 80.5%,
var(--wa-color-base-10) 80.5%,
var(--wa-color-primary-80) 100%
);
background: url(/assets/images/themer/active/hero.png) 50% 0px / cover no-repeat;
height: 40rem;
position: absolute;
top: 0;
left: 0;
right: 0;
z-index: -1;
}
.strata.hero {
height: 24rem;
}
.hero .title {
text-align: right;
margin-inline-start: 55%;
}
.hero .hero-title {
font-style: italic;
font-weight: var(--wa-font-weight-black);
font-size: var(--wa-font-size-2xl);
}
.hero wa-button[variant='brand'] {
--background: var(--wa-color-neutral-fill-quiet);
--text-color: var(--wa-color-neutral-on-normal);
}
wa-rating {
--symbol-color-active: var(--wa-color-brand-on-quiet);
}
}
/* responsive */
@container preview (min-width: 1100px) {
.hero-background {
height: 47rem;
}
.strata.hero {
height: 30rem;
}
.hero .title {
margin-block-start: 10%;
}
}
/* #endregion */
/* #region Premium */
html.wa-theme-premium .preview-container {
container-name: premium-theme;
}
@container premium-theme (min-width: 0) {
.project-header {
background-color: var(--wa-color-warning-fill-normal);
}
.strata.hero {
height: 60rem;
background: var(--wa-color-warning-fill-normal) url('/assets/images/themer/premium/hero.png') -4rem 12rem no-repeat;
background-size: cover;
padding-top: var(--wa-space-2xl);
padding-left: var(--wa-space-s);
text-align: right;
}
.hero .hero-title {
color: var(--wa-color-warning-on-normal);
font-style: italic;
}
.blog wa-avatar {
border-radius: var(--wa-border-radius-circle);
overflow: hidden;
}
.strata.products {
margin-top: -25rem;
padding-top: 10rem;
background: linear-gradient(0deg, var(--wa-color-surface-lowered) 80%, rgba(255, 255, 255, 0) 100%);
}
.product-card .badge-stock {
position: absolute;
top: var(--wa-space-xl);
right: var(--wa-space-xl);
}
.product-card wa-rating {
--symbol-size: var(--wa-font-size-m);
}
.product-card .title {
font-size: var(--wa-font-size-2xl);
}
.product-card .description {
color: var(--wa-color-text-quiet);
}
.product-card::part(footer) {
border-width: 0;
}
.product-card wa-button:not(:last-child) {
margin-inline-end: var(--wa-space-xs);
}
.blog .authors a {
--wa-color-text-link: var(--wa-color-text-quiet);
--wa-link-decoration-default: none;
}
.message-composer wa-card {
background: var(--wa-color-surface-raised);
}
.message-composer wa-card::part(header) {
border-width: 0;
background-color: var(--wa-color-neutral-fill-quiet);
}
&:not(.wa-theme-premium.wa-dark) .message-composer wa-card {
--wa-color-neutral-fill-quiet: var(--wa-color-base-95);
}
.message-composer wa-card::part(footer) {
border-width: 0;
--padding: var(--wa-space-s) var(--wa-space-xl);
}
.message-composer .grouped-buttons wa-icon-button::part(base) {
block-size: var(--wa-form-control-height-s);
inline-size: var(--wa-form-control-height-s);
justify-content: center;
}
.message-composer .grouped-buttons wa-icon-button::part(base):hover {
background-color: var(--wa-color-neutral-fill-normal);
color: var(--wa-color-text-normal);
}
.support-table th {
text-transform: uppercase;
font-weight: var(--wa-font-weight-normal);
color: var(--wa-color-text-quiet);
background-color: var(--wa-color-surface-lowered);
}
.support-table th:first-child {
border-radius: var(--wa-border-radius-l) 0 0 0;
}
.support-table th:last-child {
border-radius: 0 var(--wa-border-radius-l) 0 0;
}
.support-table tr {
--wa-color-surface-border: var(--wa-color-surface-lowered);
}
.checkout-form wa-input::part(form-control-label),
.checkout-form wa-select::part(form-control-label) {
font-size: var(--wa-font-size-s);
margin-block-end: var(--wa-space-xs);
}
.order-item .finish {
color: var(--wa-color-text-quiet);
font-size: var(--wa-font-size-s);
font-style: italic;
}
.wa-theme-premium.wa-dark {
& .message-composer .tools .grouped-buttons:not(:last-of-type) {
--wa-color-neutral-border-quiet: var(--wa-color-base-40);
}
& .preview-container pre,
& .preview-container code {
background-color: var(--wa-color-base-20);
}
/* syntax highlighting */
& .token {
&.selector {
color: var(--wa-color-green-80);
}
&.tag {
color: var(--wa-color-primary-80);
}
&.punctuation {
color: var(--wa-color-base-95);
}
&.attr-name {
color: var(--wa-color-green-80);
}
&.attr-value {
color: var(--wa-color-yellow-80);
}
}
}
.preview-container pre,
.preview-container code {
background-color: var(--wa-color-base-90);
font-size: var(--font-size-m);
}
/* syntax highlighting */
.token {
&.selector {
color: var(--wa-color-green-30);
}
&.tag {
color: var(--wa-color-primary-30);
}
&.punctuation {
color: var(--wa-color-base-10);
}
&.attr-name {
color: var(--wa-color-green-30);
}
&.attr-value {
color: var(--wa-color-yellow-30);
}
}
}
/* responsive */
@container preview (min-width: 1040px) {
.strata.hero {
background: var(--wa-color-warning-fill-normal) url('/assets/images/themer/premium/hero.png') -4rem 35% no-repeat;
padding-top: 15rem;
padding-left: 50%;
}
.strata.products {
margin-top: -25rem;
padding-top: 10rem;
background: linear-gradient(0deg, var(--wa-color-surface-lowered) 60%, rgba(255, 255, 255, 0) 100%);
}
}
/* #endregion */

View File

@@ -1,8 +1,7 @@
---
title: Animated Image
description: A component for displaying animated GIFs and WEBPs that play and pause on interaction.
tags: [imagery, niche]
icon: animated-image
tags: component
---
```html {.example}

View File

@@ -1,8 +1,7 @@
---
title: Animation
description: Animate elements declaratively with nearly 100 baked-in presets, or roll your own with custom keyframes.
tags: [helpers, primitives]
icon: animation
tags: component
---
To animate an element, wrap it in `<wa-animation>` and set an animation `name`. The animation will not start until you add the `play` attribute. Refer to the [properties table](#properties) for a list of all animation options.
@@ -197,3 +196,4 @@ Animations won't play until you apply the `play` attribute. You can omit it init
animation.play = true;
});
</script>
```

View File

@@ -1,8 +1,7 @@
---
title: Avatar
description: Avatars are used to represent a person or object.
tags: [imagery, apps, content]
icon: avatar
tags: component
---
By default, a generic icon will be shown. You can personalize avatars by adding custom icons, initials, and images. You should always provide a `label` for assistive devices.

View File

@@ -1,8 +1,7 @@
---
title: Badge
description: Badges are used to draw attention and display statuses or counts.
tags: [feedback, content]
icon: badge
tags: component
---
```html {.example}
@@ -128,3 +127,4 @@ When including badges in menu items, use the `suffix` slot to make sure they're
<wa-menu-item>Comments <wa-badge slot="suffix" variant="neutral" pill>4</wa-badge></wa-menu-item>
<wa-menu-item>Replies <wa-badge slot="suffix" variant="neutral" pill>12</wa-badge></wa-menu-item>
</wa-menu>
```

View File

@@ -1,8 +1,7 @@
---
title: Breadcrumb
description: Breadcrumbs provide a group of links so users can easily navigate a website's hierarchy.
tags: [navigation]
icon: breadcrumb
tags: component
---
Breadcrumbs are usually placed before a page's main content with the current page shown last to indicate the user's position in the navigation.
@@ -172,3 +171,4 @@ Alternatively, you can place dropdown menus in a prefix or suffix slot.
</wa-dropdown>
</wa-breadcrumb-item>
</wa-breadcrumb>
```

View File

@@ -1,8 +1,7 @@
---
title: Button Group
description: Button groups can be used to group related buttons into sections.
tags: [actions, forms, apps]
icon: button-group
tags: component
---
```html {.example}
@@ -140,17 +139,15 @@ Pill buttons are supported through the button's `pill` attribute.
</wa-button-group>
```
### Dropdowns and Native Buttons in Button Groups
### Dropdowns in Button Groups
Other elements can also be placed inside button groups:
- Native buttons as long as their [native styles](/docs/native/button) are included
- Dropdowns as long as the trigger is either a `<wa-button>`, or a `<button>` with [native styles](/docs/native/button) included.
Dropdowns can be placed inside button groups as long as the trigger is an `<wa-button>` element.
```html {.example}
<wa-button-group label="Example Button Group">
<wa-button>Button</wa-button>
<button>Native Button</button>
<wa-dropdown hoist>
<wa-button>Button</wa-button>
<wa-dropdown>
<wa-button slot="trigger" caret>Dropdown</wa-button>
<wa-menu>
<wa-menu-item>Item 1</wa-menu-item>
@@ -168,9 +165,9 @@ Create a split button using a button and a dropdown. Use a [visually hidden](/do
```html {.example}
<wa-button-group label="Example Button Group">
<wa-button variant="brand">Save</wa-button>
<wa-dropdown placement="bottom-end" hoist>
<wa-dropdown placement="bottom-end">
<wa-button slot="trigger" variant="brand" caret>
<span class="wa-visually-hidden">More options</span>
<wa-visually-hidden>More options</wa-visually-hidden>
</wa-button>
<wa-menu>
<wa-menu-item>Save</wa-menu-item>

View File

@@ -1,9 +1,8 @@
---
title: Button
description: Buttons represent actions that are available to the user.
tags: [actions, forms]
native: button
icon: button
tags: component
natives: button
---
```html {.example}
@@ -29,37 +28,35 @@ Use the `variant` attribute to set the button's semantic variant.
Use the `appearance` attribute to change the button's visual appearance.
```html {.example}
<div class="wa-stack">
<div class="wa-gap-m">
<wa-button appearance="accent" variant="neutral">Accent</wa-button>
<wa-button appearance="outlined" variant="neutral">Outlined</wa-button>
<wa-button appearance="filled" variant="neutral">Filled</wa-button>
<wa-button appearance="plain" variant="neutral">Plain</wa-button>
</div>
<div class="wa-gap-m">
<wa-button appearance="accent" variant="brand">Accent</wa-button>
<wa-button appearance="outlined" variant="brand">Outlined</wa-button>
<wa-button appearance="filled" variant="brand">Filled</wa-button>
<wa-button appearance="plain" variant="brand">Plain</wa-button>
</div>
<div class="wa-gap-m">
<wa-button appearance="accent" variant="success">Accent</wa-button>
<wa-button appearance="outlined" variant="success">Outlined</wa-button>
<wa-button appearance="filled" variant="success">Filled</wa-button>
<wa-button appearance="plain" variant="success">Plain</wa-button>
</div>
<div class="wa-gap-m">
<wa-button appearance="accent" variant="warning">Accent</wa-button>
<wa-button appearance="outlined" variant="warning">Outlined</wa-button>
<wa-button appearance="filled" variant="warning">Filled</wa-button>
<wa-button appearance="plain" variant="warning">Plain</wa-button>
</div>
<div class="wa-gap-m">
<wa-button appearance="accent" variant="danger">Accent</wa-button>
<wa-button appearance="outlined" variant="danger">Outlined</wa-button>
<wa-button appearance="filled" variant="danger">Filled</wa-button>
<wa-button appearance="plain" variant="danger">Plain</wa-button>
</div>
<div style="margin-block-end: 1rem;">
<wa-button appearance="accent" variant="neutral">Accent</wa-button>
<wa-button appearance="filled" variant="neutral">Filled</wa-button>
<wa-button appearance="outlined" variant="neutral">Outlined</wa-button>
<wa-button appearance="plain" variant="neutral">Text</wa-button>
</div>
<div style="margin-block-end: 1rem;">
<wa-button appearance="accent" variant="brand">Accent</wa-button>
<wa-button appearance="filled" variant="brand">Filled</wa-button>
<wa-button appearance="outlined" variant="brand">Outlined</wa-button>
<wa-button appearance="plain" variant="brand">Text</wa-button>
</div>
<div style="margin-block-end: 1rem;">
<wa-button appearance="accent" variant="success">Accent</wa-button>
<wa-button appearance="filled" variant="success">Filled</wa-button>
<wa-button appearance="outlined" variant="success">Outlined</wa-button>
<wa-button appearance="plain" variant="success">Text</wa-button>
</div>
<div style="margin-block-end: 1rem;">
<wa-button appearance="accent" variant="warning">Accent</wa-button>
<wa-button appearance="filled" variant="warning">Filled</wa-button>
<wa-button appearance="outlined" variant="warning">Outlined</wa-button>
<wa-button appearance="plain" variant="warning">Text</wa-button>
</div>
<div>
<wa-button appearance="accent" variant="danger">Accent</wa-button>
<wa-button appearance="filled" variant="danger">Filled</wa-button>
<wa-button appearance="outlined" variant="danger">Outlined</wa-button>
<wa-button appearance="plain" variant="danger">Text</wa-button>
</div>
```

View File

@@ -1,9 +1,7 @@
---
title: Callout
description: Callouts are used to display important messages inline.
tags: [feedback, content]
icon: callout
native: callout
tags: component
---
```html {.example}
@@ -59,71 +57,6 @@ Set the `variant` attribute to change the callout's variant.
</wa-callout>
```
### Appearance
Use the `appearance` attribute to change the callout's visual appearance (the default is `outlined filled`).
```html {.example}
<wa-callout variant="brand" appearance="outlined accent">
<wa-icon slot="icon" name="check-to-slot"></wa-icon>
This <strong>accent</strong> callout is also <strong>outlined</strong>
</wa-callout>
<br />
<wa-callout variant="brand" appearance="accent">
<wa-icon slot="icon" name="square-check"></wa-icon>
This <strong>accent</strong> callout draws attention without an outline
</wa-callout>
<br />
<wa-callout variant="brand" appearance="outlined filled">
<wa-icon slot="icon" name="fill-drip" variant="regular"></wa-icon>
This callout is both <strong>filled</strong> and <strong>outlined</strong>
</wa-callout>
<br />
<wa-callout variant="brand" appearance="filled">
<wa-icon slot="icon" name="fill" variant="regular"></wa-icon>
This callout is only <strong>filled</strong>
</wa-callout>
<br />
<wa-callout variant="brand" appearance="outlined">
<wa-icon slot="icon" name="lines-leaning" variant="regular"></wa-icon>
Here's an <strong>outlined</strong> callout
</wa-callout>
<br />
<wa-callout variant="brand" appearance="plain">
<wa-icon slot="icon" name="font" variant="regular"></wa-icon>
No bells and whistles on this <strong>plain</strong> callout
</wa-callout>
```
### Sizes
Use the `size` attribute to change a callout's size.
```html {.example}
<wa-callout variant="brand" appearance="outlined accent" size="large">
<wa-icon slot="icon" name="circle-info" variant="solid"></wa-icon>
This is meant to be very emphasized.
</wa-callout>
<wa-callout>
<wa-icon slot="icon" name="circle-info" variant="regular"></wa-icon>
Normal-sized callout.
</wa-callout>
<wa-callout variant="plain" appearance="plain" size="small">
<wa-icon slot="icon" name="circle-info" variant="regular"></wa-icon>
Just a small tip!
</wa-callout>
```
### Without Icons
Icons are optional. Simply omit the `icon` slot if you don't want them.

View File

@@ -1,8 +1,7 @@
---
title: Card
description: Cards can be used to group related subjects in a container.
tags: [organization, layout]
icon: card
tags: component
---
```html {.example}
@@ -60,8 +59,7 @@ Basic cards aren't very exciting, but they can display any content you want them
### Card with Header
Headers can be used to display titles and more.
If using SSR, you need to also use the `with-header` attribute to add a header to the card (if not, it is added automatically).
Headers can be used to display titles and more. Use the `with-header` attribute to add a header to the card.
```html {.example}
<wa-card with-header class="card-header">
@@ -96,8 +94,7 @@ If using SSR, you need to also use the `with-header` attribute to add a header t
### Card with Footer
Footers can be used to display actions, summaries, or other relevant content.
If using SSR, you need to also use the `with-footer` attribute to add a footer to the card (if not, it is added automatically).
Footers can be used to display actions, summaries, or other relevant content. Use the `with-footer` attribute to add a footer to the card.
```html {.example}
<wa-card with-footer class="card-footer">
@@ -124,8 +121,7 @@ If using SSR, you need to also use the `with-footer` attribute to add a footer t
### Images
Card images are displayed atop the card and will stretch to fit.
If using SSR, you need to also use the `with-image` attribute to add an image to the card (if not, it is added automatically).
Card images are displayed atop the card and will stretch to fit. Use the `with-image` attribute to add an image to the card.
```html {.example}
<wa-card with-image class="card-image">

View File

@@ -3,7 +3,6 @@ title: Carousel Item
description: A carousel item represent a slide within a carousel.
tags: component
parent: carousel
icon: carousel-item
---
```html {.example}

View File

@@ -1,8 +1,7 @@
---
title: Carousel
description: Carousels display an arbitrary number of content slides along a horizontal or vertical axis.
tags: [imagery, disclosure]
icon: carousel
tags: component
---
```html {.example}

View File

@@ -1,7 +1,6 @@
---
title: Component Cheatsheet
layout: docs
unlisted: true
---
<style>
@@ -45,7 +44,7 @@ table code {
}
}
</script>
<script type="module" src="./cheatsheet.js"></script>
<script type="module" src="/assets/scripts/cheatsheet.js"></script>
{% for type, all in componentsBy -%}
{% set typeTitle = "CSS custom properties" if type == "cssProperty" else ("CSS parts" if type == "cssPart" else (type | title) + "s") %}
@@ -65,21 +64,15 @@ table code {
<tr>
<th><code>{{ name }}{{ "()" if type == "method" }}</code></th>
<td>
{% set componentLinks = [] %}
{% for component in thingComponents %}
{%- set link -%}
<a href="../{{ component.slug }}"><code>&lt;{{ component.tagName }}&gt;</code></a>
{%- endset -%}
{# https://giuliachiola.dev/posts/add-items-to-an-array-in-nunjucks/ #}
{% set componentLinks = (componentLinks.push(link), componentLinks) %}
{%- endfor -%}
{% if thingComponents.length > 1 %}
<details open>
<summary><strong>{{ thingComponents.length }}</strong> components</summary>
{{ componentLinks | safe }}
{% endif %}
{% for component in thingComponents %}
<a href="../{{ component.slug }}"><code>&lt;{{ component.tagName }}&gt;</code></a>
{%- endfor -%}
{% if thingComponents.length > 1 %}
</details>
{% else %}
{{ componentLinks | safe }}
{% endif %}
</td>
</tr>

View File

@@ -1,9 +1,7 @@
---
title: Checkbox
description: Checkboxes allow the user to toggle an option on or off.
tags: [inputs, forms]
native: checkbox
icon: checkbox
tags: component
---
```html {.example}

View File

@@ -174,7 +174,7 @@ i.e. the previews of demos using the `viewport` attribute, but also opening demo
Just setting `border-radius` or `border` should work as expected:
```html{.example}
<wa-code-demo style="border: 2px dotted var(--wa-color-blue-50); border-radius: var(--wa-border-radius-m)">
<wa-code-demo style="border: 2px dotted var(--wa-color-blue-50); border-radius: var(--wa-border-radius-s)">
<pre><code class="language-html">
&lt;button&gt;Click me!&lt;/button&gt;
&lt;wa-button&gt;Click me!&lt;/wa-button&gt;

View File

@@ -1,9 +1,7 @@
---
title: Color Picker
description: Color pickers allow the user to select a color.
tags: [inputs, forms]
native: input
icon: color-picker
tags: component
---
```html {.example}
@@ -39,12 +37,10 @@ Set the color picker's format with the `format` attribute. Valid options include
To prevent users from toggling the format themselves, add the `no-format-toggle` attribute.
```html {.example}
<div class="wa-grid" style="--min-column-size: 12ch;">
<wa-color-picker format="hex" value="#4a90e2" label="Pick a hex color"></wa-color-picker>
<wa-color-picker format="rgb" value="rgb(80, 227, 194)" label="Pick an RGB color"></wa-color-picker>
<wa-color-picker format="hsl" value="hsl(290, 87%, 47%)" label="Pick an HSL color"></wa-color-picker>
<wa-color-picker format="hsv" value="hsv(55, 89%, 97%)" label="Pick an HSV color"></wa-color-picker>
</div>
<wa-color-picker format="hex" value="#4a90e2" label="Select a color"></wa-color-picker>
<wa-color-picker format="rgb" value="rgb(80, 227, 194)" label="Select a color"></wa-color-picker>
<wa-color-picker format="hsl" value="hsl(290, 87%, 47%)" label="Select a color"></wa-color-picker>
<wa-color-picker format="hsv" value="hsv(55, 89%, 97%)" label="Select a color"></wa-color-picker>
```
### Swatches

View File

@@ -1,11 +1,3 @@
{
"layout": "component.njk",
"tags": ["components"],
"eleventyComputed": {
"component": "{{ components[page.fileSlug] }}",
"description": "{{ components[page.fileSlug].summary }}",
"status": "{{ components[page.fileSlug].status }}",
"since": "{{ components[page.fileSlug].since }}",
"element": "<{{ components[page.fileSlug].tagName }}>"
}
"layout": "component.njk"
}

View File

@@ -1,8 +1,7 @@
---
title: Copy Button
description: Copies data to the clipboard when the user clicks the button.
tags: [actions, apps]
icon: copy-button
tags: component
---
```html {.example}

View File

@@ -1,9 +1,8 @@
---
title: Details
description: Details show a brief summary and expand to show additional content.
tags: [organization, apps, content, disclosure]
icon: details
native: details
tags: component
natives: details
---
<!-- cspell:dictionaries lorem-ipsum -->

View File

@@ -1,10 +1,8 @@
---
title: Dialog
description: 'Dialogs, sometimes called "modals", appear above the page and require the user''s immediate attention.'
tags: [organization, apps, disclosure]
icon: dialog
native: dialog
keywords: modal
tags: component
natives: dialog
---
<!-- cspell:dictionaries lorem-ipsum -->

View File

@@ -1,8 +1,7 @@
---
title: Divider
description: Dividers are used to visually separate or group elements.
tags: [organization, content, forms]
icon: divider
tags: component
---
```html {.example}

View File

@@ -1,8 +1,7 @@
---
title: Drawer
description: Drawers slide in from a container to expose additional options and information.
tags: [organization, disclosure]
icon: drawer
tags: component
---
<!-- cspell:dictionaries lorem-ipsum -->

View File

@@ -1,8 +1,7 @@
---
title: Dropdown
description: 'Dropdowns expose additional content that "drops down" in a panel.'
tags: [actions, apps]
icon: dropdown
tags: component
---
Dropdowns consist of a trigger and a panel. By default, activating the trigger will expose the panel and interacting outside of the panel will close it.

View File

@@ -1,8 +1,7 @@
---
title: Format Bytes
description: Formats a number as a human readable bytes value.
tags: [helpers, content, inline]
icon: format-bytes
tags: component
---
```html {.example}

View File

@@ -1,8 +1,7 @@
---
title: Format Date
description: Formats a date/time using the specified locale and options.
tags: [helpers, content, inline]
icon: format-date
tags: component
---
Localization is handled by the browser's [`Intl.DateTimeFormat` API](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat). No language packs are required.

View File

@@ -1,8 +1,7 @@
---
title: Format Number
description: Formats a number using the specified locale and options.
tags: [helpers, content, inline]
icon: format-number
tags: component
---
Localization is handled by the browser's [`Intl.NumberFormat` API](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat). No language packs are required.

View File

@@ -1,8 +1,7 @@
---
title: Icon Button
description: Icons buttons are simple, icon-only buttons that can be used for actions and in toolbars.
tags: [actions, apps]
icon: icon-button
tags: component
---
For a full list of icons that come bundled with Web Awesome, refer to the [icon component](/docs/components/icon).

View File

@@ -1,8 +1,7 @@
---
title: Icon
description: Icons are symbols that can be used to represent various options within an application.
tags: [imagery, apps, popular]
icon: icon
tags: component
---
Web Awesome comes bundled with over 2,000 free icons courtesy of [Font Awesome](https://fontawesome.com/). These icons are part of the `default` icon library. Font Awesome Pro users can unlock additional icon families. Or, if you prefer, you can register your own [custom icon library](#icon-library).

View File

@@ -1,8 +1,7 @@
---
title: Image Comparer
description: Compare visual differences between similar photos with a sliding panel.
tags: [imagery, niche]
icon: image-comparer
tags: component
---
For best results, use images that share the same dimensions. The slider can be controlled by dragging or pressing the left and right arrow keys. (Tip: press shift + arrows to move the slider in larger intervals, or home + end to jump to the beginning or end.)

View File

@@ -1,8 +1,7 @@
---
title: Include
description: Includes give you the power to embed external HTML files into the page.
tags: [helpers, primitives]
icon: include
tags: component
---
Included files are asynchronously requested using `window.fetch()`. Requests are cached, so the same file can be included multiple times, but only one request will be made.

View File

@@ -0,0 +1,492 @@
---
title: Components
description: Browse the library of customizable, framework-friendly web components included in Web Awesome.
layout: page-outline
---
<p class="index-summary">Components are the essential building blocks to create intuitive, cohesive experiences. Browse the library of customizable, framework-friendly web components included in Web Awesome.</p>
<div id="component-filter">
<wa-input type="search" placeholder="Search components" clearable autofocus></wa-input>
</div>
<div id="component-grid" class="index-grid">
<h2 class="index-category">Actions</h2>
<a href="/docs/components/button">
<wa-card with-header>
<div slot="header">
{% include "svgs/button.njk" %}
</div>
<span class="page-name">Button</span>
</wa-card>
</a>
<a href="/docs/components/button-group">
<wa-card with-header>
<div slot="header">
{% include "svgs/button-group.njk" %}
</div>
<span class="page-name">Button Group</span>
</wa-card>
</a>
<a href="/docs/components/copy-button">
<wa-card with-header>
<div slot="header">
{% include "svgs/copy-button.njk" %}
</div>
<span class="page-name">Copy Button</span>
</wa-card>
</a>
<a href="/docs/components/dropdown">
<wa-card with-header>
<div slot="header">
{% include "svgs/dropdown.njk" %}
</div>
<span class="page-name">Dropdown</span>
</wa-card>
</a>
<a href="/docs/components/icon-button">
<wa-card with-header>
<div slot="header">
{% include "svgs/icon-button.njk" %}
</div>
<span class="page-name">Icon Button</span>
</wa-card>
</a>
<a href="/docs/components/menu">
<wa-card with-header>
<div slot="header">
{% include "svgs/menu.njk" %}
</div>
<span class="page-name">Menu</span>
</wa-card>
</a>
<a href="/docs/components/qr-code">
<wa-card with-header>
<div slot="header">
{% include "svgs/qr-code.njk" %}
</div>
<span class="page-name">QR Code</span>
</wa-card>
</a>
<h2 class="index-category" style="grid-column: 1 / -1">Feedback and Status</h2>
<a href="/docs/components/badge">
<wa-card with-header>
<div slot="header">
{% include "svgs/badge.njk" %}
</div>
<span class="page-name">Badge</span>
</wa-card>
</a>
<a href="/docs/components/callout">
<wa-card with-header>
<div slot="header">
{% include "svgs/callout.njk" %}
</div>
<span class="page-name">Callout</span>
</wa-card>
</a>
<a href="/docs/components/progress-bar">
<wa-card with-header>
<div slot="header">
{% include "svgs/progress-bar.njk" %}
</div>
<span class="page-name">Progress Bar</span>
</wa-card>
</a>
<a href="/docs/components/progress-ring">
<wa-card with-header>
<div slot="header">
{% include "svgs/progress-ring.njk" %}
</div>
<span class="page-name">Progress Ring</span>
</wa-card>
</a>
<a href="/docs/components/skeleton">
<wa-card with-header>
<div slot="header">
{% include "svgs/skeleton.njk" %}
</div>
<span class="page-name">Skeleton</span>
</wa-card>
</a>
<a href="/docs/components/spinner" data-keywords="loader">
<wa-card with-header>
<div slot="header">
{% include "svgs/spinner.njk" %}
</div>
<span class="page-name">Spinner</span>
</wa-card>
</a>
<a href="/docs/components/tag">
<wa-card with-header>
<div slot="header">
{% include "svgs/tag.njk" %}
</div>
<span class="page-name">Tag</span>
</wa-card>
</a>
<a href="/docs/components/tooltip">
<wa-card with-header>
<div slot="header">
{% include "svgs/tooltip.njk" %}
</div>
<span class="page-name">Tooltip</span>
</wa-card>
</a>
<h2 class="index-category">Imagery</h2>
<a href="/docs/components/animated-image">
<wa-card with-header>
<div slot="header">
{% include "svgs/animated-image.njk" %}
</div>
<span class="page-name">Animated Image</span>
</wa-card>
</a>
<a href="/docs/components/avatar">
<wa-card with-header>
<div slot="header">
{% include "svgs/avatar.njk" %}
</div>
<span class="page-name">Avatar</span>
</wa-card>
</a>
<a href="/docs/components/carousel">
<wa-card with-header>
<div slot="header">
{% include "svgs/carousel.njk" %}
</div>
<span class="page-name">Carousel</span>
</wa-card>
</a>
<a href="/docs/components/icon">
<wa-card with-header>
<div slot="header">
{% include "svgs/icon.njk" %}
</div>
<span class="page-name">Icon</span>
</wa-card>
</a>
<a href="/docs/components/image-comparer">
<wa-card with-header>
<div slot="header">
{% include "svgs/image-comparer.njk" %}
</div>
<span class="page-name">Image Comparer</span>
</wa-card>
</a>
<h2 class="index-category">Inputs</h2>
<a href="/docs/components/checkbox">
<wa-card with-header>
<div slot="header">
{% include "svgs/checkbox.njk" %}
</div>
<span class="page-name">Checkbox</span>
</wa-card>
</a>
<a href="/docs/components/color-picker">
<wa-card with-header>
<div slot="header">
{% include "svgs/color-picker.njk" %}
</div>
<span class="page-name">Color Picker</span>
</wa-card>
</a>
<a href="/docs/components/input" data-keywords="textfield text field">
<wa-card with-header>
<div slot="header">
{% include "svgs/input.njk" %}
</div>
<span class="page-name">Input</span>
</wa-card>
</a>
<a href="/docs/components/radio-group">
<wa-card with-header>
<div slot="header">
{% include "svgs/radio-group.njk" %}
</div>
<span class="page-name">Radio Group</span>
</wa-card>
</a>
<a href="/docs/components/range">
<wa-card with-header>
<div slot="header">
{% include "svgs/range.njk" %}
</div>
<span class="page-name">Range</span>
</wa-card>
</a>
<a href="/docs/components/rating">
<wa-card with-header>
<div slot="header">
{% include "svgs/rating.njk" %}
</div>
<span class="page-name">Rating</span>
</wa-card>
</a>
<a href="/docs/components/select">
<wa-card with-header>
<div slot="header">
{% include "svgs/select.njk" %}
</div>
<span class="page-name">Select</span>
</wa-card>
</a>
<a href="/docs/components/switch" data-keywords="toggle">
<wa-card with-header>
<div slot="header">
{% include "svgs/switch.njk" %}
</div>
<span class="page-name">Switch</span>
</wa-card>
</a>
<a href="/docs/components/textarea">
<wa-card with-header>
<div slot="header">
{% include "svgs/textarea.njk" %}
</div>
<span class="page-name">Textarea</span>
</wa-card>
</a>
<h2 class="index-category">Navigation</h2>
<a href="/docs/components/breadcrumb">
<wa-card with-header>
<div slot="header">
{% include "svgs/breadcrumb.njk" %}
</div>
<span class="page-name">Breadcrumb</span>
</wa-card>
</a>
<a href="/docs/components/tab-group">
<wa-card with-header>
<div slot="header">
{% include "svgs/tab-group.njk" %}
</div>
<span class="page-name">Tab Group</span>
</wa-card>
</a>
<a href="/docs/components/tree">
<wa-card with-header>
<div slot="header">
{% include "svgs/tree.njk" %}
</div>
<span class="page-name">Tree</span>
</wa-card>
</a>
<h2 class="index-category">Organization</h2>
<a href="/docs/components/card">
<wa-card with-header>
<div slot="header">
{% include "svgs/card.njk" %}
</div>
<span class="page-name">Card</span>
</wa-card>
</a>
<a href="/docs/components/details">
<wa-card with-header>
<div slot="header">
{% include "svgs/details.njk" %}
</div>
<span class="page-name">Details</span>
</wa-card>
</a>
<a href="/docs/components/dialog" data-keywords="modal">
<wa-card with-header>
<div slot="header">
{% include "svgs/dialog.njk" %}
</div>
<span class="page-name">Dialog</span>
</wa-card>
</a>
<a href="/docs/components/divider">
<wa-card with-header>
<div slot="header">
{% include "svgs/divider.njk" %}
</div>
<span class="page-name">Divider</span>
</wa-card>
</a>
<a href="/docs/components/drawer">
<wa-card with-header id="drawer-card">
<div slot="header">
{% include "svgs/drawer.njk" %}
</div>
<span class="page-name">Drawer</span>
</wa-card>
</a>
<a href="/docs/components/page">
<wa-card with-header>
<div slot="header">
{% include "svgs/page.njk" %}
</div>
<span class="page-name">Page</span>
</wa-card>
</a>
<a href="/docs/components/split-panel">
<wa-card with-header>
<div slot="header">
{% include "svgs/split-panel.njk" %}
</div>
<span class="page-name">Split Panel</span>
</wa-card>
</a>
<h2 class="index-category">Utilities</h2>
<a href="/docs/components/animation">
<wa-card with-header>
<div slot="header">
{% include "svgs/animation.njk" %}
</div>
<span class="page-name">Animation</span>
</wa-card>
</a>
<a href="/docs/components/format-bytes">
<wa-card with-header>
<div slot="header">
{% include "svgs/format-bytes.njk" %}
</div>
<span class="page-name">Format Bytes</span>
</wa-card>
</a>
<a href="/docs/components/format-date">
<wa-card with-header>
<div slot="header">
{% include "svgs/format-date.njk" %}
</div>
<span class="page-name">Format Date</span>
</wa-card>
</a>
<a href="/docs/components/format-number">
<wa-card with-header>
<div slot="header">
{% include "svgs/format-number.njk" %}
</div>
<span class="page-name">Format Number</span>
</wa-card>
</a>
<a href="/docs/components/include">
<wa-card with-header>
<div slot="header">
{% include "svgs/include.njk" %}
</div>
<span class="page-name">Include</span>
</wa-card>
</a>
<a href="/docs/components/mutation-observer">
<wa-card with-header>
<div slot="header">
{% include "svgs/mutation-observer.njk" %}
</div>
<span class="page-name">Mutation Observer</span>
</wa-card>
</a>
<a href="/docs/components/popup">
<wa-card with-header>
<div slot="header">
{% include "svgs/popup.njk" %}
</div>
<span class="page-name">Popup</span>
</wa-card>
</a>
<a href="/docs/components/relative-time">
<wa-card with-header>
<div slot="header">
{% include "svgs/relative-time.njk" %}
</div>
<span class="page-name">Relative Time</span>
</wa-card>
</a>
<a href="/docs/components/resize-observer">
<wa-card with-header>
<div slot="header">
{% include "svgs/resize-observer.njk" %}
</div>
<span class="page-name">Resize Observer</span>
</wa-card>
</a>
<a href="/docs/components/visually-hidden">
<wa-card with-header>
<div slot="header">
{% include "svgs/visually-hidden.njk" %}
</div>
<span class="page-name">Visually Hidden</span>
</wa-card>
</a>
</div>
<div id="component-filter-empty" hidden>
No results
</div>
<script type="module">
const container = document.getElementById('component-filter');
const empty = document.getElementById('component-filter-empty');
const grid = document.getElementById('component-grid');
const input = container.querySelector('wa-input');
function updateResults() {
const filter = input.value.toLowerCase().trim();
// Hide headings while filtering
grid.querySelectorAll('h2').forEach(heading => {
heading.hidden = filter === '' ? false : true;
});
// Show matching components
grid.querySelectorAll('a').forEach(link => {
const content = link.textContent.toLowerCase();
const keywords = link.getAttribute('data-keywords') || '';
const isMatch = filter === '' || (content + keywords).includes(filter);
link.classList.toggle('hidden', !isMatch);
});
// Show empty state when there's a search filter and no results
if (filter !== '' && grid.querySelector('a:not(.hidden)') === null) {
empty.hidden = false;
} else {
empty.hidden = true;
}
}
input.addEventListener('wa-input', updateResults);
</script>
<style>
wa-card#drawer-card::part(header) {
--spacing: 0;
justify-content: flex-end;
overflow: hidden;
}
#component-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: var(--wa-space-m);
margin-block-start: var(--wa-space-2xl);
> a {
position: static;
visibility: visible;
&.hidden {
display: block;
position: absolute;
visibility: hidden;
}
}
}
#component-filter-empty {
border: dashed var(--wa-border-width-m) var(--wa-color-neutral-border-quiet);
border-radius: var(--wa-border-radius-m);
font-size: var(--wa-font-size-l);
color: var(--wa-color-text-quiet);
text-align: center;
padding-block: var(--wa-space-2xl);
margin-block-start: 0
}
</style>

Some files were not shown because too many files have changed in this diff Show More