Compare commits

..

51 Commits

Author SHA1 Message Date
Lea Verou
b001f144c8 Update library.ts 2025-05-09 13:27:45 -04:00
Lea Verou
8a7a7dc3b0 Update icon.md 2025-05-09 13:20:27 -04:00
Lea Verou
eefa57f61a Docs 2025-05-09 12:59:24 -04:00
Lea Verou
c69cabf413 Make registerIconLibrary() return the created instance 2025-05-09 12:49:13 -04:00
Lea Verou
7de111341e Update tests 2025-05-09 12:48:57 -04:00
Lea Verou
5558a0f2cd If no system() is defined, route through WA immediately 2025-05-09 12:48:47 -04:00
Lea Verou
de4221365a Drop spriteSheet option, add getMarkup option instead 2025-05-09 12:47:39 -04:00
Lea Verou
b85e6bcdf3 Aliases and external mappings 2025-05-08 15:16:16 -04:00
Lea Verou
ad539a00f2 Finish rewrite 2025-05-08 15:16:16 -04:00
Lea Verou
1559b08a63 Move FA kit-specific stuff to the FA icon library 2025-05-08 15:16:16 -04:00
Lea Verou
400db9a608 Revamp icon libraries
- Make `custom` an icon library too and remove `fromLibrary` distinction: now everything comes from a library
- `IconLibrary` class to handle functionality
- Move caching, fetching, and DOM construction to the icon library
- Rename `resolver` to `getUrl`, `fetched` to `inlined` and `addFetched()` to `inline()`
- Eliminate different handling between sprite sheets and other icons (e.g. mutation caching, template construction)
2025-05-08 15:16:16 -04:00
Lea Verou
1f280b3b9f Basic functionality with the new API 2025-05-08 15:16:16 -04:00
Lea Verou
e9f2104f15 Use system: prefix for system icons 2025-05-08 15:16:04 -04:00
Lea Verou
ad53a0aa56 Merge branch 'next' into icon-fetched 2025-05-07 16:11:26 -04:00
Cory LaViska
d94589d113 fix radio button validation (#912) 2025-05-07 18:46:24 +00:00
Cory LaViska
3508bf6339 Cloak improvement (#911)
* simplify cloak selector so it's 100% opt-in

* prevent the discovery event from bubbling

* update changelog

* whitespaec
2025-05-07 18:17:26 +00:00
Cory LaViska
61e65ffcb9 Improve native radio alignment (#904)
* show initial checks (feels less broken now)

* improve native radio dot alignment

* Update src/styles/native/radio.css

Co-authored-by: Lindsay M <126139086+lindsaym-fa@users.noreply.github.com>

* Update src/styles/native/radio.css

Co-authored-by: Lindsay M <126139086+lindsaym-fa@users.noreply.github.com>

* Update src/styles/native/radio.css

Co-authored-by: Lindsay M <126139086+lindsaym-fa@users.noreply.github.com>

---------

Co-authored-by: Lindsay M <126139086+lindsaym-fa@users.noreply.github.com>
2025-05-07 16:43:11 +00:00
Lea Verou
d79d753f4c Remove unused import 2025-05-02 17:36:08 -04:00
Lea Verou
6306955c74 Changes
- Signature
- `getKey` from URL
- Fix bugs with `deepEntries()` / `flatten()`
2025-05-02 17:32:44 -04:00
Lea Verou
cff9d56b3f Merge branch 'icon-fetched' of https://github.com/shoelace-style/webawesome into icon-fetched 2025-05-02 16:03:31 -04:00
Lea Verou
9fde4a820e Incorporate suggested edits
Co-Authored-By: Cory LaViska <55639+claviska@users.noreply.github.com>
2025-05-02 16:03:30 -04:00
Lea Verou
34c0b562fe Address feedback 2025-05-02 16:02:45 -04:00
Lea Verou
f499a932f9 Update docs/docs/resources/contributing.md
Co-authored-by: Cory LaViska <cory@abeautifulsite.net>
2025-05-02 14:35:37 -04:00
Lea Verou
85cca22d71 Update docs/docs/components/icon.md
Co-authored-by: Cory LaViska <cory@abeautifulsite.net>
2025-05-02 12:59:43 -04:00
Lea Verou
c261f25c65 Kill library="system" with 🔥 2025-05-02 11:36:56 -04:00
Lea Verou
ac1d412a8f Merge default and system libraries, fixes #901
- Pre-fetched icons for all libraries via the `fetched` property
- `addFetched()` to add more prefetched icons to an existing library (needed for extensibility story)
- Update docs
- Update changelog
- Separate unregistered and register library types
2025-05-02 11:36:56 -04:00
Lindsay M
aed28adbe4 Update to FA 6.7.2 and add all Duotone styles (#906)
* Update to FA 6.7.2 and add all Duotone styles

* Add changelog
2025-05-02 10:30:51 -04:00
Cory LaViska
15b8bde81b fix matter form control label + focus bug (#897) 2025-05-01 16:20:03 -04:00
Cory LaViska
9ee3fb5d28 Remove old code util; fix color scheme shortcut; fix initial examples (#905)
* fix color scheme shortcut

* remove old unused examples script and styles

* don't open the first example's code _sometimes_
2025-05-01 20:17:27 +00:00
Cory LaViska
47aa376c08 fix width example (#899) 2025-05-01 16:15:21 -04:00
Cory LaViska
69ba974a50 fix select + icon + clear spacing (#903)
* fix select + icon + clear spacing

* update styles

* revert
2025-05-01 18:56:02 +00:00
Lea Verou
8dfb411e5e Add typography metadata to themes 2025-04-30 09:42:27 -04:00
Lea Verou
a35a8fd2ad Theme typography.css fixes
- Add `:where([class^='wa-theme-'], [class*=' wa-theme-'])` to default `typography.css` since we declare mappings that need to be inherited by other themes.
- Remove declarations that redeclare defaults
- Ensure consistent order
- [Awesome] Switch code font to ui-monospace
2025-04-30 09:42:27 -04:00
Cory LaViska
2503005bbd fix radio group margin (#898) 2025-04-29 19:58:08 +00:00
Cory LaViska
78027170ea fix feature request advice (#895) 2025-04-29 14:41:01 -04:00
Konnor Rogers
a20aa48992 fix select (#892)
* fix select

* prettier
2025-04-29 12:33:03 -04:00
Lindsay M
ac8accd664 Fix color contrast in Premium theme, closes #880 (#890)
* Fix `--wa-color-brand-fill-loud` in Premium theme

* jk. *Actually* fix contrast in Premium theme.
2025-04-29 09:52:04 -04:00
Lea Verou
c571573063 [Skeleton] Remove base part, rel #207 (#885) 2025-04-28 17:04:06 -04:00
Lea Verou
e813440315 [Image-comparer] Several fixes + rename to comparer (#883)
Co-authored-by: Cory LaViska <cory@abeautifulsite.net>
2025-04-28 16:58:24 -04:00
Lea Verou
cfc3f181a3 Fix broken demo, improve passthrough CSS (#879)
Fixes several issues for components using the `display: contents` technique:
- Introduce `--display` CSS property to make it possible to override internal display value of base part.
- Ensure `display: contents` is always applied with `!important` to avoid rendering glitches like the one here
- Ensure non-inherited custom properties are also inherited
- Ensure the `hidden` attribute still works.
2025-04-23 11:52:23 -04:00
Konnor Rogers
7545f04c46 remove all references to /docs/installation (#872) 2025-04-21 17:01:09 -04:00
Lea Verou
38bd6528fe Only apply wa-button-group-vertical class when <wa-radio-group> is a radio button group, fixes #870 2025-04-15 18:07:57 -04:00
Lea Verou
2202ea9642 CSS custom properties to set certain component attributes (#447) 2025-04-15 17:56:38 -04:00
Konnor Rogers
58fbcede51 update patterns to isPro (#869) 2025-04-14 14:59:27 -04:00
Lea Verou
971200cc88 Move domChange() to separate module (#868) 2025-04-14 12:52:00 -04:00
Konnor Rogers
b75d3b615c fix login media query and leaking variables with Turbo (#867)
* fix media queries to use wa-desktop-only

* fix errors in remix.js when changing pages

* prettier
2025-04-14 11:11:08 -04:00
Kelsey Jackson
9d1c47449e Kj/first pattern launch (#861)
* dividing up e-commerce patterns

* started invoice table

* building out the filter

* Initial Commit

* switching machines

* switching machines

* started split image pattern:

* commiting to merge in next

* commiting to switch machines

* upated the product detail patterns

* updated product list patterns

* updated some patterns

* filter work

* added utilities to order history

* added utilities to order-summary

* updated width of container

* switching machines

* switching machines

* fixing conflicts

* editing descriptions

* updated descriptions

* adding some polish

* more filter work

* updated prouct preview

* Revisions to "Product Features"

* "Category Filters" revisions + `wa-placeholder` utility

* cleaned up product list

* tweaked product overview

* tweaked order history

* tweak category preview

* "Checkout Form" revisions

* Re-add `navigation` to "Product Features" carousel

* cleaned up shopping cart

* tweaked order summary

* Add missing file extension

* Fix typo in file name

* Revert checkout form changes in attempt to improve diff

* Reimplement checkout form revisions

* cleaned up checkout form

* Add "What's a Pattern?" and "Using Patterns" to index

* Update category descriptions and headings

* Add docs layout for patterns with stylesheet

* tweaked customer review

* Add checkout form example

* a little more polish

* more tweaks

* switching branches to merge

* created e-commerce index

* unlisted links

* updtated content

* updated content for category preview

* updated order history

* committing to bring branches up to next

* inital commit

* Reorganize app patterns into separate pages

* switching machines

* Add link style utilities

* Refactor product list patterns

* more polish

* Refactor product overview patterns

* switching machines

* Refactor shopping cart patterns

* committing to pull down changes

* updated product preview

* updated image

* updated incentives

* Clean up utility usage and code formatting for category previews

* started updating with style utilities

* Clean up utilities and formatting, replace placeholder text in order history

* updated incentive images

* updated reviews

* added review variant

* more polish

* some heavy duty updates

* removed store navigation

* switching machines
:

* Clean up utility usage and code formatting for order summaries

* Clean up utility usage and code formatting for product previews

* big switchover

* removed old file

* working on sidebar

* updated sidebar

* created info category

* splitting up the old blog page

* a little cleanup and adding detail

* got three done

* updated social share

* Quick formatting adjustment

* initial paywalls

* Use overviews in pattern subcategories (#826)

* Do not error if no pages

* Automatically set parents and tags for patterns

* Update overview.njk

* [WIP] Use overview pages for pattern listings

* Remove explicit parents

---------

Co-authored-by: lindsaym-fa <dev@lindsaym.design>

* some newsletter signups

* added login patterns

* reworked feeds'

* a few more

* added a few more

* add more app patterns

* App pattern tweaks (#827)

* Fix `patterns.css` reference

* Tweaks to action panel patterns

* Tweaks to comments patterns

* Progress on data display patterns

* Progress on empty state patterns

* Use email-related data from recent update in `pattern-main` (altered slightly)

* Tweaks to data display patterns

* Tweaks to empty state patterns

* Tweaks to FAQ patterns

* Tweaks to feed patterns

* Tweaks to grid patterns

* Tweaks to pagination patterns

* Tweaks to pricing patterns

* Tweaks to description list patterns

* Tweaks to leaderboard patterns

* Add and update names and descriptions

* Ensure comments fields have labels

* Tweak recent additions

* switching machines

* catching up with next

* added permissions pattern

* switching machines

* added post articles

* having local browset issues

* a few more app patterns

* took out info patterns

* spiffed up the action panels

* gave the action panels a once over

* added content to data display

* updated a few patterns

* clean up a few e-commerce patterns

* added time componen to reviews

---------

Co-authored-by: lindsaym-fa <dev@lindsaym.design>
Co-authored-by: Lea Verou <lea@verou.me>
Co-authored-by: Lindsay M <126139086+lindsaym-fa@users.noreply.github.com>
2025-04-14 11:10:54 -04:00
Lea Verou
003fd28cb0 Mark code-demo and viewport-demo as pro 2025-04-10 10:57:06 -05:00
Lea Verou
2f300d8930 [Popup] Use viewport as default boundary for flip too (#865) 2025-04-10 10:52:15 -05:00
Lea Verou
f13deb87bb Drop hoist from public API, use Popover API where supported (#351)
* First stab at using the Popover API for PE

* fix popup

* First stab at using the Popover API for PE

* fix popup

* Prettier

* Fix TS error

* Remove workaround

* Default to `strategy = fixed` if Popover API is not supported

* Clear out default UA styles for popovers

* Kill `hoist` with fire 🔥

* Refactor

* Update `@floating-ui/dom`

* Fix flipping and shofting

* Fix autosize

* Use `defaultBoundary` for `flip` too

That way we get the previous behavior for it.

* Remove `strategy`, just use `SUPPORTS_POPOVER` check where relevant

* Remove uses of `strategy`

* Use viewport as the default boundary for shifting and autosizing and add `boundary=scroll` to override

---------

Co-authored-by: konnorrogers <konnor5456@gmail.com>
2025-04-09 16:47:40 -05:00
Konnor Rogers
deb9fd70b3 fix pass through copying (#860)
* fix pass through copying

* prettier
2025-04-04 13:46:41 -04:00
154 changed files with 2023 additions and 1240 deletions

View File

@@ -36,10 +36,16 @@ const globalData = {
},
};
const passThroughExtensions = ['js', 'css', 'png', 'svg', 'jpg', 'mp4'];
const passThrough = [...passThroughExtensions.map(ext => 'docs/**/*.' + ext)];
export default function (eleventyConfig) {
/**
* If you plan to add or remove any of these extensions, make sure to let either Konnor or Cory know as these passthrough extensions
* will also need to be updated in the Web Awesome App.
*/
const passThroughExtensions = ['js', 'css', 'png', 'svg', 'jpg', 'mp4'];
const baseDir = process.env.BASE_DIR || 'docs';
const passThrough = [...passThroughExtensions.map(ext => path.join(baseDir, '**/*.' + ext))];
/**
* This is the guard we use for now to make sure our final built files dont need a 2nd pass by the server. This keeps us able to still deploy the bare HTML files on Vercel until the app is ready.
*/

View File

@@ -0,0 +1,9 @@
import { inlined } from '../../dist/components/icon/library.wa.js';
let { classic } = inlined;
let { solid, regular } = classic;
export default [
...Object.entries(solid).map(([name, svg]) => ({ name, family: 'solid', variant: 'solid', svg })),
...Object.entries(regular).map(([name, svg]) => ({ name, family: 'regular', variant: 'regular', svg })),
];

View File

@@ -4,7 +4,6 @@
{% include 'head.njk' %}
<meta name="theme-color" content="#f36944">
<script type="module" src="/assets/scripts/code-examples.js"></script>
<script type="module" src="/assets/scripts/scroll.js"></script>
<script type="module" src="/assets/scripts/turbo.js"></script>
@@ -20,7 +19,7 @@
</head>
<body class="layout-{{ layout | stripExtension }}{{ ' page-wide' if wide }}">
<!-- use view="desktop" as default to reduce layout jank on desktop site. -->
<wa-page view="desktop" disable-navigation-toggle="">
<wa-page view="desktop" disable-navigation-toggle="" mobile-breakpoint="1140">
<header slot="header" class="wa-split">
{# Logo #}
<div id="docs-branding">
@@ -33,13 +32,13 @@
<span class="wa-desktop-only">{% include "logo.njk" %}</span>
<span class="wa-mobile-only">{% include "logo-simple.njk" %}</span>
</a>
<small id="version-number" class="only-desktop">{{ package.version }}</small>
<wa-badge variant="warning" appearance="filled" class="only-desktop">Alpha</wa-badge>
<small id="version-number" class="wa-desktop-only">{{ package.version }}</small>
<wa-badge variant="warning" appearance="filled" class="wa-desktop-only">Alpha</wa-badge>
</div>
<div id="docs-toolbar" class="wa-cluster wa-gap-xs">
{# Desktop selectors #}
<div class="only-desktop wa-cluster wa-gap-xs">
<div class="wa-desktop-only wa-cluster wa-gap-xs">
{% include "preset-theme-selector.njk" %}
{% include "color-scheme-selector.njk" %}
</div>
@@ -48,7 +47,7 @@
<wa-button id="search-trigger" appearance="outlined" size="small" data-search>
<wa-icon slot="prefix" name="magnifying-glass"></wa-icon>
Search
<kbd slot="suffix" class="only-desktop">/</kbd>
<kbd slot="suffix" class="wa-desktop-only">/</kbd>
</wa-button>
{# Login #}

View File

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@@ -255,7 +255,7 @@
{# Importing #}
<h2>Importing</h2>
<p>
The <a href="/docs/installation/#quick-start-autoloading-via-cdn">autoloader</a> is the recommended way to import components. If you prefer to do it manually, use one of the following code snippets.
The <a href="/docs/#quick-start-autoloading-via-cdn">autoloader</a> is the recommended way to import components. If you prefer to do it manually, use one of the following code snippets.
</p>
<wa-tab-group label="How would you like to import this component?">

View File

@@ -126,7 +126,7 @@
{% for h in hues -%}
{%- if h !== 'gray' -%}
<wa-radio-button id="gray-undertone-{{ h }}" value="{{ h }}" label="{{ h | capitalize }}" style="--color: var(--wa-color-{{ h }})"></wa-radio-button>
<wa-tooltip for="gray-undertone-{{ h }}" hoist>
<wa-tooltip for="gray-undertone-{{ h }}">
{{ h | capitalize }}
</wa-tooltip>
{%- endif -%}
@@ -141,7 +141,7 @@
@input="tweaking.grayChroma = true" @change="tweaking.grayChroma = false">
<div slot="label">
Gray colorfulness
<wa-icon-button @click="grayChroma = originalGrayChroma" class="clear-button" name="circle-xmark" library="system" variant="regular" label="Reset"></wa-icon-button>
<wa-icon-button @click="grayChroma = originalGrayChroma" class="clear-button" name="system:circle-xmark" variant="regular" label="Reset"></wa-icon-button>
</div>
</wa-slider>
<div class="label-min">Neutral</div>
@@ -160,7 +160,7 @@
@change="tweaking.hue = tweaking.{{ hue }} = false">
<div slot="label">
Tweak {{ hue }} hue
<wa-icon-button @click="hueShifts.{{ hue }} = 0" class="clear-button" name="circle-xmark" library="system" variant="regular" label="Reset"></wa-icon-button>
<wa-icon-button @click="hueShifts.{{ hue }} = 0" class="clear-button" name="system:circle-xmark" variant="regular" label="Reset"></wa-icon-button>
</div>
</wa-slider>
<div class="label-min">More {{hueBefore}}</div>
@@ -204,7 +204,7 @@ style="--min: {{ chromaScaleBounds[0] }}; --max: {{ chromaScaleBounds[1] }};">
@change="tweaking.chroma = false">
<div slot="label">
Overall colorfulness
<wa-icon-button @click="chromaScale = 1" class="clear-button" name="circle-xmark" library="system" variant="regular" label="Reset"></wa-icon-button>
<wa-icon-button @click="chromaScale = 1" class="clear-button" name="system:circle-xmark" variant="regular" label="Reset"></wa-icon-button>
</div>
</wa-slider>
<div class="label-min">More muted</div>

View File

@@ -25,8 +25,8 @@ wa_data.palettes = {
{% endfor %}
};
</script>
<link href="{{ page.url }}../remix.css" rel="stylesheet">
<script src="{{ page.url }}../remix.js" type="module"></script>
<link href="/docs/themes/remix.css" rel="stylesheet">
<script src="/docs/themes/remix.js" type="module"></script>
{% endblock %}
{% block header %}

View File

@@ -102,12 +102,7 @@ const templates = {
export function codeExamplesPlugin(eleventyConfig, options = {}) {
const defaultOptions = {
container: 'body',
defaultOpen: (code, { outputPathIndex }) => {
return (
outputPathIndex === 1 && // is first
code.textContent.length < 500
); // is short
},
defaultOpen: () => false,
};
options = { ...defaultOptions, ...options };

View File

@@ -33,7 +33,7 @@ export function copyCodePlugin(eleventyConfig, options = {}) {
// Add a copy button
pre.innerHTML += `<wa-icon-button href="#${preId}" class="block-link-icon" name="link"></wa-icon-button>
<wa-copy-button from="${codeId}" class="copy-button" hoist></wa-copy-button>`;
<wa-copy-button from="${codeId}" class="copy-button"></wa-copy-button>`;
});
return doc.toString();

View File

@@ -1,59 +0,0 @@
document.addEventListener('click', event => {
const toggle = event.target?.closest('.code-example-toggle');
const pen = event.target?.closest('.code-example-pen');
// Toggle source
if (toggle) {
const codeExample = toggle.closest('.code-example');
const isOpen = !codeExample.classList.contains('open');
toggle.setAttribute('aria-expanded', isOpen ? 'true' : 'false');
codeExample.classList.toggle('open', isOpen);
}
// Edit in CodePen
if (pen) {
const codeExample = pen.closest('.code-example');
const code = codeExample.querySelector('code');
const cdnUrl = document.documentElement.dataset.cdnUrl;
const html =
`<script data-fa-kit-code="b10bfbde90" type="module" src="${cdnUrl}webawesome.loader.js"></script>\n` +
`<link rel="stylesheet" href="${cdnUrl}styles/themes/default.css">\n` +
`<link rel="stylesheet" href="${cdnUrl}styles/webawesome.css">\n` +
`<link rel="stylesheet" href="${cdnUrl}styles/utilities.css">\n\n` +
`${code.textContent}`;
const css = 'html > body {\n padding: 2rem !important;\n}';
const js = '';
const form = document.createElement('form');
form.action = 'https://codepen.io/pen/define';
form.method = 'POST';
form.target = '_blank';
const data = {
title: '',
description: '',
tags: ['webawesome'],
editors: '1000',
head: '<meta name="viewport" content="width=device-width">',
html_classes: '',
css_external: '',
js_external: '',
js_module: true,
js_pre_processor: 'none',
html,
css,
js,
};
const input = document.createElement('input');
input.type = 'hidden';
input.name = 'data';
input.value = JSON.stringify(data);
form.append(input);
document.documentElement.append(form);
form.submit();
form.remove();
}
});

View File

@@ -1,34 +1,5 @@
let initialPageLoadComplete = document.readyState === 'complete';
if (!initialPageLoadComplete) {
window.addEventListener('load', () => {
initialPageLoadComplete = true;
});
}
// Helper for view transitions
export function domChange(fn, { behavior = 'smooth', ignoreInitialLoad = true } = {}) {
const canUseViewTransitions =
document.startViewTransition && !window.matchMedia('(prefers-reduced-motion: reduce)').matches;
// Skip transitions on initial page load
if (!initialPageLoadComplete && ignoreInitialLoad) {
fn(false);
return null;
}
if (canUseViewTransitions && behavior === 'smooth') {
const transition = document.startViewTransition(() => {
fn(true);
// Wait a brief delay before finishing the transition to prevent jumpiness
return new Promise(resolve => setTimeout(resolve, 200));
});
return transition;
} else {
fn(false);
return null;
}
}
import { domChange } from './util/dom-change.js';
export { domChange };
export function nextFrame() {
return new Promise(resolve => requestAnimationFrame(resolve));
@@ -135,6 +106,6 @@ document.addEventListener('keydown', event => {
!event.composedPath().some(el => ['input', 'textarea'].includes(el?.tagName?.toLowerCase()))
) {
event.preventDefault();
colorScheme.set(theming.colorScheme.resolvedValue === 'dark' ? 'light' : 'dark');
colorScheme.set(colorScheme.get() === 'dark' ? 'light' : 'dark');
}
});

View File

@@ -0,0 +1,37 @@
let initialPageLoadComplete = document.readyState === 'complete';
if (!initialPageLoadComplete) {
window.addEventListener('load', () => {
initialPageLoadComplete = true;
});
}
/**
* Helper for performing a DOM change using a view transition, wherever supported and reduced motion is not desired.
* @param {function} fn - Function to perform the DOM change. If async, must resolve when the change is complete.
* @param {object} [options] - Options for the transition
* @param {'smooth' | 'instant'} [options.behavior] - Transition behavior. Defaults to 'smooth'. 'instant' will skip the transition.
* @param {boolean} [options.ignoreInitialLoad] - If true, will skip the transition on initial page load. Defaults to true.
*/
export function domChange(fn, { behavior = 'smooth', ignoreInitialLoad = true } = {}) {
const canUseViewTransitions =
document.startViewTransition && !window.matchMedia('(prefers-reduced-motion: reduce)').matches;
// Skip transitions on initial page load
if (!initialPageLoadComplete && ignoreInitialLoad) {
fn(false);
return null;
}
if (canUseViewTransitions && behavior === 'smooth') {
const transition = document.startViewTransition(() => {
fn(true);
// Wait a brief delay before finishing the transition to prevent jumpiness
return new Promise(resolve => setTimeout(resolve, 200));
});
return transition;
} else {
fn(false);
return null;
}
}

View File

@@ -1,87 +0,0 @@
.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);
color: var(--wa-color-text-normal);
margin-block-end: var(--wa-flow-spacing);
}
.code-example-preview {
padding: 2rem;
border-bottom: var(--wa-border-style) var(--wa-panel-border-width) var(--wa-color-neutral-border-quiet);
> :first-child {
margin-block-start: 0;
}
> :last-child {
margin-block-end: 0;
}
}
.code-example-source {
border-bottom: var(--wa-border-style) var(--wa-panel-border-width) var(--wa-color-neutral-border-quiet);
}
.code-example:not(.open) .code-example-source {
display: none;
}
.code-example.open .code-example-toggle wa-icon {
rotate: 180deg;
}
.code-example-source pre {
position: relative;
border-radius: 0;
margin: 0;
white-space: normal;
}
.code-example-source:not(:has(+ .code-example-buttons)) {
border-bottom: none;
pre {
border-bottom-right-radius: var(--wa-border-radius-l);
border-bottom-left-radius: var(--wa-border-radius-l);
}
}
.code-example-buttons {
display: flex;
align-items: stretch;
button {
all: unset;
flex: 1 0 auto;
font-size: 0.875rem;
color: var(--wa-color-text-quiet);
border-left: var(--wa-border-style) var(--wa-panel-border-width) var(--wa-color-neutral-border-quiet);
text-align: center;
padding: 0.5rem;
cursor: pointer;
&:first-of-type {
border-left: none;
border-bottom-left-radius: var(--wa-border-radius-l);
}
&:last-of-type {
border-bottom-right-radius: var(--wa-border-radius-l);
}
&:focus-visible {
outline: var(--wa-focus-ring);
}
}
.code-example-pen {
flex: 0 0 100px;
white-space: nowrap;
}
wa-icon {
width: 1em;
height: 1em;
vertical-align: -2px;
}
}

View File

@@ -1,4 +1,3 @@
@import 'code-examples.css';
@import 'code-highlighter.css';
@import 'copy-code.css';
@import 'outline.css';
@@ -608,13 +607,6 @@ table.colors {
margin-block-end: var(--wa-flow-spacing);
}
/** mobile */
@media screen and (max-width: 768px) {
wa-page .only-desktop {
display: none;
}
}
/** desktop */
@media screen and not (max-width: 768px) {
/* Navigation sidebar */

View File

@@ -167,7 +167,7 @@ Other elements can also be placed inside button groups:
<wa-button-group label="Example Button Group">
<wa-button>Button</wa-button>
<button>Native Button</button>
<wa-dropdown hoist>
<wa-dropdown>
<wa-button slot="trigger" caret>Dropdown</wa-button>
<wa-menu>
<wa-menu-item>Item 1</wa-menu-item>
@@ -185,7 +185,7 @@ 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-button>

View File

@@ -3,6 +3,7 @@ title: Code Demo
description: Code demos can be used to render code examples as inline live demos.
tags: component
noAlpha: true
isPro: true
---
```html {.example}

View File

@@ -1,14 +1,16 @@
---
title: Image Comparer
description: Compare visual differences between similar photos with a sliding panel.
title: Comparer
description: Compare visual differences between similar content with a sliding panel.
tags: [imagery, niche]
icon: image-comparer
icon: comparer
---
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.)
This is especially useful for comparing images, but can be used for comparing any type of content (for an example of using it to compare entire UIs, check out our [theme pages](/docs/themes/default/)).
For best results, use content that shares 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.)
```html {.example}
<wa-image-comparer>
<wa-comparer>
<img
slot="before"
src="https://images.unsplash.com/photo-1517331156700-3c241d2b4d83?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=800&q=80&sat=-100&bri=-5"
@@ -19,7 +21,7 @@ For best results, use images that share the same dimensions. The slider can be c
src="https://images.unsplash.com/photo-1517331156700-3c241d2b4d83?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=800&q=80"
alt="Color version of kittens in a basket looking around."
/>
</wa-image-comparer>
</wa-comparer>
```
## Examples
@@ -29,7 +31,7 @@ For best results, use images that share the same dimensions. The slider can be c
Use the `position` attribute to set the initial position of the slider. This is a percentage from `0` to `100`.
```html {.example}
<wa-image-comparer position="25">
<wa-comparer position="25">
<img
slot="before"
src="https://images.unsplash.com/photo-1520903074185-8eca362b3dce?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1200&q=80"
@@ -40,5 +42,5 @@ Use the `position` attribute to set the initial position of the slider. This is
src="https://images.unsplash.com/photo-1520640023173-50a135e35804?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2250&q=80"
alt="A person sitting on a yellow curb tying shoelaces on a boot."
/>
</wa-image-comparer>
</wa-comparer>
```

View File

@@ -88,10 +88,10 @@ You can add the special `data-dialog="close"` attribute to a button inside the d
### Custom Width
Just use the CSS `width` property to set the dialog's width.
Just use the `--width` custom property to set the dialog's width.
```html {.example}
<wa-dialog label="Dialog" with-header with-footer class="dialog-width" style="width: 50vw;">
<wa-dialog label="Dialog" with-header with-footer class="dialog-width" style="--width: 50vw;">
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
<wa-button slot="footer" variant="brand" data-dialog="close">Close</wa-button>
</wa-dialog>

View File

@@ -180,38 +180,3 @@ To create a submenu, nest an `<wa-menu slot="submenu">` element in a [menu item]
:::warning
As a UX best practice, avoid using more than one level of submenu when possible.
:::
### Hoisting
Dropdown panels will be clipped if they're inside a container that has `overflow: auto|hidden`. The `hoist` attribute forces the panel to use a fixed positioning strategy, allowing it to break out of the container. In this case, the panel will be positioned relative to its [containing block](https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#Identifying_the_containing_block), which is usually the viewport unless an ancestor uses a `transform`, `perspective`, or `filter`. [Refer to this page](https://developer.mozilla.org/en-US/docs/Web/CSS/position#fixed) for more details.
```html {.example}
<div class="dropdown-hoist">
<wa-dropdown>
<wa-button slot="trigger" caret>No Hoist</wa-button>
<wa-menu>
<wa-menu-item>Item 1</wa-menu-item>
<wa-menu-item>Item 2</wa-menu-item>
<wa-menu-item>Item 3</wa-menu-item>
</wa-menu>
</wa-dropdown>
<wa-dropdown hoist>
<wa-button slot="trigger" caret>Hoist</wa-button>
<wa-menu>
<wa-menu-item>Item 1</wa-menu-item>
<wa-menu-item>Item 2</wa-menu-item>
<wa-menu-item>Item 3</wa-menu-item>
</wa-menu>
</wa-dropdown>
</div>
<style>
.dropdown-hoist {
position: relative;
border: solid 2px var(--wa-color-surface-border);
padding: var(--wa-space-m);
overflow: hidden;
}
</style>
```

View File

@@ -17,11 +17,94 @@ Not sure which icon to use? [Find the perfect icon over at Font Awesome!](https:
The default icon library is Font Awesome Free, which comes with two icon families: `classic` and `brands`. Use the `family` attribute to set the icon family.
Many Font Awesome Pro icon families have variants such as `thin`, `light`, `regular`, and `solid`. Font Awesome Pro users can [provide their kit code](/docs/installation/#using-font-awesome-kit-codes) to unlock additional families, including `sharp` and `duotone`. For these icon families, use the `variant` attribute to set the variant.
Many Font Awesome Pro icon families have variants such as `thin`, `light`, `regular`, and `solid`. Font Awesome Pro users can [provide their kit code](/docs/#using-font-awesome-kit-codes) to unlock additional families, including `sharp`, `duotone`, and `sharp-duotone`. For these icon families, use the `variant` attribute to set the variant.
```html {.example}
<wa-icon family="brands" name="font-awesome"></wa-icon>
<wa-icon family="brands" name="web-awesome"></wa-icon>
<wa-icon family="brands" name="font-awesome"></wa-icon>
<wa-icon family="brands" name="web-awesome"></wa-icon>
<wa-icon family="classic" variant="light" name="sparkles"></wa-icon>
<wa-icon family="sharp" variant="solid" name="fire"></wa-icon>
<wa-icon family="duotone" variant="regular" name="cake-slice"></wa-icon>
```
### Setting defaults via CSS
You can use certain CSS custom properties to set icon defaults, not just on the icon itself, but any ancestor.
This can be useful when you want certain parameters to vary based on context, e.g. icons inside callouts or all icons for a given theme.
:::warning
These CSS properties are intended to set **defaults**, and thus only make a difference when the corresponding attributes are not set.
In future versions of Web Awesome, we may change this behavior to allow CSS properties to override attributes if `!important` is used.
:::
For example, here is how you can use CSS custom properties to set a default icon for each type of callout:
```html {.example}
<wa-callout>
<!-- Look ma, no attributes! -->
<wa-icon slot="icon"></wa-icon>
This is a normal callout.
</wa-callout>
<wa-callout variant="danger">
<wa-icon slot="icon" name="dumpster-fire" variant="solid"></wa-icon>
This is a callout with an explicit icon, which overrides these defaults.
</wa-callout>
<wa-callout variant="warning">
<!-- Look ma, no attributes! -->
<wa-icon slot="icon"></wa-icon>
Here be dragons.
</wa-callout>
<wa-callout variant="danger">
<!-- Look ma, no attributes! -->
<wa-icon slot="icon"></wa-icon>
Here be more dragons.
</wa-callout>
<wa-callout variant="success">
<!-- Look ma, no attributes! -->
<wa-icon slot="icon"></wa-icon>
Success!
</wa-callout>
<style>
wa-callout {
--wa-icon-variant: regular;
--wa-icon-name: info-circle;
&[variant="warning"] {
--wa-icon-name: triangle-exclamation;
}
&[variant="danger"] {
--wa-icon-name: circle-exclamation;
}
&[variant="success"] {
--wa-icon-name: circle-check;
}
}
</style>
```
You can even set icons dynamically, as a response to user interaction or media queries.
For example, here's how we can change the icon on hover:
```html {.example}
<wa-button class="github" href="https://github.com/webawesome/webawesome"><wa-icon slot="prefix" fixed-width></wa-icon> GitHub Repo</wa-button>
<style>
.github {
--wa-icon-name: github;
--wa-icon-family: brands;
&:hover {
--wa-icon-name: arrow-up-right-from-square;
--wa-icon-family: classic;
}
}
</style>
```
### Colors
@@ -103,7 +186,8 @@ Custom icons can be loaded individually with the `src` attribute. Only SVGs on a
You can register additional icons to use with the `<wa-icon>` component through icon libraries. Icon files can exist locally or on a CORS-enabled endpoint (e.g. a CDN). There is no limit to how many icon libraries you can register and there is no cost associated with registering them, as individual icons are only requested when they're used.
Web Awesome ships with two built-in icon libraries, `default` and `system`. The [default icon library](#customizing-the-default-library) is provided courtesy of [Font Awesome](https://fontawesome.com/). The [system icon library](#customizing-the-system-library) contains only a small subset of icons that are used internally by Web Awesome components.
Web Awesome ships with one built-in icon library: `default`.
The [default icon library](#customizing-the-default-library) is provided courtesy of [Font Awesome](https://fontawesome.com/).
To register an additional icon library, use the `registerIconLibrary()` function that's exported from `dist/webawesome.js`. At a minimum, you must provide a name and a resolver function. The resolver function translates an icon name to a URL where the corresponding SVG file exists. Refer to the examples below to better understand how it works.
@@ -115,8 +199,9 @@ Here's an example that registers an icon library located in the `/assets/icons`
<script type="module">
import { registerIconLibrary } from '/dist/webawesome.js';
registerIconLibrary('my-icons', {
resolver: (name, family, variant) => `/assets/icons/${name}.svg`,
registerIconLibrary({
name: 'my-icons',
getUrl: (name, family, variant) => `/assets/icons/${name}.svg`,
mutator: svg => svg.setAttribute('fill', 'currentColor')
});
</script>
@@ -131,6 +216,289 @@ To display an icon, set the `library` and `name` attributes of an `<wa-icon>` el
If an icon is used before registration occurs, it will be empty initially but shown when registered.
### Customizing the Default Icon Library
The default icon library contains over 2,000 icons courtesy of [Font Awesome](https://fontawesome.com/).
These are the icons that display when you use `<wa-icon>` without the `library` attribute.
If you prefer to have these icons resolve to a different icon library, simply register it using the `default` name:
For example, this will change the default icon library to use [Bootstrap Icons](https://icons.getbootstrap.com/) loaded from the jsDelivr CDN.
```html
<script type="module">
import { registerIconLibrary } from '/dist/webawesome.js';
registerIconLibrary('default', {
name: 'default',
getUrl: (name, family) => {
const suffix = family === 'filled' ? '-fill' : '';
return `https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/icons/${name}${suffix}.svg`
}
});
</script>
```
You can also register an existing library as the default:
```html
<script type="module">
import { registerIconLibrary } from '/dist/webawesome.js';
let myIcons = registerIconLibrary({
name: 'my-icons',
getUrl: (name, family, variant) => `/assets/icons/${name}.svg`,
mutator: svg => svg.setAttribute('fill', 'currentColor')
});
// Alias of my-icons to default
registerIconLibrary('default', myIcons);
</script>
```
This allows you to have multiple libraries that co-exist, only one of which is the default.
### Customizing system icons
Web Awesome components use a number of icons internally.
For example, the checkmark icon in `<wa-checkbox>` or the chevron used in `<wa-details>`.
These icons have a `system:` prefix in their name, e.g. `system:check` or `system:chevron-down`.
To specify how these map to your icon library, you can define a `system()` function that maps Web Awesomes system icons to your librarys icons.
The `system()` function receives the icon name, family and variant as arguments and returns an object with the new `name`, `family`, and `variant`.
For example, here is how to define system icon mappings for the Bootstrap Icons library:
```js
import { registerIconLibrary } from '/dist/webawesome.js';
let system = {
filled: ['circle', 'pause', 'play', 'star', 'user'],
nameMap: {
'check': 'check-lg',
'circle': 'circle',
'circle-xmark': 'x-circle',
'eye-dropper': 'eyedropper',
'eye': 'eye',
'eye-slash': 'eye-slash',
'grip-vertical': 'grip-vertical',
'indeterminate': 'dash-lg',
'minus': 'dash-lg',
'pause': 'pause',
'play': 'play',
'user': 'person',
'xmark': 'x-lg',
}
};
registerIconLibrary({
name: 'bootstrap-icons',
system(name, family) {
return {
// Transform names where different
name: system.nameMap[name] || name,
// Default to non-filled as many of system's "solid" icons are not filled in Bootstrap Icons
family: system.filled.includes(name) ? 'filled' : ''
};
},
getUrl: (name, family) => {
const suffix = family === 'filled' ? '-fill' : '';
return `https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/icons/${name}${suffix}.svg`
}
});
```
You can also specify a `library` key to resolve a system icon through a different library, e.g. WAs default icon library.
For example, the code above will try to resolve any system icons through your library and only fall back to WA's default library if they fail to load.
This maximizes the odds that icons from your library are used, but can also be unpredictable.
To resolve any system icon you have not vetted via the Web Awesome default library, you can use the `library` key:
```js
let system = {
filled: ['circle', 'pause', 'play', 'star', 'user'],
nameMap: {
'chevron-down': 'chevron-down',
'chevron-left': 'chevron-left',
'chevron-right': 'chevron-right',
'check': 'check-lg',
'circle': 'circle',
'eye-dropper': 'eyedropper',
'grip-vertical': 'grip-vertical',
'indeterminate': 'dash-lg',
'pause': 'pause',
'play': 'play',
'circle-xmark': 'x-circle',
'grip-vertical': 'grip-vertical',
'eye-slash': 'eye-slash',
'eye': 'eye',
'user': 'person',
'xmark': 'x-lg',
}
};
registerIconLibrary({
name: 'bootstrap-icons',
system(name, family) {
if (!system.nameMap[name]) {
// If the icon is not known, use the default library
return { library: 'wa', name, family, variant };
}
return {
name: system.nameMap[name],
family: system.filled.includes(name) ? 'filled' : ''
};
},
getUrl: (name, family) => {
const suffix = family === 'filled' ? '-fill' : '';
return `https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/icons/${name}${suffix}.svg`
}
});
```
#### Index of All System Icons { #system-icons-index }
These are all system icons used currently by Web Awesome components (`system:` prefix omitted for readability):
| Name | Variant | Icon |
| --- | --- | --- |
{%- for icon in systemIcons %}
| `{{ icon.name }}` | `{{ icon.variant }}` | <wa-icon library="wa" name="system:{{ icon.name }}" family="classic" variant="{{ icon.variant }}"></wa-icon> |
{%- endfor %}
### Inlined icons { #inlined }
Inlined icons are SVG icons that are loaded directly from code rather than from HTTP requests, making them load faster in your application.
Inlining critical icons improves performance by eliminating HTTP requests, reducing load times, and ensuring they are immediately available.
As an example, all of [Web Awesomes default system icons](#system-icons-index) are inlined, and we strongly advise you inline the corresponding icons in your custom icon libraries as well.
#### How to Use Inlined Icons
The `inlined` property allows you to provide icon SVG markup directly in your code.
This creates a mapping of `(name, family, variant)` to SVG markup.
```html
<script type="module">
import { registerIconLibrary } from '/dist/webawesome.js';
registerIconLibrary({
name: 'default',
getUrl: name => `/path/to/custom/icons/${name}.svg`,
inlined: {
check: '<svg xmlns="http://www.w3.org/2000/svg">...</svg>',
}
});
</script>
```
The structure of the `inlined` property is as follows:
- For simple libraries (no families or variants): `{ name: svg }`
- For libraries with families (no variants): `{ family: { name: svg } }`
- For complex libraries (with both family and variant): `{ family: { variant: { name: svg } } }`
#### Adding More Inlined Icons
To add additional icons to an existing library, use the `inline()` method:
```js
import { getIconLibrary } from '/dist/webawesome.js';
let defaultLibrary = getIconLibrary('default');
defaultLibrary.inline({
classic: { // family
regular: { // variant
'circle-info': '<svg xmlns="http://www.w3.org/2000/svg">...</svg>',
'triangle-exclamation': '<svg xmlns="http://www.w3.org/2000/svg">...</svg>'
// ...
}
}
});
```
#### Best Practice
When using custom icon libraries with Web Awesome components, always inline system icons to ensure optimal performance.
### Customize icon markup, for SVG sprites and more
By default, icon markup is produced by fetching the URL returned by the `getUrl()` function.
You can provide a `getMarkup()` function to customize this.
A common use case for that is SVG sprites, often used to improve performance by avoiding multiple trips for each SVG.
The browser will load the sprite sheet once and then you reference the particular SVG within the sprite sheet using its id:
```html
<script type="module">
import { registerIconLibrary } from '/dist/webawesome.js';
registerIconLibrary({
name: 'sprite',
getUrl: name => `/assets/images/sprite.svg#${name}`,
getMarkup: url => `<svg fill="currentColor"><use href="${url}"></use></svg>`
});
</script>
```
As always, make sure to benchmark these changes. When using HTTP/2, it may in fact be more bandwidth-friendly to use multiple small requests instead of 1 large sprite sheet.
:::warning
When using sprite sheets, the `wa-load` and `wa-error` events will not fire.
For security reasons, browsers may apply the same-origin policy on `<use>` elements located in the `<wa-icon>` shadow DOM and may refuse to load a cross-origin URL. There is currently no defined way to set a cross-origin policy for `<use>` elements. For this reason, sprite sheets should only be used if you're self-hosting them.
:::
### Fallbacks
By default, if an icon fails to load, WA will retry using the WA default icon library, and if that also fails, a blank icon will be displayed.
You can provide a `fallback()` function to customize this behavior.
Some examples for common use cases follow.
If you never want icons outside your library to display:
```js
registerIconLibrary({
name: 'my-icons',
getUrl: (name, family, variant) => `/assets/icons/${name}.svg`,
fallback: (name, family, variant) => {
// Don't show anything if an icon is not found
return null;
}
});
```
If you want to display a certain designated "missing icon" icon:
```js
registerIconLibrary({
name: 'my-icons',
getUrl: (name, family, variant) => `/assets/icons/${name}.svg`,
inlined: {
// Make sure the missing icon never fails by inlining it:
'missing-icon': '<svg xmlns="http://www.w3.org/2000/svg">...</svg>'
}
fallback: (name, family, variant) => {
return {name: 'missing-icon'};
}
});
```
If you want to retry with the default family and variant:
```js
registerIconLibrary({
name: 'my-icons',
getUrl: (name, family, variant) => `/assets/icons/${name}.svg`,
fallback: (name, family, variant) => {
if (family !== 'classic' || variant !== 'solid') {
return { name, family: 'classic', variant: 'solid' };
}
}
});
```
## Icon Library Examples { #icon-libraries }
The following examples demonstrate how to register a number of popular, open source icon libraries via CDN. Feel free to adapt the code as you see fit to use your own origin or naming conventions.
### Bootstrap Icons
@@ -143,8 +511,42 @@ Icons in this library are licensed under the [MIT License](https://github.com/tw
<script type="module">
import { registerIconLibrary } from '/dist/webawesome.js';
registerIconLibrary('default', {
resolver: (name, family) => {
let system = {
filled: ['circle', 'pause', 'play', 'star', 'user'],
nameMap: {
'chevron-down': 'chevron-down',
'chevron-left': 'chevron-left',
'chevron-right': 'chevron-right',
'check': 'check-lg',
'circle': 'circle',
'eye-dropper': 'eyedropper',
'grip-vertical': 'grip-vertical',
'indeterminate': 'dash-lg',
'pause': 'pause',
'play': 'play',
'circle-xmark': 'x-circle',
'grip-vertical': 'grip-vertical',
'eye-slash': 'eye-slash',
'eye': 'eye',
'user': 'person',
'xmark': 'x-lg',
}
};
registerIconLibrary({
name: 'default',
system(name, family) {
if (!system.nameMap[name]) {
// If the icon is not known, use the default library
return { library: 'wa', name: name, family, variant };
}
return {
name: system.nameMap[name],
family: system.filled.includes(name) ? 'filled' : ''
};
},
getUrl: (name, family) => {
const suffix = family === 'filled' ? '-fill' : '';
return `https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/icons/${name}${suffix}.svg`
}
@@ -162,8 +564,9 @@ Icons in this library are licensed under the [Creative Commons 4.0 License](http
<script type="module">
import { registerIconLibrary } from '/dist/webawesome.js';
registerIconLibrary('boxicons', {
resolver: name => {
registerIconLibrary({
name: 'boxicons',
getUrl: name => {
let folder = 'regular';
if (name.substring(0, 4) === 'bxs-') folder = 'solid';
if (name.substring(0, 4) === 'bxl-') folder = 'logos';
@@ -216,8 +619,9 @@ Icons in this library are licensed under the [MIT License](https://github.com/lu
<script type="module">
import { registerIconLibrary } from '/dist/webawesome.js';
registerIconLibrary('lucide', {
resolver: name => `https://cdn.jsdelivr.net/npm/lucide-static@0.16.29/icons/${name}.svg`
registerIconLibrary({
name: 'lucide',
getUrl: name => `https://cdn.jsdelivr.net/npm/lucide-static@0.16.29/icons/${name}.svg`
});
</script>
```
@@ -232,8 +636,9 @@ Icons in this library are licensed under the [MIT License](https://github.com/ta
<script type="module">
import { registerIconLibrary } from '/dist/webawesome.js';
registerIconLibrary('heroicons', {
resolver: name => `https://cdn.jsdelivr.net/npm/heroicons@2.0.1/24/outline/${name}.svg`
registerIconLibrary({
name: 'heroicons',
getUrl: name => `https://cdn.jsdelivr.net/npm/heroicons@2.0.1/24/outline/${name}.svg`
});
</script>
@@ -257,8 +662,9 @@ Icons in this library are licensed under the [MIT License](https://github.com/lu
<script type="module">
import { registerIconLibrary } from '/dist/webawesome.js';
registerIconLibrary('iconoir', {
resolver: name => `https://cdn.jsdelivr.net/gh/lucaburgio/iconoir@latest/icons/${name}.svg`
registerIconLibrary({
name: 'iconoir',
getUrl: name => `https://cdn.jsdelivr.net/gh/lucaburgio/iconoir@latest/icons/${name}.svg`
});
</script>
@@ -282,8 +688,9 @@ Icons in this library are licensed under the [MIT License](https://github.com/io
<script type="module">
import { registerIconLibrary } from '/dist/webawesome.js';
registerIconLibrary('ionicons', {
resolver: name => `https://cdn.jsdelivr.net/npm/ionicons@5.1.2/dist/ionicons/svg/${name}.svg`,
registerIconLibrary({
name: 'ionicons',
getUrl: name => `https://cdn.jsdelivr.net/npm/ionicons@5.1.2/dist/ionicons/svg/${name}.svg`,
mutator: svg => {
svg.setAttribute('fill', 'currentColor');
svg.setAttribute('stroke', 'currentColor');
@@ -327,8 +734,9 @@ Icons in this library are licensed under the [MIT License](https://github.com/mi
<script type="module">
import { registerIconLibrary } from '/dist/webawesome.js';
registerIconLibrary('jam', {
resolver: name => `https://cdn.jsdelivr.net/npm/jam-icons@2.0.0/svg/${name}.svg`,
registerIconLibrary({
name: 'jam',
getUrl: name => `https://cdn.jsdelivr.net/npm/jam-icons@2.0.0/svg/${name}.svg`,
mutator: svg => svg.setAttribute('fill', 'currentColor')
});
</script>
@@ -360,8 +768,9 @@ Icons in this library are licensed under the [Apache 2.0 License](https://github
<script type="module">
import { registerIconLibrary } from '/dist/webawesome.js';
registerIconLibrary('material', {
resolver: name => {
registerIconLibrary({
name: 'material',
getUrl: name => {
const match = name.match(/^(.*?)(_(round|sharp))?$/);
return `https://cdn.jsdelivr.net/npm/@material-icons/svg@1.0.5/svg/${match[1]}/${match[3] || 'outline'}.svg`;
},
@@ -403,8 +812,9 @@ Icons in this library are licensed under the [Apache 2.0 License](https://github
<script type="module">
import { registerIconLibrary } from '/dist/webawesome.js';
registerIconLibrary('remixicon', {
resolver: name => {
registerIconLibrary({
name: 'remixicon',
getUrl: name => {
const match = name.match(/^(.*?)\/(.*?)?$/);
match[1] = match[1].charAt(0).toUpperCase() + match[1].slice(1);
return `https://cdn.jsdelivr.net/npm/remixicon@2.5.0/icons/${match[1]}/${match[2]}.svg`;
@@ -440,8 +850,9 @@ Icons in this library are licensed under the [MIT License](https://github.com/ta
<script type="module">
import { registerIconLibrary } from '/dist/webawesome.js';
registerIconLibrary('tabler', {
resolver: name => `https://cdn.jsdelivr.net/npm/@tabler/icons@1.68.0/icons/${name}.svg`,
registerIconLibrary({
name: 'tabler',
getUrl: name => `https://cdn.jsdelivr.net/npm/@tabler/icons@1.68.0/icons/${name}.svg`,
mutator: svg => {
svg.style.fill = 'none';
svg.setAttribute('stroke', 'currentColor');
@@ -476,8 +887,9 @@ Icons in this library are licensed under the [Apache 2.0 License](https://github
<script type="module">
import { registerIconLibrary } from '/dist/webawesome.js';
registerIconLibrary('unicons', {
resolver: name => {
registerIconLibrary({
name: 'unicons',
getUrl: name => {
const match = name.match(/^(.*?)(-s)?$/);
return `https://cdn.jsdelivr.net/npm/@iconscout/unicons@3.0.3/svg/${match[2] === '-s' ? 'solid' : 'line'}/${
match[1]
@@ -504,61 +916,6 @@ Icons in this library are licensed under the [Apache 2.0 License](https://github
</div>
```
### Customizing the Default Library
The default icon library contains over 2,000 icons courtesy of [Font Awesome](https://fontawesome.com/). These are the icons that display when you use `<wa-icon>` without the `library` attribute. If you prefer to have these icons resolve elsewhere or to a different icon library, register an icon library using the `default` name and a custom resolver.
For example, this will change the default icon library to use [Bootstrap Icons](https://icons.getbootstrap.com/) loaded from the jsDelivr CDN.
```html
<script type="module">
import { registerIconLibrary } from '/dist/webawesome.js';
registerIconLibrary('default', {
resolver: (name, family) => {
const suffix = family === 'filled' ? '-fill' : '';
return `https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/icons/${name}${suffix}.svg`
}
});
</script>
```
#### Customize the default library to use SVG sprites
To improve performance you can use a SVG sprites to avoid multiple trips for each SVG. The browser will load the sprite sheet once and then you reference the particular SVG within the sprite sheet using hash selector.
As always, make sure to benchmark these changes. When using HTTP/2, it may in fact be more bandwidth-friendly to use multiple small requests instead of 1 large sprite sheet.
:::warning
When using sprite sheets, the `wa-load` and `wa-error` events will not fire.
For security reasons, browsers may apply the same-origin policy on `<use>` elements located in the `<wa-icon>` shadow DOM and may refuse to load a cross-origin URL. There is currently no defined way to set a cross-origin policy for `<use>` elements. For this reason, sprite sheets should only be used if you're self-hosting them.
:::
```html
<script type="module">
import { registerIconLibrary } from '/dist/webawesome.js';
registerIconLibrary('sprite', {
resolver: name => `/assets/images/sprite.svg#${name}`,
mutator: svg => svg.setAttribute('fill', 'currentColor'),
spriteSheet: true
});
</script>
```
### Customizing the System Library
The system library contains only the icons used internally by Web Awesome components. Unlike the default icon library, the system library does not rely on physical assets. Instead, its icons are hard-coded as data URIs into the resolver to ensure their availability.
If you want to change the icons Web Awesome uses internally, you can register an icon library using the `system` name and a custom resolver. If you choose to do this, it's your responsibility to provide all of the icons that are required by components. You can reference `src/components/library.system.ts` for a complete list of system icons used by Web Awesome.
```html
<script type="module">
import { registerIconLibrary } from '/dist/webawesome.js';
registerIconLibrary('system', {
resolver: name => `/path/to/custom/icons/${name}.svg`
});
</script>
```

View File

@@ -468,75 +468,20 @@ Use the `sync` attribute to make the popup the same width or height as the ancho
</script>
```
### Positioning Strategy
By default, the popup is positioned using an absolute positioning strategy. However, if your anchor is fixed or exists within a container that has `overflow: auto|hidden`, the popup risks being clipped. To work around this, you can use a fixed positioning strategy by setting the `strategy` attribute to `fixed`.
The fixed positioning strategy reduces jumpiness when the anchor is fixed and allows the popup to break out containers that clip. When using this strategy, it's important to note that the content will be positioned relative to its [containing block](https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#Identifying_the_containing_block), which is usually the viewport unless an ancestor uses a `transform`, `perspective`, or `filter`. [Refer to this page](https://developer.mozilla.org/en-US/docs/Web/CSS/position#fixed) for more details.
In this example, you can see how the popup breaks out of the overflow container when it's fixed. The fixed positioning strategy tends to be less performant than absolute, so avoid using it unnecessarily.
Toggle the switch and scroll the container to see the difference.
```html {.example}
<div class="popup-strategy">
<div class="overflow">
<wa-popup placement="top" strategy="fixed" active>
<span slot="anchor"></span>
<div class="box"></div>
</wa-popup>
</div>
<wa-switch checked>Fixed</wa-switch>
</div>
<style>
.popup-strategy .overflow {
position: relative;
height: 300px;
border: solid 2px var(--wa-color-surface-border);
overflow: auto;
}
.popup-strategy span[slot='anchor'] {
display: inline-block;
width: 150px;
height: 150px;
border: dashed 2px var(--wa-color-neutral-fill-loud);
margin: 150px 50px;
}
.popup-strategy .box {
width: 100px;
height: 50px;
background: var(--wa-color-brand-fill-loud);
border-radius: var(--wa-border-radius-m);
}
.popup-strategy wa-switch {
margin-top: 1rem;
}
</style>
<script>
const container = document.querySelector('.popup-strategy');
const popup = container.querySelector('wa-popup');
const fixed = container.querySelector('wa-switch');
fixed.addEventListener('change', () => (popup.strategy = fixed.checked ? 'fixed' : 'absolute'));
</script>
```
### Flip
When the popup doesn't have enough room in its preferred placement, it can automatically flip to keep it in view. To enable this, use the `flip` attribute. By default, the popup will flip to the opposite placement, but you can configure preferred fallback placements using `flip-fallback-placement` and `flip-fallback-strategy`. Additional options are available to control the flip behavior's boundary and padding.
When the popup doesn't have enough room in its preferred placement, it can automatically flip to keep it in view and visually connected to its anchor.
To enable this, use the `flip` attribute. By default, the popup will flip to the opposite placement, but you can configure preferred fallback placements using `flip-fallback-placement` and `flip-fallback-strategy`. Additional options are available to control the flip behavior's boundary and padding.
By default, flip takes effect when the popup would overflow the viewport.
You can use `boundary="scroll"` to make the popup resize when it overflows its nearest scrollable container instead.
Scroll the container to see how the popup flips to prevent clipping.
```html {.example}
<div class="popup-flip">
<div class="overflow">
<wa-popup placement="top" flip active>
<wa-popup placement="top" flip active boundary="scroll">
<span slot="anchor"></span>
<div class="box"></div>
</wa-popup>
@@ -592,7 +537,7 @@ Scroll the container to see how the popup changes it's fallback placement to pre
```html {.example}
<div class="popup-flip-fallbacks">
<div class="overflow">
<wa-popup placement="top" flip flip-fallback-placements="right bottom" flip-fallback-strategy="initial" active>
<wa-popup placement="top" flip flip-fallback-placements="right bottom" flip-fallback-strategy="initial" active boundary="scroll">
<span slot="anchor"></span>
<div class="box"></div>
</wa-popup>
@@ -626,14 +571,18 @@ Scroll the container to see how the popup changes it's fallback placement to pre
### Shift
When a popup is longer than its anchor, it risks being clipped by an overflowing container. In this case, use the `shift` attribute to shift the popup along its axis and back into view. You can customize the shift behavior using `shiftBoundary` and `shift-padding`.
When a popup is longer than its anchor, it risks overflowing.
In this case, use the `shift` attribute to shift the popup along its axis and back into view. You can customize the shift behavior using `shiftBoundary` and `shift-padding`.
By default, auto-size takes effect when the popup would overflow the viewport.
You can use `boundary="scroll"` to make the popup resize when it overflows its nearest scrollable container instead.
Toggle the switch to see the difference.
```html {.example}
<div class="popup-shift">
<div class="overflow">
<wa-popup placement="top" shift shift-padding="10" active>
<wa-popup placement="top" shift shift-padding="10" active boundary="scroll">
<span slot="anchor"></span>
<div class="box"></div>
</wa-popup>
@@ -676,7 +625,11 @@ Toggle the switch to see the difference.
### Auto-size
Use the `auto-size` attribute to tell the popup to resize when necessary to prevent it from getting clipped. Possible values are `horizontal`, `vertical`, and `both`. You can use `autoSizeBoundary` and `auto-size-padding` to customize the behavior of this option. Auto-size works well with `flip`, but if you're using `auto-size-padding` make sure `flip-padding` is the same value.
Use the `auto-size` attribute to tell the popup to resize when necessary to prevent it from overflowing.
Possible values are `horizontal`, `vertical`, and `both`. You can use `autoSizeBoundary` and `auto-size-padding` to customize the behavior of this option. Auto-size works well with `flip`, but if you're using `auto-size-padding` make sure `flip-padding` is the same value.
By default, auto-size takes effect when the popup would overflow the viewport.
You can use `boundary="scroll"` to make the popup resize when it overflows its nearest scrollable container instead.
When using `auto-size`, one or both of `--auto-size-available-width` and `--auto-size-available-height` will be applied to the host element. These values determine the available space the popover has before clipping will occur. Since they cascade, you can use them to set a max-width/height on your popup's content and easily control its overflow.
@@ -685,7 +638,7 @@ Scroll the container to see the popup resize as its available space changes.
```html {.example}
<div class="popup-auto-size">
<div class="overflow">
<wa-popup placement="top" auto-size="both" auto-size-padding="10" active>
<wa-popup placement="top" auto-size="both" auto-size-padding="10" active boundary="scroll">
<span slot="anchor"></span>
<div class="box"></div>
</wa-popup>

View File

@@ -425,11 +425,3 @@ This can be hard to conceptualize, so heres a fairly large example showing how l
</script>
```
<script type="module">
//
// TODO - remove once we switch to the Popover API
//
customElements.whenDefined('wa-select').then(() => {
document.querySelectorAll('wa-code-demo [slot="preview"] wa-select').forEach(select => select.hoist = true);
});
</script>

View File

@@ -129,7 +129,7 @@ Set a matching width and height to make a circle, square, or rounded avatar skel
<style>
.skeleton-avatars wa-skeleton {
display: inline-block;
display: inline-flex;
width: 3rem;
height: 3rem;
margin-right: 0.5rem;

View File

@@ -152,25 +152,3 @@ Use the `--max-width` custom property to change the width the tooltip can grow t
<wa-button id="wrapping-tooltip">Hover me</wa-button>
```
### Hoisting
Tooltips will be clipped if they're inside a container that has `overflow: auto|hidden|scroll`. The `hoist` attribute forces the tooltip to use a fixed positioning strategy, allowing it to break out of the container. In this case, the tooltip will be positioned relative to its [containing block](https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#Identifying_the_containing_block), which is usually the viewport unless an ancestor uses a `transform`, `perspective`, or `filter`. [Refer to this page](https://developer.mozilla.org/en-US/docs/Web/CSS/position#fixed) for more details.
```html {.example}
<div class="tooltip-hoist">
<wa-tooltip for="no-hoist">This is a tooltip</wa-tooltip>
<wa-button id="no-hoist">No Hoist</wa-button>
<wa-tooltip for="hoist" hoist>This is a tooltip</wa-tooltip>
<wa-button id="hoist">Hoist</wa-button>
</div>
<style>
.tooltip-hoist {
position: relative;
overflow: hidden;
border: solid 2px var(--wa-color-surface-border);
padding: var(--wa-space-m);
}
</style>
```

View File

@@ -3,6 +3,7 @@ title: Viewport Demo
description: Viewport demos can be used to display an iframe as a resizable, zoomable preview.
tags: component
noAlpha: true
isPro: true
---
```html {.example}

View File

@@ -10,7 +10,7 @@ You can customize the look and feel of Web Awesome at a high level with themes.
Web Awesome uses [themes](/docs/themes) to apply a cohesive look and feel across the entire library. Themes are built with a collection of predefined CSS custom properties, which we call [design tokens](/docs/tokens), and there are many premade themes you can choose from.
To use a theme, simply add a link to the theme's stylesheet to the `<head>` of your page. For example, you can replace the link to `default.css` in the [installation code](/docs/installation/#quick-start-autoloading-via-cdn) with this snippet to use the *Awesome* theme:
To use a theme, simply add a link to the theme's stylesheet to the `<head>` of your page. For example, you can replace the link to `default.css` in the [installation code](/docs/#quick-start-autoloading-via-cdn) with this snippet to use the *Awesome* theme:
```html
<link rel="stylesheet" href="{% cdnUrl 'styles/themes/awesome.css' %}" />
@@ -197,7 +197,7 @@ For example, we can give all outlined callouts a thick left border, regardless o
<style>
wa-callout:is(
[appearance~="outlined"],
[appearance~="outlined"],
.wa-outlined
) {
border-left-width: var(--wa-panel-border-radius);

View File

@@ -1412,7 +1412,8 @@ hasOutline: false
import { registerIconLibrary } from '/dist/webawesome.js';
// Ensure regular icons are always available for the knobs
registerIconLibrary('fa-classic-regular', {
registerIconLibrary({
name: 'fa-classic-regular',
resolver: name => `https://ka-f.fontawesome.com/releases/v6.5.1/svgs/regular/${name}.svg`
});
@@ -1453,36 +1454,47 @@ hasOutline: false
break;
case 'premium':
iconFamily.value = 'custom';
registerIconLibrary('default', {
registerIconLibrary({
name: 'default',
resolver: name => `/assets/icons/chunk/${name}.svg`,
mutator: svg => {[...svg.querySelectorAll('[fill="black"]')].map(el => el.setAttribute('fill', 'currentColor'));}
});
registerIconLibrary('system', {
registerIconLibrary({
name: 'system',
resolver: name => `/assets/icons/chunk/${name}.svg`,
mutator: svg => {[...svg.querySelectorAll('[fill="black"]')].map(el => el.setAttribute('fill', 'currentColor'));}
});
break;
case 'playful':
iconFamily.value = 'custom';
registerIconLibrary('default', {
registerIconLibrary({
name: 'default',
resolver: name => `/assets/icons/jelly/${name}.svg`,
mutator: svg => {[...svg.querySelectorAll('[fill="black"]')].map(el => el.setAttribute('fill', 'currentColor'));}
});
registerIconLibrary('system', {
registerIconLibrary({
name: 'system',
name: 'system',
name: 'system',
resolver: name => `/assets/icons/jelly/${name}.svg`,
mutator: svg => {[...svg.querySelectorAll('[fill="black"]')].map(el => el.setAttribute('fill', 'currentColor'));}
});
break;
case 'brutalist':
caregisterIconLibrary({
registerIconLibrary({
registerIconLibrary({
name: 'default',
iconFamily.value = 'custom';
registerIconLibrary('default', {
registerIconLibrary({
name: 'default',
resolver: name => `/assets/icons/utility/${name}.svg`,
mutator: svg => {
[...svg.querySelectorAll('[fill="black"]')].map(el => el.setAttribute('fill', 'currentColor'));
[...svg.querySelectorAll('[stroke="black"]')].map(el => el.setAttribute('stroke', 'currentColor'));
}
});
registerIconLibrary('system', {
registerIconLibrary({
name: 'system',
resolver: name => `/assets/icons/utility/${name}.svg`,
mutator: svg => {
[...svg.querySelectorAll('[fill="black"]')].map(el => el.setAttribute('fill', 'currentColor'));
@@ -1497,10 +1509,12 @@ hasOutline: false
break;
case 'classic':
iconFamily.value = 'custom';
registerIconLibrary('default', {
registerIconLibrary({
name: 'default',
resolver: name => `/assets/icons/bootstrap/${name}.svg`,
});
registerIconLibrary('system', {
registerIconLibrary({
name: 'system',
resolver: name => `/assets/icons/bootstrap/${name}.svg`,
});
break;
@@ -1531,7 +1545,8 @@ hasOutline: false
iconLibrary = 'sharp-solid';
}
// Ensures sharp-solid variations are available for ratings, etc.
registerIconLibrary('always-solid', {
registerIconLibrary({
name: 'always-solid',
resolver: name => `https://ka-f.fontawesome.com/releases/v6.5.1/svgs/sharp-solid/${name}.svg`
});
solidifyRatingStars();
@@ -1557,15 +1572,18 @@ hasOutline: false
iconLibrary = 'solid';
}
// Ensures solid variations are available for radios, ratings, etc.
registerIconLibrary('always-solid', {
registerIconLibrary({
name: 'always-solid',
resolver: name => `https://ka-f.fontawesome.com/releases/v6.5.1/svgs/solid/${name}.svg`
});
solidifyRatingStars();
}
registerIconLibrary('default', {
registerIconLibrary({
name: 'default',
resolver: name => `https://ka-f.fontawesome.com/releases/v6.5.1/svgs/${iconLibrary}/${name}.svg`
});
registerIconLibrary('system', {
registerIconLibrary({
name: 'system',
resolver: name => `https://ka-f.fontawesome.com/releases/v6.5.1/svgs/${iconLibrary}/${name}.svg`
});
};

View File

@@ -10,7 +10,7 @@ override:tags: []
{% markdown %}
## Installation
Layout components are included in Web Awesome's [autoloader](/docs/installation/#quick-start-autoloading-via-cdn). You can also import them individually via [cherry picking](/docs/installation/#cherry-picking).
Layout components are included in Web Awesome's [autoloader](/docs/#quick-start-autoloading-via-cdn). You can also import them individually via [cherry picking](/docs/#cherry-picking).
Layout utilities are bundled with all [style utilities](/docs/utilities). You can import all Web Awesome page styles (including [native styles](/docs/native/)) by including the following stylesheet in your project:

View File

@@ -11,8 +11,8 @@ file: styles/native/radio.css
```html {.example}
<div class="wa-cluster">
<label><input type="radio" name="radio" value="1"> Option 1</label>
<label><input type="radio" name="radio" value="2"> Option 2</label>
<label><input type="radio" name="a" value="1" checked> Option 1</label>
<label><input type="radio" name="a" value="2"> Option 2</label>
</div>
```
@@ -24,9 +24,9 @@ To set the initial value and checked state, use the `checked` attribute on the c
```html {.example}
<div class="wa-cluster">
<label><input type="radio" name="radio" value="1"> Option 1</label>
<label><input type="radio" name="radio" value="2"> Option 2</label>
<label><input type="radio" name="radio" value="3" checked> Option 3</label>
<label><input type="radio" name="b" value="1" checked> Option 1</label>
<label><input type="radio" name="b" value="2"> Option 2</label>
<label><input type="radio" name="b" value="3"> Option 3</label>
</div>
```
@@ -36,9 +36,9 @@ Use the `disabled` attribute to disable a radio.
```html {.example}
<div class="wa-cluster">
<label><input type="radio" name="radio" value="1"> Option 1</label>
<label><input type="radio" name="radio" value="2" disabled> Option 2</label>
<label><input type="radio" name="radio" value="3"> Option 3</label>
<label><input type="radio" name="c" value="1" checked> Option 1</label>
<label><input type="radio" name="c" value="2" disabled> Option 2</label>
<label><input type="radio" name="c" value="3"> Option 3</label>
</div>
```
@@ -49,26 +49,26 @@ Use the [size utilities](/docs/utilities/size) to change the radios' size.
```html {.example}
<fieldset class="wa-size-s wa-cluster">
<legend>Small</legend>
<label><input type="radio" name="radio" value="1"> Option 1</label>
<label><input type="radio" name="radio" value="2"> Option 2</label>
<label><input type="radio" name="radio" value="3"> Option 3</label>
<label><input type="radio" name="d" value="1" checked> Option 1</label>
<label><input type="radio" name="d" value="2"> Option 2</label>
<label><input type="radio" name="d" value="3"> Option 3</label>
</fieldset>
<br />
<fieldset class="wa-size-m wa-cluster">
<legend>Medium</legend>
<label><input type="radio" name="radio" value="1"> Option 1</label>
<label><input type="radio" name="radio" value="2"> Option 2</label>
<label><input type="radio" name="radio" value="3"> Option 3</label>
<label><input type="radio" name="e" value="1" checked> Option 1</label>
<label><input type="radio" name="e" value="2"> Option 2</label>
<label><input type="radio" name="e" value="3"> Option 3</label>
</fieldset>
<br />
<fieldset class="wa-size-l wa-cluster">
<legend>Large</legend>
<label><input type="radio" name="radio" value="1"> Option 1</label>
<label><input type="radio" name="radio" value="2"> Option 2</label>
<label><input type="radio" name="radio" value="3"> Option 3</label>
<label><input type="radio" name="f" value="1" checked> Option 1</label>
<label><input type="radio" name="f" value="2"> Option 2</label>
<label><input type="radio" name="f" value="3"> Option 3</label>
</fieldset>
```
@@ -78,16 +78,16 @@ You can wrap native radios in a flex container to give them a horizontal or vert
```html {.example}
<div class="wa-cluster">
<label><input type="radio" name="radio" value="1"> Option 1</label>
<label><input type="radio" name="radio" value="2"> Option 2</label>
<label><input type="radio" name="radio" value="3" checked> Option 3</label>
<label><input type="radio" name="g" value="1" checked> Option 1</label>
<label><input type="radio" name="g" value="2"> Option 2</label>
<label><input type="radio" name="g" value="3"> Option 3</label>
</div>
```
```html {.example}
<div class="wa-stack">
<label><input type="radio" name="radio" value="1"> Option 1</label>
<label><input type="radio" name="radio" value="2"> Option 2</label>
<label><input type="radio" name="radio" value="3" checked> Option 3</label>
<label><input type="radio" name="h" value="1" checked> Option 1</label>
<label><input type="radio" name="h" value="2"> Option 2</label>
<label><input type="radio" name="h" value="3"> Option 3</label>
</div>
```

View File

@@ -2,6 +2,7 @@
title: Action Panel
description: 'Help users complete tasks efficiently with quick access to key actions.'
icon: action-panel
isPro: true
---
## Simple

View File

@@ -1,6 +1,7 @@
---
title: Activity Log
description: 'Track and organize recent user actions or events.'
---
## Simple
@@ -260,7 +261,7 @@ description: 'Track and organize recent user actions or events.'
</li>
</ul>
</div>
</div>
</div>
```

View File

@@ -1,6 +1,7 @@
---
title: Comments
description: 'Enable users to engage in discussions, provide feedback, or record their thoughts.'
isPro: true
---
## Card with Header & Footer
@@ -91,7 +92,7 @@ description: 'Enable users to engage in discussions, provide feedback, or record
<wa-icon-button name="face-smile" label="Add Sticker" id="sticker-button"></wa-icon-button>
<wa-tooltip for="sticker-button">Add Sticker</wa-tooltip>
</div>
<wa-button variant="brand">Comment</wa-button>
<wa-button variant="brand">Comment</wa-button>
</div>
</div>
</div>
@@ -107,9 +108,9 @@ description: 'Enable users to engage in discussions, provide feedback, or record
<div class="wa-stack">
<div class="wa-flank" style="--flank-size: 3rem">
<div class="wa-frame:portrait wa-border-radius-s">
<img
src="https://images.unsplash.com/photo-1607675742178-f616ae75044b?q=80&w=3435&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
alt="the cover image for the film"
<img
src="https://images.unsplash.com/photo-1607675742178-f616ae75044b?q=80&w=3435&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
alt="the cover image for the film"
/>
</div>
<span class="wa-heading-l">Heretic</span>

View File

@@ -1,10 +1,11 @@
---
title: Data Display
description: 'Convey insights, metrics, and aggregate data at a glance.'
isPro: true
---
## Simple
## Simple
```html {.example}
<wa-card>
<div class="wa-grid wa-gap-3xl" style="--min-column-size: 24ch;">
@@ -170,4 +171,4 @@ description: 'Convey insights, metrics, and aggregate data at a glance.'
</article>
</div>
</wa-card>
```
```

View File

@@ -1,6 +1,7 @@
---
title: Description List
description: 'Help users digest detailed information in a structured, easy-to-scan format.'
isPro: true
---
## Left Aligned
@@ -249,4 +250,4 @@ description: 'Help users digest detailed information in a structured, easy-to-sc
</a>
</div>
</wa-card>
```
```

View File

@@ -1,6 +1,7 @@
---
title: Empty State
description: 'Guide users with helpful prompts and visuals when no content is available.'
isPro: true
---
## Simple
@@ -125,7 +126,7 @@ description: 'Guide users with helpful prompts and visuals when no content is av
</div>
</wa-card>
```
## Add people
## Add people
```html {.example}
<wa-card style="max-width: 60ch; margin: 0 auto;">
<div class="wa-stack wa-gap-xs">
@@ -146,7 +147,7 @@ description: 'Guide users with helpful prompts and visuals when no content is av
<span class="wa-caption-m">DevOps</span>
</div>
<wa-button appearance="plain">
<wa-icon name="user-plus" slot="prefix"></wa-icon>
<wa-icon name="user-plus" slot="prefix"></wa-icon>
Invite
</wa-button>
</div>
@@ -162,7 +163,7 @@ description: 'Guide users with helpful prompts and visuals when no content is av
<span class="wa-caption-m">Captain</span>
</div>
<wa-button appearance="plain">
<wa-icon name="user-plus" slot="prefix"></wa-icon>
<wa-icon name="user-plus" slot="prefix"></wa-icon>
Invite
</wa-button>
</div>
@@ -178,7 +179,7 @@ description: 'Guide users with helpful prompts and visuals when no content is av
<span class="wa-caption-m">Cloud Engineer</span>
</div>
<wa-button appearance="plain">
<wa-icon name="user-plus" slot="prefix"></wa-icon>
<wa-icon name="user-plus" slot="prefix"></wa-icon>
Invite
</wa-button>
</div>
@@ -193,7 +194,7 @@ description: 'Guide users with helpful prompts and visuals when no content is av
<span class="wa-caption-m">UX Writer</span>
</div>
<wa-button appearance="plain">
<wa-icon name="user-plus" slot="prefix"></wa-icon>
<wa-icon name="user-plus" slot="prefix"></wa-icon>
Invite
</wa-button>
</div>

View File

@@ -1,6 +1,7 @@
---
title: FAQ
description: 'Empower users to learn more with a structured list of questions and answers.'
isPro: true
---
## With Flanked Heading & Description
@@ -86,7 +87,7 @@ description: 'Empower users to learn more with a structured list of questions an
<dt class="wa-heading-m">How often do you update your courses?</dt>
<dd>A course is updated once there is a fundamental shift in the language or librarys underlying API. You can check our <a href="#">workshop</a> list to see if a new version of a given course is on the schedule. You may also write to us as <a href="#">support@frontendmasters.com</a> with suggestions for updates.</dd>
</div>
<div class="wa-stack wa-gap-xs">
<dt class="wa-heading-m">Do you offer certificates of completion?</dt>
<dd>You can download certificates of completion from the <a href="#">Completed Courses</a> list in your Learning Library. Click the diploma icon next to the course to download the certificate in light or dark mode. A link to your Public Profile is included on each certificate if youve created one. Public Profiles showcase your learning journey and are a fantastic way to share progress with friends, co-workers, or employers. Public Profiles are available to members with an active Frontend Masters subscription who have watched ten or more hours of content. Visit the <a href="#">Public Profile</a> section in My Account to get started.</dd>
@@ -103,16 +104,16 @@ description: 'Empower users to learn more with a structured list of questions an
</ul>
</dd>
</div>
<div class="wa-stack wa-gap-xs">
<dt class="wa-heading-m">Do you have discounts for students?</dt>
<dd>We are part of the <a href="#">GitHub Student Developer Pack</a>, allowing students six months of free access to the entire platform.</dd>
</div>
<div class="wa-stack wa-gap-xs">
<dt class="wa-heading-m">How do I cancel my plan?</dt>
<dd>You can cancel your Frontend Masters subscription by visiting the <a href="#">Subscription tab</a> in your My Account area.</dd>
</div>
</dl>
</div>
```
```

View File

@@ -1,6 +1,7 @@
---
title: Grid List
description: 'Improve browsing and selection by organizing data in a structured grid layout.'
isPro: true
---
## Cards with Footer Actions
@@ -397,8 +398,8 @@ description: 'Improve browsing and selection by organizing data in a structured
<div class="wa-stack">
<div class="wa-cluster wa-gap-s"><span>Draft</span> <wa-badge appearance="filled outlined" variant="neutral">1</wa-badge></div>
<wa-card>
<div class="wa-flank:end">
<div class="wa-stack wa-gap-2xs">
@@ -426,12 +427,12 @@ description: 'Improve browsing and selection by organizing data in a structured
<wa-icon name="plus"></wa-icon>
Add Task
</wa-button>
</div>
<div class="wa-stack">
<div class="wa-cluster wa-gap-s"><span>In Progress</span> <wa-badge appearance="filled outlined" variant="neutral">2</wa-badge></div>
<wa-card>
<div class="wa-flank:end">
<div class="wa-stack wa-gap-2xs">
@@ -481,7 +482,7 @@ description: 'Improve browsing and selection by organizing data in a structured
<wa-icon name="plus"></wa-icon>
Add Task
</wa-button>
</div>
<div class="wa-stack">
@@ -508,18 +509,17 @@ description: 'Improve browsing and selection by organizing data in a structured
<wa-avatar initials="KK" label="Avatar with initials: KK"></wa-avatar>
</div>
</wa-card>
<wa-button appearance="plain">
<wa-icon name="plus"></wa-icon>
Add Task
</wa-button>
</div>
</div>
</div>
```

View File

@@ -5,4 +5,5 @@ parent: patterns
layout: overview
override:tags: []
listChildren: true
isPro: false
---

View File

@@ -1,6 +1,7 @@
---
title: Leaderboard
description: 'Engage and motivate users by highlighting top performers, scores, and achievements.'
isPro: true
---
## Simple

View File

@@ -1,6 +1,7 @@
---
title: Pagination
description: 'Improve navigation and performance by breaking long lists or content into manageable pages.'
isPro: true
---
## Simple

View File

@@ -1,6 +1,7 @@
---
title: Permissions
description: 'Permission patterns provide or restrict access to users.'
isPro: true
---
## With Form Inputs
@@ -118,7 +119,7 @@ description: 'Permission patterns provide or restrict access to users.'
<wa-button size="small" variant="brand" pill>Generate</wa-button>
</div>
</div>
</wa-card>
```
@@ -170,7 +171,7 @@ description: 'Permission patterns provide or restrict access to users.'
<wa-option value="moderator">Moderator</wa-option>
<wa-option value="contributor">Contributor</wa-option>
<wa-option value="reader">Reader</wa-option>
</wa-select>
</wa-select>
</td>
</tr>
<tr>
@@ -222,7 +223,7 @@ description: 'Permission patterns provide or restrict access to users.'
</tbody>
</table>
</wa-tab-panel>
</wa-tab-group>
</div>
```

View File

@@ -1,6 +1,7 @@
---
title: Pricing
description: 'Help users make informed purchasing decisions with clear, structured pricing.'
isPro: true
---
## Three Tiers
@@ -164,4 +165,4 @@ description: 'Help users make informed purchasing decisions with clear, structur
</wa-callout>
</div>
</wa-card>
```
```

View File

@@ -2,6 +2,7 @@
title: Blog
description: TODO
unlisted: true
isPro: true
---
TODO Page Description
@@ -367,4 +368,4 @@ TODO Page Description
}
</style>
```
```

View File

@@ -2,8 +2,9 @@
title: Business
description: TODO
unlisted: true
isPro: true
---
TODO Page Description
## Examples
## Examples

View File

@@ -2,6 +2,7 @@
title: Category Filter
description: 'Helps the user find the right products with filters to refine search results by specific attributes.'
icon: checkbox
isPro: true
---
## Sidebar with Checkboxes & Expandable Filters
@@ -80,4 +81,4 @@ icon: checkbox
</div>
<div class="wa-placeholder"></div>
</div>
```
```

View File

@@ -2,6 +2,7 @@
title: Category Preview
description: 'Help shoppers discover your product offerings with showcases of product categories.'
icon: preview
isPro: true
---
## Split with Image Grid
@@ -159,4 +160,4 @@ icon: preview
</div>
</div>
</div>
```
```

View File

@@ -1,6 +1,7 @@
---
title: Checkout Form
description: 'Let shoppers checkout with ease with streamlined forms to capture shipping and payment info.'
isPro: true
---
@@ -241,4 +242,4 @@ description: 'Let shoppers checkout with ease with streamlined forms to capture
</div>
</div>
</div>
```
```

View File

@@ -1,6 +1,7 @@
---
title: Incentives
description: 'Encourage shoppers to buy your products with value propositions, discounts, and promotions.'
isPro: true
---
## 3 Column
@@ -120,4 +121,4 @@ description: 'Encourage shoppers to buy your products with value propositions, d
</div>
</div>
</div>
```
```

View File

@@ -4,4 +4,5 @@ description: Pre-built product overviews, shopping carts, and more to help you b
parent: patterns
layout: overview
override:tags: []
isPro: false
---

View File

@@ -1,6 +1,7 @@
---
title: Order History
description: 'Empower your customers to view past purchases and track upcoming orders with comprehensive order histories.'
isPro: true
---
## List

View File

@@ -1,6 +1,7 @@
---
title: Order Summary
description: 'Give shoppers confidence in their purchases with summaries of everything included in their order.'
isPro: true
---
## Simple
@@ -291,4 +292,4 @@ description: 'Give shoppers confidence in their purchases with summaries of ever
</div>
</wa-callout>
</div>
```
```

View File

@@ -1,6 +1,7 @@
---
title: Product Lists
description: 'Let shoppers browse and compare products with detailed lists of the products in your store.'
isPro: true
---
## Simple Grid with Ratings
@@ -179,4 +180,4 @@ description: 'Let shoppers browse and compare products with detailed lists of th
</a>
</div>
</div>
```
```

View File

@@ -1,6 +1,7 @@
---
title: Product Overview
description: 'Showcase your products with overviews including images, ratings, features, options, and more.'
isPro: true
---
## Split with Image
@@ -426,4 +427,4 @@ description: 'Showcase your products with overviews including images, ratings, f
</ul>
</div>
</div>
```
```

View File

@@ -2,6 +2,7 @@
title: Product Preview
description: 'Give shoppers a quick look at your products as they browse with modal previews.'
icon: preview
isPro: true
---
## With Product Options
@@ -157,4 +158,4 @@ icon: preview
</div>
</div>
</wa-card>
```
```

View File

@@ -1,6 +1,7 @@
---
title: Product Reviews
description: 'Help shoppers make informed decisions with ratings, reviews, and testimonials from your customers.'
isPro: true
---
## Multi column
@@ -208,4 +209,4 @@ description: 'Help shoppers make informed decisions with ratings, reviews, and t
</div>
<wa-divider></wa-divider>
</div>
```
```

View File

@@ -1,6 +1,7 @@
---
title: Shopping Cart
description: 'Give shoppers an overview of selected items with shopping carts that let them edit items and proceed to checkout.'
isPro: true
---
## Two Columns with Summary Card
@@ -274,4 +275,4 @@ description: 'Give shoppers an overview of selected items with shopping carts th
</wa-button>
</div>
</wa-drawer>
```
```

View File

@@ -2,6 +2,7 @@
title: Store Navigation
description: 'Help shoppers explore categories and find products with all of the links they need to navigate your store.'
unlisted: true
isPro: true
---
## Popup Menu
@@ -81,4 +82,4 @@ unlisted: true
</style>
```
```

View File

@@ -2,8 +2,9 @@
title: Business
description: TODO
unlisted: true
isPro: true
---
TODO Page Description
## Examples
## Examples

View File

@@ -2,8 +2,9 @@
title: Entertainment
description: TODO
unlisted: true
isPro: true
---
TODO Page Description
## Examples
## Examples

View File

@@ -3,6 +3,7 @@ title: Patterns
description: Patterns are reusable UI solutions to common design problems, ready to copy and paste into any project.
layout: overview
override:tags: []
isPro: false
---
<div class="max-line-length">
@@ -21,7 +22,7 @@ Patterns are written as standard HTML, so you can use them just as you would any
To use a pattern in your project, refer to each pattern's docs for a copyable code snippet. Paste the snippet wherever you'd like the pattern to appear in your project.
Because patterns use a combination of Web Awesome features, they work best when you have [native styles](/docs/native), [style utilities](/docs/utilities), and a [theme](/docs/themes) installed in addition to Web Awesome [components](/docs/components). Refer to the [Installation page](/docs/installation) to set up all of these features in your project.
Because patterns use a combination of Web Awesome features, they work best when you have [native styles](/docs/native), [style utilities](/docs/utilities), and a [theme](/docs/themes) installed in addition to Web Awesome [components](/docs/components). Refer to the [Installation page](/docs/) to set up all of these features in your project.
{% endmarkdown %}
</div>

View File

@@ -2,8 +2,9 @@
title: Membership
description: TODO
unlisted: true
isPro: true
---
TODO Page Description
## Examples
## Examples

View File

@@ -2,6 +2,7 @@
title: News
description: TODO
unlisted: true
isPro: true
---
TODO Page Description
@@ -157,4 +158,4 @@ TODO Page Description
}
}
</style>
```
```

View File

@@ -2,8 +2,9 @@
title: Non-profit
description: TODO
unlisted: true
isPro: true
---
TODO Page Description
## Examples
## Examples

View File

@@ -2,5 +2,6 @@
"layout": "patterns.njk",
"tags": ["patterns"],
"wide": true,
"noAlpha": true
"noAlpha": true,
"isPro": true
}

View File

@@ -2,8 +2,9 @@
title: Portfolio
description: TODO
unlisted: true
isPro: true
---
TODO Page Description
## Examples
## Examples

View File

@@ -2,8 +2,9 @@
title: Product Landing
description: TODO
unlisted: true
isPro: true
---
TODO Page Description
## Examples
## Examples

View File

@@ -12,6 +12,25 @@ Components with the <wa-badge variant="warning" pill>Experimental</wa-badge> bad
During the alpha period, things might break! We take breaking changes very seriously, but sometimes they're necessary to make the final product that much better. We appreciate your patience!
:::
## Next
- All icon libraries can now declare **pre-fetched** icons and skip the HTTP request.
When these icons are used, the pre-fetched version is automatically used, with no additional opt-in.
- 🚨 BREAKING: No more `system` library, just use `default`.
The improvement above allowed us to fold Web Awesomes own `system` icon library into the `default` library
so the performance benefits can be automatic and shared across all uses of the `default` library,
rather then requiring a conscious decision to use a different library.
This also makes WA components play better with different icon libraries and different default families and variants.
- Fixed a bug that caused an undesired margin below radio groups
- 🚨 BREAKING: Renamed `<image-comparer>` to `<wa-comparer>` and improved compatibility for non-image content
- Added support for Duotone Thin, Light, and Regular styles and the Sharp Duotone family of styles to `<wa-icon>`
- Fixed a bug that caused `<wa-radio-group>` to have an undesired margin below it
- Fixed a bug in `<wa-select>` that caused incorrect spacing of icons
- Fixed a bug in the Matter theme that prevented clicks on form control labels to not focus the control
- Fixed a bug in `<wa-radio-group>` that prevented radio buttons from validating
- Improved native radio alignment
- Improved the `.wa-cloak` utility class so all FOUCE-related solutions are 100% opt-in
## 3.0.0-alpha.12
### Enhancements

View File

@@ -31,7 +31,7 @@ The [issue tracker](https://github.com/shoelace-style/webawesome-alpha/issues) i
- Please **do not** use the issue tracker for feature requests. Use [the discussion forum](https://github.com/shoelace-style/webawesome-alpha/discussions/categories/ideas) instead.
- Please **do not** derail, hijack, or troll issues. Keep the discussion on topic and be respectful of others.
- Please **do not** post comments with "+1" or "👍". Use [reactions](https://github.blog/2016-03-10-add-reactions-to-pull-requests-issues-and-comments/) instead.
- Please **do** use the issue tracker for feature requests, bug reports, and pull requests.
- Please **do** use the issue tracker for bug reports and pull requests.
Issues that do not follow these guidelines are subject to closure. There simply aren't enough resources for the author and contributors to troubleshoot personal support requests.
@@ -352,13 +352,11 @@ Form controls should support submission and validation through the following con
### System Icons
Avoid inlining SVG icons inside of templates. If a component requires an icon, make sure `<wa-icon>` is a dependency of the component and use the [system library](/components/icon#customizing-the-system-library):
Avoid inlining SVG icons inside of templates.
If a component requires an icon, make sure `<wa-icon>` is a dependency of the component.
```html
<wa-icon library="system" name="..." variant="..."></wa-icon>
```
This will render the icons instantly whereas the default library will fetch them from a remote source. If an icon isn't available in the system library, you will need to add it to `library.system.ts`. Using the system library ensures that all icons load instantly and are customizable by users who wish to provide a custom resolver for the system library.
If it is not one of the [pre-fetched icons](/components/icon#fetched) in the `default` library,
you should add it. This will render the icons instantly rather than fetching them from a remote source.
### Writing tests

View File

@@ -0,0 +1,71 @@
---
title: CSS Properties Benchmark
unlisted: true
wide: true
---
{% set icons = {
check: '<svg xmlns="http://www.w3.org/2000/svg" height="16" width="14" viewBox="0 0 448 512"><path d="M438.6 105.4c12.5 12.5 12.5 32.8 0 45.3l-256 256c-12.5 12.5-32.8 12.5-45.3 0l-128-128c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0L160 338.7 393.4 105.4c12.5-12.5 32.8-12.5 45.3 0z"/></svg>',
'chevron-down': '<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16" viewBox="0 0 512 512"><path d="M233.4 406.6c12.5 12.5 32.8 12.5 45.3 0l192-192c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L256 338.7 86.6 169.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3l192 192z"/></svg>',
'chevron-left': '<svg xmlns="http://www.w3.org/2000/svg" height="16" width="10" viewBox="0 0 320 512"><path d="M9.4 233.4c-12.5 12.5-12.5 32.8 0 45.3l192 192c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L77.3 256 246.6 86.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0l-192 192z"/></svg>',
'chevron-right': '<svg xmlns="http://www.w3.org/2000/svg" height="16" width="10" viewBox="0 0 320 512"><path d="M310.6 233.4c12.5 12.5 12.5 32.8 0 45.3l-192 192c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3L242.7 256 73.4 86.6c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0l192 192z"/></svg>',
circle: '<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16" viewBox="0 0 512 512"><path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512z"/></svg>',
'eye-dropper': '<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16" viewBox="0 0 512 512"><path d="M341.6 29.2L240.1 130.8l-9.4-9.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3l160 160c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3l-9.4-9.4L482.8 170.4c39-39 39-102.2 0-141.1s-102.2-39-141.1 0zM55.4 323.3c-15 15-23.4 35.4-23.4 56.6v42.4L5.4 462.2c-8.5 12.7-6.8 29.6 4 40.4s27.7 12.5 40.4 4L89.7 480h42.4c21.2 0 41.6-8.4 56.6-23.4L309.4 335.9l-45.3-45.3L143.4 411.3c-3 3-7.1 4.7-11.3 4.7H96V379.9c0-4.2 1.7-8.3 4.7-11.3L221.4 247.9l-45.3-45.3L55.4 323.3z"/></svg>',
'grip-vertical': '<svg xmlns="http://www.w3.org/2000/svg" height="16" width="10" viewBox="0 0 320 512"><path d="M40 352l48 0c22.1 0 40 17.9 40 40l0 48c0 22.1-17.9 40-40 40l-48 0c-22.1 0-40-17.9-40-40l0-48c0-22.1 17.9-40 40-40zm192 0l48 0c22.1 0 40 17.9 40 40l0 48c0 22.1-17.9 40-40 40l-48 0c-22.1 0-40-17.9-40-40l0-48c0-22.1 17.9-40 40-40zM40 320c-22.1 0-40-17.9-40-40l0-48c0-22.1 17.9-40 40-40l48 0c22.1 0 40 17.9 40 40l0 48c0 22.1-17.9 40-40 40l-48 0zM232 192l48 0c22.1 0 40 17.9 40 40l0 48c0 22.1-17.9 40-40 40l-48 0c-22.1 0-40-17.9-40-40l0-48c0-22.1 17.9-40 40-40zM40 160c-22.1 0-40-17.9-40-40L0 72C0 49.9 17.9 32 40 32l48 0c22.1 0 40 17.9 40 40l0 48c0 22.1-17.9 40-40 40l-48 0zM232 32l48 0c22.1 0 40 17.9 40 40l0 48c0 22.1-17.9 40-40 40l-48 0c-22.1 0-40-17.9-40-40l0-48c0-22.1 17.9-40 40-40z"/></svg>',
indeterminate: '<svg part="indeterminate-icon" class="icon" viewBox="0 0 16 16"><g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round"><g stroke="currentColor" stroke-width="2"><g transform="translate(2.285714, 6.857143)"><path d="M10.2857143,1.14285714 L1.14285714,1.14285714"></path></g></g></g></svg>',
minus: '<svg xmlns="http://www.w3.org/2000/svg" height="16" width="14" viewBox="0 0 448 512"><path d="M432 256c0 17.7-14.3 32-32 32L48 288c-17.7 0-32-14.3-32-32s14.3-32 32-32l352 0c17.7 0 32 14.3 32 32z"/></svg>',
pause: '<svg xmlns="http://www.w3.org/2000/svg" height="16" width="10" viewBox="0 0 320 512"><path d="M48 64C21.5 64 0 85.5 0 112V400c0 26.5 21.5 48 48 48H80c26.5 0 48-21.5 48-48V112c0-26.5-21.5-48-48-48H48zm192 0c-26.5 0-48 21.5-48 48V400c0 26.5 21.5 48 48 48h32c26.5 0 48-21.5 48-48V112c0-26.5-21.5-48-48-48H240z"/></svg>',
play: '<svg xmlns="http://www.w3.org/2000/svg" height="16" width="12" viewBox="0 0 384 512"><path d="M73 39c-14.8-9.1-33.4-9.4-48.5-.9S0 62.6 0 80V432c0 17.4 9.4 33.4 24.5 41.9s33.7 8.1 48.5-.9L361 297c14.3-8.7 23-24.2 23-41s-8.7-32.2-23-41L73 39z"/></svg>',
star: '<svg xmlns="http://www.w3.org/2000/svg" height="16" width="18" viewBox="0 0 576 512"><path d="M316.9 18C311.6 7 300.4 0 288.1 0s-23.4 7-28.8 18L195 150.3 51.4 171.5c-12 1.8-22 10.2-25.7 21.7s-.7 24.2 7.9 32.7L137.8 329 113.2 474.7c-2 12 3 24.2 12.9 31.3s23 8 33.8 2.3l128.3-68.5 128.3 68.5c10.8 5.7 23.9 4.9 33.8-2.3s14.9-19.3 12.9-31.3L438.5 329 542.7 225.9c8.6-8.5 11.7-21.2 7.9-32.7s-13.7-19.9-25.7-21.7L381.2 150.3 316.9 18z"/></svg>',
user: '<svg xmlns="http://www.w3.org/2000/svg" height="16" width="14" viewBox="0 0 448 512"><path d="M224 256A128 128 0 1 0 224 0a128 128 0 1 0 0 256zm-45.7 48C79.8 304 0 383.8 0 482.3C0 498.7 13.3 512 29.7 512H418.3c16.4 0 29.7-13.3 29.7-29.7C448 383.8 368.2 304 269.7 304H178.3z"/></svg>',
xmark: '<svg xmlns="http://www.w3.org/2000/svg" height="16" width="12" viewBox="0 0 384 512"><path d="M342.6 150.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L192 210.7 86.6 105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L146.7 256 41.4 361.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L192 301.3 297.4 406.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L237.3 256 342.6 150.6z"/></svg>'
} %}
<style>
.icon-tests {
font-size: .5rem;
line-height: 1;
}
wa-icon {
transition: 1s font-size;
&:hover {
font-size: 1rem;
}
}
</style>
{% set repetitions = 200 %}
<h2>Setting everything via attributes</h2>
<div class="icon-tests">
{% for icon, svg in icons %}
{% for i in range(repetitions) %}
<wa-icon name="{{ icon }}" variant="solid" family="classic"></wa-icon>
{% endfor %}
{% endfor %}
</div>
<h2>Setting variant & family via CSS</h2>
<div class="icon-tests" style="--wa-icon-variant: regular; --wa-icon-family: classic">
{% for icon, svg in icons %}
{% for i in range(repetitions) %}
<wa-icon name="{{ icon }}"></wa-icon>
{% endfor %}
{% endfor %}
</div>
<h2>Setting name via CSS</h2>
<div class="icon-tests">
{% for icon, svg in icons %}
<span style="--wa-icon-name: {{ icon }}">
{% for i in range(repetitions) %}
<wa-icon variant="solid" family="classic"></wa-icon>
{% endfor %}
</span>
{% endfor %}
</div>

View File

@@ -5,4 +5,6 @@ isPro: true
tags: pro
palette: rudimentary
brand: green
fonts:
body: Inter
---

View File

@@ -4,4 +4,6 @@ description: Punchy and vibrant, the rockstar of themes.
order: 0.2
palette: bright
brand: blue
fonts:
body: Quicksand
---

View File

@@ -5,4 +5,8 @@ isPro: true
tags: pro
palette: default
brand: blue
fonts:
body: Space Grotesk
heading: IBM Plex Sans Condensed
code: Space Mono
---

View File

@@ -3,4 +3,5 @@ title: Classic
description: Timeless elegance that never goes out of style.
order: 0.1
palette: classic
brand: blue
---

View File

@@ -4,4 +4,8 @@ description: Your trusty companion, like a perfectly broken-in pair of jeans.
order: 0
palette: default
brand: blue
fonts:
body: ui-sans-serif
code: ui-monospace
longform: ui-serif
---

View File

@@ -26,14 +26,14 @@ eleventyComputed:
{% include 'theme-showcase.njk' %}
{% endset %}
<wa-image-comparer style="width: 100%" position="90">
<wa-comparer style="width: 100%" position="90">
<div slot="after" class="theme-showcase wa-gap-xl">
{{ content | safe }}
</div>
<div slot="before" class="theme-showcase wa-gap-xl wa-invert">
{{ content | safe }}
</div>
</wa-image-comparer>
</wa-comparer>
<script type="module">
import { urls as stylesheetURLs, docsURLs, icons } from "/assets/scripts/tweak/data.js";

View File

@@ -5,4 +5,6 @@ isPro: true
tags: pro
palette: elegant
brand: indigo
fonts:
body: Figtree
---

View File

@@ -5,6 +5,10 @@ isPro: true
tags: pro
palette: mild
brand: indigo
fonts:
body: 'Wix Madefor Text'
code: Roboto Mono
longform: Roboto Serif
---
Set the page theme to "{{ title }}" from the top right to preview the following examples.

View File

@@ -4,4 +4,8 @@ description: Soft and soothing, like a lazy Sunday morning.
isPro: true
tags: pro
palette: natural
fonts:
body: Mulish
heading: Lora
longform: Lora
---

View File

@@ -5,4 +5,8 @@ isPro: true
tags: pro
palette: rudimentary
brand: purple
fonts:
body: Nunito
heading: Fredoka
code: Azeret Mono
---

View File

@@ -5,4 +5,8 @@ isPro: true
tags: pro
palette: anodized
brand: cyan
fonts:
body: DM Sans
heading: Playfair Display
longform: Playfair
---

View File

@@ -30,8 +30,8 @@ function init() {
codeSnippets = document.querySelector('#usage ~ wa-tab-group.import-stylesheet-code:first-of-type');
codeSnippets = {
html: codeSnippets.querySelector('code.language-html'),
css: codeSnippets.querySelector('code.language-css'),
html: codeSnippets?.querySelector('code.language-html'),
css: codeSnippets?.querySelector('code.language-css'),
};
data = {
@@ -73,11 +73,10 @@ function init() {
Promise.all(Object.values(selects).map(select => select.updateComplete)).then(() => render());
return { selects, codeSnippets, data, computed, render };
globalThis.remixApp = { selects, codeSnippets, data, computed, render };
}
globalThis.remixApp = init();
init();
// Async load CSS for other themes *before* current theme stylesheet
let themeStylesheet = document.querySelector('#theme-stylesheet');
@@ -151,12 +150,14 @@ function render(changedAspect) {
// Update code snippets
for (let language in codeSnippets) {
let codeSnippet = codeSnippets[language];
if (!codeSnippet) {
continue;
}
let code = getThemeCode(data.baseTheme, data.params, { language, cdnUrl });
codeSnippet.textContent = code;
Prism.highlightElement(codeSnippet);
}
}
addEventListener('turbo:render', event => {
remixApp = init();
});
addEventListener('turbo:render', init);

View File

@@ -5,4 +5,6 @@ isPro: true
tags: pro
palette: vogue
brand: indigo
fonts:
body: Inter
---

View File

@@ -27,6 +27,7 @@ See your theme's focus ring in action by navigating this form example with your
<style>
form > * + * {
display: block;
--display: block;
width: fit-content;
margin-block-start: var(--wa-space-m);
}

View File

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

View File

@@ -286,7 +286,7 @@ layout: page
</wa-callout>
</div>
<div>
<wa-button href="/docs/installation" appearance="outlined" class="tile">
<wa-button href="/docs/" appearance="outlined" class="tile">
<div style="display: flex; justify-content: space-between; align-items: center; margin-block-end: 1rem;">
<div class="icon-heading" style="margin-block-end: 0;">
<wa-icon name="pen-ruler" fixed-width></wa-icon>

27
package-lock.json generated
View File

@@ -10,12 +10,13 @@
"license": "MIT",
"dependencies": {
"@ctrl/tinycolor": "^4.1.0",
"@floating-ui/dom": "^1.6.12",
"@floating-ui/dom": "^1.6.13",
"@shoelace-style/animations": "^1.2.0",
"@shoelace-style/localize": "^3.2.1",
"composed-offset-position": "^0.0.6",
"lit": "^3.2.1",
"qr-creator": "^1.0.0"
"qr-creator": "^1.0.0",
"style-observer": "^0.0.7"
},
"devDependencies": {
"@11ty/eleventy": "3.0.0",
@@ -1632,18 +1633,20 @@
}
},
"node_modules/@floating-ui/dom": {
"version": "1.6.12",
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.12.tgz",
"integrity": "sha512-NP83c0HjokcGVEMeoStg317VD9W7eDlGK7457dMBANbKA6GJZdc7rjujdgqzTaz93jkGgc5P/jeWbaCHnMNc+w==",
"version": "1.6.13",
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.13.tgz",
"integrity": "sha512-umqzocjDgNRGTuO7Q8CU32dkHkECqI8ZdMZ5Swb6QAM0t5rnlrN3lGo1hdpscRd3WS8T6DKYK4ephgIH9iRh3w==",
"license": "MIT",
"dependencies": {
"@floating-ui/core": "^1.6.0",
"@floating-ui/utils": "^0.2.8"
"@floating-ui/utils": "^0.2.9"
}
},
"node_modules/@floating-ui/utils": {
"version": "0.2.8",
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.8.tgz",
"integrity": "sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig=="
"version": "0.2.9",
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.9.tgz",
"integrity": "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==",
"license": "MIT"
},
"node_modules/@github/catalyst": {
"version": "1.6.0",
@@ -13085,6 +13088,12 @@
"node": ">=0.8.0"
}
},
"node_modules/style-observer": {
"version": "0.0.7",
"resolved": "https://registry.npmjs.org/style-observer/-/style-observer-0.0.7.tgz",
"integrity": "sha512-t75H3CRy+vd5q3yqyrf/De4tkz33hPQTiCcfh0NTesI5G7kJnZ227LEYTwqjKTtaFOCJvqZcYFHpJlF8bsk3bQ==",
"license": "MIT"
},
"node_modules/supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",

View File

@@ -68,12 +68,13 @@
},
"dependencies": {
"@ctrl/tinycolor": "^4.1.0",
"@floating-ui/dom": "^1.6.12",
"@floating-ui/dom": "^1.6.13",
"@shoelace-style/animations": "^1.2.0",
"@shoelace-style/localize": "^3.2.1",
"composed-offset-position": "^0.0.6",
"lit": "^3.2.1",
"qr-creator": "^1.0.0"
"qr-creator": "^1.0.0",
"style-observer": "^0.0.7"
},
"devDependencies": {
"@11ty/eleventy": "3.0.0",

View File

@@ -108,15 +108,14 @@ export default class WaAnimatedImage extends WebAwesomeElement {
<div part="control-box" class="control-box">
<slot name="play-icon">
<wa-icon
name="play"
library="system"
name="system:play"
variant="solid"
class="default"
style="margin-inline-start: 3px;"
></wa-icon>
</slot>
<slot name="pause-icon">
<wa-icon name="pause" library="system" variant="solid" class="default"></wa-icon>
<wa-icon name="system:pause" variant="solid" class="default"></wa-icon>
</slot>
</div>
`

View File

@@ -81,7 +81,7 @@ export default class WaAvatar extends WebAwesomeElement {
} else {
avatarWithoutImage = html`
<slot name="icon" part="icon" class="icon" role="img" aria-label=${this.label}>
<wa-icon name="user" library="system" variant="solid"></wa-icon>
<wa-icon name="system:user" variant="solid"></wa-icon>
</slot>
`;
}

View File

@@ -92,8 +92,7 @@ export default class WaBreadcrumb extends WebAwesomeElement {
<span hidden aria-hidden="true">
<slot name="separator">
<wa-icon
name=${this.localize.dir() === 'rtl' ? 'chevron-left' : 'chevron-right'}
library="system"
name="system:${this.localize.dir() === 'rtl' ? 'chevron-left' : 'chevron-right'}"
variant="solid"
></wa-icon>
</slot>

View File

@@ -1,11 +1,7 @@
:host {
display: contents;
position: relative;
}
--display: inline-flex;
:where([part~='base']) {
all: inherit;
display: inline-flex;
position: relative;
}
/*

View File

@@ -7,6 +7,7 @@ import { MirrorValidator } from '../../internal/validators/mirror-validator.js';
import { watch } from '../../internal/watch.js';
import { WebAwesomeFormAssociatedElement } from '../../internal/webawesome-form-associated-element.js';
import nativeStyles from '../../styles/native/button.css';
import passthroughStyles from '../../styles/shadow/passthrough.css';
import appearanceStyles from '../../styles/utilities/appearance.css';
import sizeStyles from '../../styles/utilities/size.css';
import variantStyles from '../../styles/utilities/variants.css';
@@ -39,6 +40,7 @@ import styles from './button.css';
* @csspart caret - The button's caret icon, a `<wa-icon>` element.
* @csspart spinner - The spinner that shows when the button is in the loading state.
*
* @cssproperty --display - Set to `none` to hide the element, or any other valid `display` value to override the internal `display` value of the `base` part.
* @cssproperty --background-color - The button's background color when the button is not being interacted with.
* @cssproperty --background-color-active - The button's background color when active.
* @cssproperty --background-color-hover - The button's background color on hover.
@@ -51,7 +53,7 @@ import styles from './button.css';
*/
@customElement('wa-button')
export default class WaButton extends WebAwesomeFormAssociatedElement {
static shadowStyle = [variantStyles, appearanceStyles, sizeStyles, nativeStyles, styles];
static shadowStyle = [passthroughStyles, variantStyles, appearanceStyles, sizeStyles, nativeStyles, styles];
static rectProxy = 'button';
static get validators() {
@@ -263,9 +265,7 @@ export default class WaButton extends WebAwesomeFormAssociatedElement {
<slot name="suffix" part="suffix" class="suffix"></slot>
${
this.caret
? html`
<wa-icon part="caret" class="caret" library="system" name="chevron-down" variant="solid"></wa-icon>
`
? html` <wa-icon part="caret" class="caret" name="system:chevron-down" variant="solid"></wa-icon> `
: ''
}
${this.loading ? html`<wa-spinner part="spinner"></wa-spinner>` : ''}

View File

@@ -608,7 +608,7 @@ export default class WaCarousel extends WebAwesomeElement {
@click=${prevEnabled ? () => this.previous() : null}
>
<slot name="previous-icon">
<wa-icon library="system" name="${isRTL ? 'chevron-right' : 'chevron-left'}"></wa-icon>
<wa-icon name="system:${isRTL ? 'chevron-right' : 'chevron-left'}"></wa-icon>
</slot>
</button>
@@ -625,7 +625,7 @@ export default class WaCarousel extends WebAwesomeElement {
@click=${nextEnabled ? () => this.next() : null}
>
<slot name="next-icon">
<wa-icon library="system" name="${isRTL ? 'chevron-left' : 'chevron-right'}"></wa-icon>
<wa-icon name="system:${isRTL ? 'chevron-left' : 'chevron-right'}"></wa-icon>
</slot>
</button>
</div>

View File

@@ -234,7 +234,7 @@ export default class WaCheckbox extends WebAwesomeFormAssociatedElement {
@click=${this.handleClick}
/>
<wa-icon part="${iconState}-icon icon" library="system" name=${iconName}></wa-icon>
<wa-icon part="${iconState}-icon icon" name="system:${iconName}"></wa-icon>
</span>
<slot part="label"></slot>

View File

@@ -210,12 +210,6 @@ export default class WaColorPicker extends WebAwesomeFormAssociatedElement {
/** Disables the color picker. */
@property({ type: Boolean }) disabled = false;
/**
* Enable this option to prevent the panel from being clipped when the component is placed inside a container with
* `overflow: auto|scroll`. Hoisting uses a fixed positioning strategy that works in many, but not all, scenarios.
*/
@property({ type: Boolean }) hoist = false;
/** Shows the opacity slider. Enabling this will cause the formatted value to be HEXA, RGBA, or HSLA. */
@property({ type: Boolean }) opacity = false;
@@ -1047,8 +1041,7 @@ export default class WaColorPicker extends WebAwesomeFormAssociatedElement {
@focus=${this.stopNestedEventPropagation}
>
<wa-icon
library="system"
name="eye-dropper"
name="system:eye-dropper"
variant="solid"
label=${this.localize.term('selectAColorFromTheScreen')}
></wa-icon>
@@ -1097,7 +1090,6 @@ export default class WaColorPicker extends WebAwesomeFormAssociatedElement {
aria-disabled=${this.disabled ? 'true' : 'false'}
.containingElement=${this}
?disabled=${this.disabled}
?hoist=${this.hoist}
@wa-after-show=${this.handleAfterShow}
@wa-after-hide=${this.handleAfterHide}
>

View File

@@ -6,9 +6,6 @@
display: inline-block;
position: relative;
}
.image-comparer {
max-width: 100%;
max-height: 100%;
overflow: hidden;
@@ -24,6 +21,10 @@
max-width: 100% !important;
height: auto;
}
&::slotted(:not(img, svg)) {
isolation: isolate;
}
}
.after {

View File

@@ -1,18 +1,19 @@
import { expect } from '@open-wc/testing';
import { sendKeys } from '@web/test-runner-commands';
import { html } from 'lit';
import sinon from 'sinon';
import { fixtures } from '../../internal/test/fixture.js';
import type WaImageComparer from './image-comparer.js';
import type WaComparer from './comparer.js';
describe('<wa-image-comparer>', () => {
describe('<wa-comparer>', () => {
for (const fixture of fixtures) {
describe(`with "${fixture.type}" rendering`, () => {
it('should render a basic before/after', async () => {
const el = await fixture<WaImageComparer>(html`
<wa-image-comparer>
const el = await fixture<WaComparer>(html`
<wa-comparer>
<div slot="before"></div>
<div slot="after"></div>
</wa-image-comparer>
</wa-comparer>
`);
const afterPart = el.shadowRoot!.querySelector<HTMLElement>('[part~="after"]')!;
@@ -29,11 +30,11 @@ describe('<wa-image-comparer>', () => {
});
it('should emit change event when position changed manually', async () => {
const el = await fixture<WaImageComparer>(html`
<wa-image-comparer>
const el = await fixture<WaComparer>(html`
<wa-comparer>
<div slot="before"></div>
<div slot="after"></div>
</wa-image-comparer>
</wa-comparer>
`);
const handler = sinon.spy();
@@ -46,194 +47,166 @@ describe('<wa-image-comparer>', () => {
});
it('should increment position on arrow right', async () => {
const el = await fixture<WaImageComparer>(html`
<wa-image-comparer>
const el = await fixture<WaComparer>(html`
<wa-comparer>
<div slot="before"></div>
<div slot="after"></div>
</wa-image-comparer>
</wa-comparer>
`);
const base = el.shadowRoot!.querySelector<HTMLElement>('[part~="base"]')!;
base.dispatchEvent(
new KeyboardEvent('keydown', {
key: 'ArrowRight',
}),
);
const handle = el.shadowRoot!.querySelector<HTMLElement>('[part~="handle"]')!;
handle.focus();
await sendKeys({ press: 'ArrowRight' });
await el.updateComplete;
expect(el.position).to.equal(51);
});
it('should decrement position on arrow left', async () => {
const el = await fixture<WaImageComparer>(html`
<wa-image-comparer>
const el = await fixture<WaComparer>(html`
<wa-comparer>
<div slot="before"></div>
<div slot="after"></div>
</wa-image-comparer>
</wa-comparer>
`);
const base = el.shadowRoot!.querySelector<HTMLElement>('[part~="base"]')!;
const handle = el.shadowRoot!.querySelector<HTMLElement>('[part~="handle"]')!;
base.dispatchEvent(
new KeyboardEvent('keydown', {
key: 'ArrowLeft',
}),
);
handle.focus();
await sendKeys({ press: 'ArrowLeft' });
await el.updateComplete;
expect(el.position).to.equal(49);
});
it('should set position to 0 on home key', async () => {
const el = await fixture<WaImageComparer>(html`
<wa-image-comparer>
const el = await fixture<WaComparer>(html`
<wa-comparer>
<div slot="before"></div>
<div slot="after"></div>
</wa-image-comparer>
</wa-comparer>
`);
const base = el.shadowRoot!.querySelector<HTMLElement>('[part~="base"]')!;
const handle = el.shadowRoot!.querySelector<HTMLElement>('[part~="handle"]')!;
base.dispatchEvent(
new KeyboardEvent('keydown', {
key: 'Home',
}),
);
handle.focus();
await sendKeys({ press: 'Home' });
await el.updateComplete;
expect(el.position).to.equal(0);
});
it('should set position to 100 on end key', async () => {
const el = await fixture<WaImageComparer>(html`
<wa-image-comparer>
const el = await fixture<WaComparer>(html`
<wa-comparer>
<div slot="before"></div>
<div slot="after"></div>
</wa-image-comparer>
</wa-comparer>
`);
const base = el.shadowRoot!.querySelector<HTMLElement>('[part~="base"]')!;
const handle = el.shadowRoot!.querySelector<HTMLElement>('[part~="handle"]')!;
base.dispatchEvent(
new KeyboardEvent('keydown', {
key: 'End',
}),
);
handle.focus();
await sendKeys({ press: 'End' });
await el.updateComplete;
expect(el.position).to.equal(100);
});
it('should clamp to 100 on arrow right', async () => {
const el = await fixture<WaImageComparer>(html`
<wa-image-comparer>
const el = await fixture<WaComparer>(html`
<wa-comparer>
<div slot="before"></div>
<div slot="after"></div>
</wa-image-comparer>
</wa-comparer>
`);
el.position = 0;
await el.updateComplete;
const base = el.shadowRoot!.querySelector<HTMLElement>('[part~="base"]')!;
const handle = el.shadowRoot!.querySelector<HTMLElement>('[part~="handle"]')!;
base.dispatchEvent(
new KeyboardEvent('keydown', {
key: 'ArrowLeft',
}),
);
handle.focus();
await sendKeys({ press: 'ArrowLeft' });
await el.updateComplete;
expect(el.position).to.equal(0);
});
it('should clamp to 0 on arrow left', async () => {
const el = await fixture<WaImageComparer>(html`
<wa-image-comparer>
const el = await fixture<WaComparer>(html`
<wa-comparer>
<div slot="before"></div>
<div slot="after"></div>
</wa-image-comparer>
</wa-comparer>
`);
el.position = 100;
await el.updateComplete;
const base = el.shadowRoot!.querySelector<HTMLElement>('[part~="base"]')!;
const handle = el.shadowRoot!.querySelector<HTMLElement>('[part~="handle"]')!;
base.dispatchEvent(
new KeyboardEvent('keydown', {
key: 'ArrowRight',
}),
);
handle.focus();
await sendKeys({ press: 'ArrowRight' });
await el.updateComplete;
expect(el.position).to.equal(100);
});
it('should increment position by 10 on arrow right + shift', async () => {
const el = await fixture<WaImageComparer>(html`
<wa-image-comparer>
const el = await fixture<WaComparer>(html`
<wa-comparer>
<div slot="before"></div>
<div slot="after"></div>
</wa-image-comparer>
</wa-comparer>
`);
const base = el.shadowRoot!.querySelector<HTMLElement>('[part~="base"]')!;
const handle = el.shadowRoot!.querySelector<HTMLElement>('[part~="handle"]')!;
base.dispatchEvent(
new KeyboardEvent('keydown', {
key: 'ArrowRight',
shiftKey: true,
}),
);
handle.focus();
await sendKeys({ press: 'Shift+ArrowRight' });
await el.updateComplete;
expect(el.position).to.equal(60);
});
it('should decrement position by 10 on arrow left + shift', async () => {
const el = await fixture<WaImageComparer>(html`
<wa-image-comparer>
const el = await fixture<WaComparer>(html`
<wa-comparer>
<div slot="before"></div>
<div slot="after"></div>
</wa-image-comparer>
</wa-comparer>
`);
const base = el.shadowRoot!.querySelector<HTMLElement>('[part~="base"]')!;
const handle = el.shadowRoot!.querySelector<HTMLElement>('[part~="handle"]')!;
base.dispatchEvent(
new KeyboardEvent('keydown', {
key: 'ArrowLeft',
shiftKey: true,
}),
);
handle.focus();
await sendKeys({ press: 'Shift+ArrowLeft' });
await el.updateComplete;
expect(el.position).to.equal(40);
});
it('should set position by attribute', async () => {
const el = await fixture<WaImageComparer>(html`
<wa-image-comparer position="10">
const el = await fixture<WaComparer>(html`
<wa-comparer position="10">
<div slot="before"></div>
<div slot="after"></div>
</wa-image-comparer>
</wa-comparer>
`);
expect(el.position).to.equal(10);
});
it('should move position on drag', async () => {
const el = await fixture<WaImageComparer>(html`
<wa-image-comparer>
const el = await fixture<WaComparer>(html`
<wa-comparer>
<div slot="before" style="width: 50px"></div>
<div slot="after" style="width: 50px"></div>
</wa-image-comparer>
</wa-comparer>
`);
const handle = el.shadowRoot!.querySelector<HTMLElement>('[part~="handle"]')!;
const base = el.shadowRoot!.querySelector<HTMLElement>('[part~="base"]')!;
const rect = base.getBoundingClientRect();
const rect = handle.getBoundingClientRect();
const offsetX = rect.left + window.pageXOffset;
const offsetY = rect.top + window.pageYOffset;
@@ -241,7 +214,7 @@ describe('<wa-image-comparer>', () => {
document.dispatchEvent(
new PointerEvent('pointermove', {
clientX: offsetX + 20,
clientX: offsetX + 15,
clientY: offsetY,
}),
);

View File

@@ -7,52 +7,50 @@ import { watch } from '../../internal/watch.js';
import WebAwesomeElement from '../../internal/webawesome-element.js';
import { LocalizeController } from '../../utilities/localize.js';
import '../icon/icon.js';
import styles from './image-comparer.css';
import styles from './comparer.css';
/**
* @summary Compare visual differences between similar photos with a sliding panel.
* @documentation https://backers.webawesome.com/docs/components/image-comparer
* @summary Compare visual differences between similar content with a sliding panel.
* @documentation https://backers.webawesome.com/docs/components/comparer
* @status stable
* @since 2.0
*
* @dependency wa-icon
*
* @slot before - The before image, an `<img>` or `<svg>` element.
* @slot after - The after image, an `<img>` or `<svg>` element.
* @slot before - The before content, often an `<img>` or `<svg>` element.
* @slot after - The after content, often an `<img>` or `<svg>` element.
* @slot handle - The icon used inside the handle.
*
* @event change - Emitted when the position changes.
*
* @csspart base - The component's base wrapper.
* @csspart before - The container that wraps the before image.
* @csspart after - The container that wraps the after image.
* @csspart divider - The divider that separates the images.
* @csspart handle - The handle that the user drags to expose the after image.
* @csspart before - The container that wraps the before content.
* @csspart after - The container that wraps the after content.
* @csspart divider - The divider that separates the before and after content.
* @csspart handle - The handle that the user drags to expose the after content.
*
* @cssproperty --divider-color - The color of the divider.
* @cssproperty --divider-width - The width of the dividing line.
* @cssproperty --handle-color - The color of the icon used inside the handle.
* @cssproperty --handle-size - The size of the compare handle.
*/
@customElement('wa-image-comparer')
export default class WaImageComparer extends WebAwesomeElement {
@customElement('wa-comparer')
export default class WaComparer extends WebAwesomeElement {
static shadowStyle = styles;
private readonly localize = new LocalizeController(this);
@query('.image-comparer') base: HTMLElement;
@query('.handle') handle: HTMLElement;
/** The position of the divider as a percentage. */
@property({ type: Number, reflect: true }) position = 50;
private handleDrag(event: PointerEvent) {
const { width } = this.base.getBoundingClientRect();
const { width } = this.getBoundingClientRect();
const isRtl = this.localize.dir() === 'rtl';
event.preventDefault();
drag(this.base, {
drag(this, {
onMove: x => {
this.position = parseFloat(clamp((x / width) * 100, 0, 100).toFixed(2));
if (isRtl) this.position = 100 - this.position;
@@ -98,46 +96,45 @@ export default class WaImageComparer extends WebAwesomeElement {
const isRtl = this.hasUpdated ? this.localize.dir() === 'rtl' : this.dir === 'rtl';
return html`
<div part="base" id="image-comparer" class="image-comparer" @keydown=${this.handleKeyDown}>
<div class="image">
<div part="before" class="before">
<slot name="before"></slot>
</div>
<div
part="after"
class="after"
style=${styleMap({
clipPath: isRtl ? `inset(0 0 0 ${100 - this.position}%)` : `inset(0 ${100 - this.position}% 0 0)`,
})}
>
<slot name="after"></slot>
</div>
<div class="image">
<div part="before" class="before">
<slot name="before"></slot>
</div>
<div
part="divider"
class="divider"
part="after"
class="after"
style=${styleMap({
left: isRtl ? `${100 - this.position}%` : `${this.position}%`,
clipPath: isRtl ? `inset(0 0 0 ${100 - this.position}%)` : `inset(0 ${100 - this.position}% 0 0)`,
})}
@mousedown=${this.handleDrag}
@touchstart=${this.handleDrag}
>
<div
part="handle"
class="handle"
role="scrollbar"
aria-valuenow=${this.position}
aria-valuemin="0"
aria-valuemax="100"
aria-controls="image-comparer"
tabindex="0"
>
<slot name="handle">
<wa-icon library="system" name="grip-vertical" variant="solid"></wa-icon>
</slot>
</div>
<slot name="after"></slot>
</div>
</div>
<div
part="divider"
class="divider"
style=${styleMap({
left: isRtl ? `${100 - this.position}%` : `${this.position}%`,
})}
@keydown=${this.handleKeyDown}
@mousedown=${this.handleDrag}
@touchstart=${this.handleDrag}
>
<div
part="handle"
class="handle"
role="scrollbar"
aria-valuenow=${this.position}
aria-valuemin="0"
aria-valuemax="100"
aria-controls="comparer"
tabindex="0"
>
<slot name="handle">
<wa-icon name="system:grip-vertical" variant="solid"></wa-icon>
</slot>
</div>
</div>
`;
@@ -146,6 +143,6 @@ export default class WaImageComparer extends WebAwesomeElement {
declare global {
interface HTMLElementTagNameMap {
'wa-image-comparer': WaImageComparer;
'wa-comparer': WaComparer;
}
}

View File

@@ -97,13 +97,6 @@ export default class WaCopyButton extends WebAwesomeElement {
/** The preferred placement of the tooltip. */
@property({ attribute: 'tooltip-placement' }) tooltipPlacement: 'top' | 'right' | 'bottom' | 'left' = 'top';
/**
* Enable this option to prevent the tooltip from being clipped when the component is placed inside a container with
* `overflow: auto|hidden|scroll`. Hoisting uses a fixed positioning strategy that works in many, but not all,
* scenarios.
*/
@property({ type: Boolean }) hoist = false;
private async handleCopy() {
if (this.disabled || this.isCopying) {
return;
@@ -203,13 +196,13 @@ export default class WaCopyButton extends WebAwesomeElement {
<!-- Render a visually hidden label to appease the accessibility checking gods -->
<span class="wa-visually-hidden">${this.currentLabel}</span>
<slot part="copy-icon" name="copy-icon">
<wa-icon library="system" name="copy" variant="regular" fixed-width></wa-icon>
<wa-icon name="system:copy" variant="regular" fixed-width></wa-icon>
</slot>
<slot part="success-icon" name="success-icon" variant="solid" hidden>
<wa-icon library="system" name="check" fixed-width></wa-icon>
<wa-icon name="system:check" fixed-width></wa-icon>
</slot>
<slot part="error-icon" name="error-icon" variant="solid" hidden>
<wa-icon library="system" name="xmark" fixed-width></wa-icon>
<wa-icon name="system:xmark" fixed-width></wa-icon>
</slot>
<wa-tooltip
class=${classMap({
@@ -220,7 +213,6 @@ export default class WaCopyButton extends WebAwesomeElement {
for="copy-button"
placement=${this.tooltipPlacement}
?disabled=${this.disabled}
?hoist=${this.hoist}
exportparts="
base:tooltip__base,
base__popup:tooltip__base__popup,

View File

@@ -1,13 +1,10 @@
:host {
--show-duration: 200ms;
--hide-duration: 200ms;
display: contents !important;
--display: block;
}
details {
all: inherit;
display: block;
overflow-anchor: none;
}

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