Compare commits

...

23 Commits

Author SHA1 Message Date
Brian Talbot
272f6853ce WIP - revising + extending background pattern styling 2025-11-14 10:04:19 -05:00
Brian Talbot
5a6ce1c4d3 Gating Launch Sale-Based Banner and Dialog (#1769)
* gating wa-launch banner and dialog based on coupon state

* addressing PR feedback

* Update packages/webawesome/docs/_includes/_dialog-wa-launch.njk

Co-authored-by: Konnor Rogers <konnor5456@gmail.com>

---------

Co-authored-by: Konnor Rogers <konnor5456@gmail.com>
2025-11-13 11:46:55 -05:00
Brian Talbot
e695b54dcd Revert Custom Last Modified Utility (#1750)
* reverting custom `getLastModifiedISO` utility

* using 11ty's `git Last Modified` on docs/resources/changelog

* dumbing down "Last updated" date to be static
2025-11-13 11:25:29 -05:00
Brian Talbot
9a3fc59e6a forcing brand color on launch sale dialog (#1764)
* to override any docs-based brand
2025-11-12 15:21:28 -05:00
Brian Talbot
dfaa959951 Add Launch Sale Dialog (#1739)
* adding site-wide dialog to base.njk

* adding logic to control dialog rendering

* adding sale-based site-wide dialog

* overriding docs brand props for `#dialog-site`

* adding plausible event tracking to sale-based site-wide dialog
2025-11-12 14:46:04 -05:00
Brian Talbot
9d6ded49b9 Fix Broken Docs Links (#1740)
* fixing broken links in docs/resources/contributing.md

* removing broken links in docs/resources/changelog.md

* fixing broken link in docs/components/button-group.md

* fixing broken link in docs/components/comparison.md

* fixing broken link in docs/components/divider.md

* fixing broken link in docs/components/radio-group.md

* fixing broken link in docs/utilities/frame.md

* fixing broken link in docs/utilities/gap.md

* fixing broken link in docs/utilities/rounding.md

* Apply suggestions from code review

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

* addressing PR feedback

---------

Co-authored-by: Lindsay M <126139086+lindsaym-fa@users.noreply.github.com>
2025-11-07 13:35:09 -05:00
Brian Talbot
3451255454 Docs: Add Free Badge (#1696)
* adding Free Badge macro

* using semantic color value for Pro Badge's `color`

* removing unncessary `border-color` from Pro Badge's custom CSS

* adding `data-pro-badge` attr to Pro Badge

* using freeBadge and proBadge macros in themes.njk
2025-11-06 13:12:57 -05:00
Brian Talbot
20628a98eb 🧼 Anchor Link Clean Up (#1732)
* using wa-icon in generated anchors

* moving anchor-heading styling from docs.css to utils.css

* improving anchor-heading styling

* adding subtle transition
* using --wa-space-3xs custom property for padding
2025-11-06 11:13:54 -05:00
Brian Talbot
3d4b3ada27 Allow Setting data-no-anchor at the View-Level (#1718)
* adding option to set 11ty's `data-no-anchor` at view-level

* moving all `base.njk` initializations to before `<html>`

* updating anchor-headings.js transformer logic
2025-11-05 14:06:56 -05:00
Lindsay M
0bead172fe Tweak <wa-radio appearance="button"> focus styles (#1695) 2025-11-05 13:31:40 -05:00
Christian Oliff
9ca3e6f39c Fix typo in 'Github' heading to 'GitHub' (#1720) 2025-11-05 07:25:13 -05:00
Brian Talbot
e2c7286ccc Control 11ty-Generated Headings' Display via Front Matter (#1715)
* controlling 11ty generated headings' display via frontmatter

* addressing PR feedback

* addressing additional PR feedback
2025-11-04 14:10:59 -05:00
Cory LaViska
46812e08c5 fix slider on touch; fixes #1703 (#1709) 2025-11-03 09:43:21 -05:00
Brian Talbot
0f601df74a removing page title suffixes from search results (#1697)
* Use <title> for search indexing but strip trailing site suffix at
build time
* centralize title logic with shared composePageTitle() and
stripSiteTitleSuffix() helpers
* introduce single source of truth for site name and separators
* keep client search rendering simple; no post-processing in the browser
* preserve existing page/OG title behavior while improving search
relevance
2025-10-31 14:03:39 -04:00
Cory LaViska
eb2410ff88 fix links (#1691) 2025-10-30 11:29:30 -04:00
Cory LaViska
479fff4b61 remove unsupported property; closes #1677 (#1689) 2025-10-30 11:06:47 -04:00
Brian Talbot
9c7b341889 adding planned stretch goal-based components to sidebar.njk (#1682) 2025-10-30 10:41:50 -04:00
Kelsey Jackson
d09973b571 updated sidebar (#1687)
* updated sidebar

* Update packages/webawesome/docs/_includes/sidebar.njk

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* fixed pr

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-10-30 09:22:39 -05:00
Cory LaViska
a03da137e3 nest tree item (#1686) 2025-10-30 09:37:19 -04:00
Brian Talbot
d8b0447151 Docs: Adding CSS Selector Info to Components (#1679)
* adding CSS selector info to component docs

* adding Copy Button to custom states' CSS selectors in component docs
2025-10-29 17:25:50 -04:00
konnorrogers
6f0bd4e872 update versions.txt 2025-10-28 12:19:06 -04:00
konnorrogers
55973a7eff update package-lock 2025-10-28 12:15:46 -04:00
konnorrogers
e9aa72cc87 Bump package.json version 2025-10-28 12:14:39 -04:00
37 changed files with 570 additions and 261 deletions

View File

@@ -1,2 +1 @@
3.0.0-beta.5
3.0.0-beta.6
3.0.0

2
package-lock.json generated
View File

@@ -14013,7 +14013,7 @@
},
"packages/webawesome": {
"name": "@awesome.me/webawesome",
"version": "3.0.0-beta.6",
"version": "3.0.0",
"license": "MIT",
"dependencies": {
"@ctrl/tinycolor": "4.1.0",

View File

@@ -1,6 +1,5 @@
import { nanoid } from 'nanoid';
import { parse as HTMLParse } from 'node-html-parser';
import { execFileSync } from 'node:child_process';
import * as fs from 'node:fs';
import * as path from 'node:path';
import { anchorHeadingsTransformer } from './_transformers/anchor-headings.js';
@@ -21,7 +20,6 @@ import { replaceTextPlugin } from './_plugins/replace-text.js';
import { searchPlugin } from './_plugins/search.js';
const __dirname = url.fileURLToPath(new URL('.', import.meta.url));
const isDev = process.argv.includes('--develop');
const ignoreGit = process.env.ELEVENTY_IGNORE_GIT === 'true';
const passThroughExtensions = ['js', 'css', 'png', 'svg', 'jpg', 'mp4'];
async function getPackageData() {
@@ -60,15 +58,6 @@ export default async function (eleventyConfig) {
if (updateComponentData) {
allComponents = getComponents();
}
// Invalidate last-modified cache for changed content files during watch
if (Array.isArray(changedFiles)) {
for (const file of changedFiles) {
if (/\.(md|njk|html)$/i.test(file)) {
lastModCache.delete(file);
}
}
}
});
/**
@@ -102,6 +91,31 @@ export default async function (eleventyConfig) {
image: 'https://webawesome.com/assets/images/open-graph/default.png',
};
// Title composition/stripping config - single source of truth
const SITE_NAME = siteMetadata.name;
const SITE_TITLE_SEPARATORS = ['|'];
// Helper to escape user-provided strings for safe use inside RegExp sources
const escapeRegExp = string => (string + '').replace(/[.*+?^${}()|[\\]\\]/g, '\\$&');
// Precompute a reusable regex to strip a trailing site name suffix from titles, e.g. " | Web Awesome"
// Supports configured separators and flexible whitespace. This keeps search titles clean and improves Lunr scoring
const siteNameEscapedForRegex = escapeRegExp(SITE_NAME);
const separatorsEscaped = SITE_TITLE_SEPARATORS.map(s => escapeRegExp(s)).join('');
const siteTitleSuffixPattern = new RegExp(`\\s*[${separatorsEscaped}]\\s*${siteNameEscapedForRegex}$`);
// Helper to remove the site suffix from a page title. Keep this in sync with how page titles
// are composed (see eleventyComputed.pageTitle) so search indexing stays consistent
const stripSiteTitleSuffix = title => (title || '').replace(siteTitleSuffixPattern, '');
// Helper to compose a full page title with site suffix when appropriate
// Uses the same separator set as the stripping logic for consistency
const composePageTitle = baseTitle => {
const title = baseTitle || SITE_NAME;
const preferredSeparator = SITE_TITLE_SEPARATORS[0] || '|';
return title !== SITE_NAME ? `${title} ${preferredSeparator} ${SITE_NAME}` : title;
};
eleventyConfig.addGlobalData('siteMetadata', siteMetadata);
// Template filters - {{ content | filter }}
@@ -110,97 +124,6 @@ export default async function (eleventyConfig) {
eleventyConfig.addFilter('stripExtension', string => path.parse(string + '').name);
eleventyConfig.addFilter('stripPrefix', content => content.replace(/^wa-/, ''));
eleventyConfig.addFilter('uniqueId', (_value, length = 8) => nanoid(length));
// Returns last modified date as ISO 8601 (UTC, Z-suffixed)
// Fallback order: front matter override -> Git last commit date -> filesystem mtime -> now
// Caching: in-memory per inputPath during one build/dev session
// Override: pass a Date or string: {{ page.inputPath | gitLastModifiedISO(lastUpdated) }}
const lastModCache = new Map();
let repoRoot = null; // lazily resolved; null => not resolved, undefined => failed
function getLastModifiedISO(inputPath, overrideDate) {
if (overrideDate instanceof Date) {
return overrideDate.toISOString();
}
if (typeof overrideDate === 'string' && overrideDate) {
const parsed = new Date(overrideDate);
if (!isNaN(parsed.getTime())) return parsed.toISOString();
}
if (!inputPath) return new Date().toISOString();
if (lastModCache.has(inputPath)) return lastModCache.get(inputPath);
// Try Git (ISO via %cI). Use a repo-root-relative path for portability.
if (!ignoreGit) {
try {
if (repoRoot === null) {
try {
repoRoot = execFileSync('git', ['rev-parse', '--show-toplevel'], {
stdio: ['ignore', 'pipe', 'ignore'],
cwd: __dirname,
})
.toString()
.trim();
} catch (_) {
repoRoot = undefined;
}
}
const gitPath = repoRoot ? path.relative(repoRoot, inputPath) : inputPath;
const args = ['log', '-1', '--format=%cI', '--follow', '--', gitPath];
const result = execFileSync('git', args, {
stdio: ['ignore', 'pipe', 'ignore'],
cwd: repoRoot || path.dirname(inputPath),
})
.toString()
.trim();
if (result) {
const iso = new Date(result).toISOString();
lastModCache.set(inputPath, iso);
return iso;
}
} catch (_) {
// continue to fs fallback
}
}
// Fallback to filesystem mtime
try {
const stats = fs.statSync(inputPath);
const iso = new Date(stats.mtime).toISOString();
lastModCache.set(inputPath, iso);
return iso;
} catch (_) {
const now = new Date().toISOString();
lastModCache.set(inputPath, now);
return now;
}
}
eleventyConfig.addFilter('gitLastModifiedISO', function (inputPath, overrideDate) {
return getLastModifiedISO(inputPath, overrideDate);
});
// Attach lastUpdatedISO to page data so templates can use {{ lastUpdatedISO }} directly
eleventyConfig.addGlobalData('eleventyComputed', {
lastUpdatedISO: data => getLastModifiedISO(data.page?.inputPath, data.lastUpdated),
// Page title with smart + default site name formatting
pageTitle: data => {
const title = data.title || siteMetadata.name;
return title !== siteMetadata.name ? `${title} | ${siteMetadata.name}` : title;
},
// Open Graph title with smart + default site name formatting
ogTitle: data => {
const ogTitle = data.ogTitle || data.title || siteMetadata.name;
return ogTitle !== siteMetadata.name ? `${ogTitle} | ${siteMetadata.name}` : ogTitle;
},
ogDescription: data => data.ogDescription || data.description,
ogImage: data => data.ogImage || siteMetadata.image,
ogUrl: data => {
if (data.ogUrl) return data.ogUrl;
const url = data.page?.url || '';
return url ? `${siteMetadata.url}${url}` : siteMetadata.url;
},
ogType: data => data.ogType || 'website',
});
// Trims whitespace and pipes from the start and end of a string. Useful for CEM types, which can be pipe-delimited.
// With Prettier 3, this means a leading pipe will exist be present when the line wraps.
eleventyConfig.addFilter('trimPipes', content => {
@@ -354,6 +277,11 @@ export default async function (eleventyConfig) {
searchPlugin({
filename: '',
selectorsToIgnore: ['code.example'],
// Use <title> but strip a trailing site name suffix for cleaner search results
getTitle: doc => {
const raw = doc.querySelector('title')?.textContent ?? '';
return stripSiteTitleSuffix(raw);
},
getContent: doc => doc.querySelector('#content')?.textContent ?? '',
}),
);

View File

@@ -33,9 +33,9 @@ unlisted: true
--secondary-opacity: 1.0;
}
wa-page.background-grid {
--grid-line-color: color-mix(in oklab, var(--wa-color-text-normal), transparent 96%);
--subgrid-line-color: color-mix(in oklab, var(--wa-color-text-normal), transparent 98%);
wa-page.background-overlay-grid {
--grid-opacity: 0.04;
--subgrid-opacity: 0.02;
}
wa-page > [slot='main-footer'] {
@@ -209,7 +209,7 @@ unlisted: true
.vehicle wa-icon[name="bulldozer"] { animation: rumble-bulldozer 0.1s ease-in-out infinite; }
</style>
<wa-page disable-sticky="header" class="background-grid">
<wa-page disable-sticky="header" class="background-overlay-grid">
<header slot="header">
<div class="header-content wa-split" style="flex-wrap: nowrap;">
<wa-icon variant="brands" name="web-awesome" class="icon-brand-logo"></wa-icon>

View File

@@ -1,5 +1,5 @@
{% raw %}
{%- if not currentUser.hasPro -%}
{%- if req.stripe.discount.active and not currentUser.hasPro -%}
<div slot="banner" class="banner-wa-launch wa-dark">
<div class="banner-content wa-split">
<div class="wa-cluster wa-gap-s">

View File

@@ -0,0 +1,95 @@
{% raw %}
{%- if req.stripe.discount.active and not currentUser.hasPro -%}
<wa-dialog id="dialog-site" light-dismiss without-header>
<div class="background-overlay-wa-pattern" style="justify-content: center; margin-inline: calc(var(--spacing) * -1); margin-block-start: calc(var(--spacing) * -1); margin-block-end: var(--spacing); background: linear-gradient(to bottom, var(--wa-color-brand), var(--wa-color-brand-50)); color: var(--wa-color-brand-on-loud); padding: var(--wa-space-3xl) var(--spacing); --background-opacity: 0.2;">
<div class="wa-stack wa-align-items-center" style="text-align: center;">
<wa-icon name="party-horn" family="duotone" variant="solid" style="font-size: var(--wa-font-size-3xl); --secondary-color: var(--wa-color-brand-40); --secondary-opacity: 1.0;"></wa-icon>
<h2 class="wa-heading-2xl brand-font">Get a lifetime discount on Web Awesome Pro!</h2>
</div>
</div>
<div class="wa-stack wa-gap-l">
<p>Celebrate our official launch with a 20% discount on a Web Awesome Pro plan&hellip;<span class="appearance-underlined variant-drawn" style="--underline-color: var(--wa-color-brand);">for life</span>! But hurry, this lifetime discount is only available for a limited time.</p>
<div class="wa-split">
<wa-button type="button" appearance="plain" data-dialog="close" class="plausible-event-name=launch_dialog:close_button_click">Maybe Later</wa-button>
<wa-button variant="neutral" appearance="accent" href="/purchase" class="brand-font plausible-event-name=launch_dialog:pro_purchase_button_click">
<wa-icon slot="start" variant="regular" name="rocket-launch"></wa-icon>
Get Pro + Save 20%
</wa-button>
</div>
</div>
</wa-dialog>
<script type="module">
(function() {
const SITE_DIALOG_DISMISSED_KEY = 'dialog-wa-launch-sale-dismissed';
// Early exit if user has dismissed the dialog
try {
if (localStorage.getItem(SITE_DIALOG_DISMISSED_KEY) === 'true') {
return;
}
} catch (e) {
// localStorage may be disabled or unavailable
return;
}
// Wait for DOM and dialog element
const dialog = document.getElementById('dialog-site');
if (!dialog) {
return;
}
// Helper function to safely track Plausible events
const trackEvent = (eventName) => {
if (typeof plausible !== 'undefined') {
plausible(eventName);
}
};
// Initialize dialog functionality
let initCalled = false;
const initDialog = () => {
// Prevent double initialization
if (initCalled) {
return;
}
initCalled = true;
// Track when dialog is shown
dialog.addEventListener('wa-show', () => {
trackEvent('launch_dialog:view');
}, { once: true });
// Track when dialog is dismissed
dialog.addEventListener('wa-hide', (event) => {
// Track overlay click or Escape key dismissal
// Button clicks are tracked via CSS classes, so we only track non-button dismissals
if (event.detail?.source === dialog) {
trackEvent('launch_dialog:overlay_click');
}
// Save dismissal state to localStorage
try {
localStorage.setItem(SITE_DIALOG_DISMISSED_KEY, 'true');
} catch (e) {
// localStorage may be disabled or unavailable
}
}, { once: true });
// Show dialog after a short delay to ensure page is loaded
setTimeout(() => {
dialog.open = true;
}, 500);
};
customElements.whenDefined("wa-dialog").then(() => {
initDialog()
})
})();
</script>
{%- endif -%}
{% endraw %}

View File

@@ -1,5 +1,9 @@
<!DOCTYPE html>
<html lang="en" data-fa-kit-code="38c11e3f20" data-version="{{ package.version }}" class="wa-cloak">
{% if hasAnchors == undefined %}{% set hasAnchors = true %}{% endif %}
{% if hasBanner == undefined %}{% set hasBanner = true %}{% endif %}
{% if hasSiteDialog == undefined %}{% set hasSiteDialog = true %}{% endif %}
{% if hasGeneratedTitle == undefined %}{% set hasGeneratedTitle = true %}{% endif %}
<html lang="en" data-fa-kit-code="38c11e3f20" data-version="{{ package.version }}" class="wa-cloak"{% if hasAnchors == false %} data-no-anchor{% endif %}>
<head>
{% include 'head.njk' %}
<meta name="theme-color" content="#f36944">
@@ -27,9 +31,6 @@
</script>
</head>
<body class="layout-{{ layout | stripExtension }} page-{{ pageClass or page.fileSlug or 'home' }}{{ ' page-wide' if wide }}">
{% if hasBanner == undefined %}
{% set hasBanner = true %}
{% endif %}
{% set defaultWaPageAttributes = defaultWaPageAttributes or { view: 'desktop', 'disable-navigation-toggle': true, 'mobile-breakpoint': 1180, 'disable-sticky': 'banner' } %}
{% set waPageAttributes = waPageAttributes or {} %}
@@ -120,7 +121,9 @@
<div id="flashes">{% server "flashes" %}</div>
{% block header %}
<h1 class="title">{{ title }}</h1>
{% if hasGeneratedTitle %}
<h1 class="title">{{ title }}</h1>
{% endif %}
{% endblock %}
{% block beforeContent %}{% endblock %}
@@ -134,6 +137,11 @@
{% include 'search.njk' %}
{#- Site-Wide Dialog -#}
{% if hasSiteDialog %}
{% include "_dialog-wa-launch.njk" ignore missing %}
{% endif %}
{# Footer #}
{% block pageFooter %}{% endblock %}
</wa-page>

View File

@@ -0,0 +1,7 @@
{% macro freeBadge(params) %}
{% set description = params.description or "This feature is available in the free version of Web Awesome" %}
{% set badgeId = params.id or ("free-badge-" + ("" | uniqueId(8))) %}
<wa-badge appearance="filled" variant="neutral" pill class="free" id="{{ badgeId }}" data-free-badge>Free</wa-badge>
<wa-tooltip for="{{ badgeId }}">{{ description }}</wa-tooltip>
{% endmacro %}

View File

@@ -1,6 +1,6 @@
{% macro proBadge(params) %}
{% set description = params.description or "This requires access to Web Awesome Pro" %}
{% set badgeId = params.id or ("pro-badge-" + ("" | uniqueId(8))) %}
<wa-badge appearance="accent" pill class="pro" id="{{ badgeId }}">Pro</wa-badge>
<wa-badge appearance="accent" pill class="pro" id="{{ badgeId }}" data-pro-badge>Pro</wa-badge>
<wa-tooltip for="{{ badgeId }}">{{ description }}</wa-tooltip>
{% endmacro %}

View File

@@ -29,7 +29,7 @@
<li><a href="/docs/resources/contributing">Contributing</a></li>
<li><a href="/docs/resources/changelog">Changelog</a></li>
<li><a href="/docs/resources/visual-tests">Visual Tests</a></li>
</ul>
@@ -101,6 +101,7 @@
<li><a href="/docs/components/dropdown-item">Dropdown Item</a></li>
</ul>
</li>
<li><span class="is-planned wa-split">File Input <a href="https://github.com/shoelace-style/webawesome/issues/1240" target="_blank">{{ plannedBadge("A Web Awesome Kickstarter stretch goal!") }}</a></span></li>
<li><a href="/docs/components/format-bytes/">Format Bytes</a></li>
<li><a href="/docs/components/format-date/">Format Date</a></li>
<li><a href="/docs/components/format-number/">Format Number</a></li>
@@ -109,6 +110,7 @@
<li><a href="/docs/components/input/">Input</a></li>
<li><a href="/docs/components/intersection-observer">Intersection Observer</a></li>
<li><a href="/docs/components/mutation-observer/">Mutation Observer</a></li>
<li><span class="is-planned wa-split">Number Input <a href="https://github.com/shoelace-style/webawesome/issues/1688" target="_blank">{{ plannedBadge("A Web Awesome Kickstarter stretch goal!") }}</a></span></li>
<li><a href="/docs/components/popover/">Popover</a></li>
<li><a href="/docs/components/popup/">Popup</a></li>
<li><a href="/docs/components/progress-bar/">Progress Bar</a></li>
@@ -144,9 +146,15 @@
</li>
<li><a href="/docs/components/tag/">Tag</a></li>
<li><a href="/docs/components/textarea/">Textarea</a></li>
<li><span class="is-planned wa-split">Toast <a href="https://github.com/shoelace-style/webawesome/issues/105" target="_blank">{{ plannedBadge("A Web Awesome Kickstarter stretch goal!") }}</a></span></li>
<li><a href="/docs/components/tooltip/">Tooltip</a></li>
<li><a href="/docs/components/tree/">Tree</a></li>
<li><a href="/docs/components/tree-item/">Tree Item</a></li>
<li>
<a href="/docs/components/tree/">Tree</a>
<ul>
<li><a href="/docs/components/tree-item/">Tree Item</a></li>
</ul>
</li>
<li><span class="is-planned wa-split">Video <a href="https://github.com/shoelace-style/webawesome/issues/985" target="_blank">{{ plannedBadge("A Web Awesome Kickstarter stretch goal!") }}</a></span></li>
<li><a href="/docs/components/zoomable-frame">Zoomable Frame</a></li>
{# PLOP_NEW_COMPONENT_PLACEHOLDER #}
</ul>
@@ -365,9 +373,6 @@
<li>
<a href="/docs/patterns/layouts/ecommerce/">Ecommerce</a>
</li>
<li>
<a href="/docs/patterns/layouts/app/">App</a>
</li>
<li>
<a href="/docs/patterns/layouts/blog/">Blog</a>
</li>

View File

@@ -211,7 +211,12 @@
<tr>
<td class="table-name"><code>{{ state.name }}</code></td>
<td class="table-description">{{ state.description | inlineMarkdown | safe }}</td>
<td class="table-selector"><code>:state({{ state.name }})</code></td>
<td class="table-selector">
<span class="wa-cluster wa-gap-3xs">
<code>:state({{ state.name }})</code>
<wa-copy-button value=":state({{ state.name }})"></wa-copy-button>
</span>
</td>
</tr>
{% endfor %}
</tbody>
@@ -230,6 +235,7 @@
<tr>
<th class="table-name">Name</th>
<th class="table-description">Description</th>
<th class="table-selector">CSS selector</th>
</tr>
</thead>
<tbody>
@@ -237,6 +243,12 @@
<tr>
<td class="table-name"><code>{{ cssPart.name }}</code></td>
<td class="table-description">{{ cssPart.description | inlineMarkdown | safe }}</td>
<td class="table-selector">
<span class="wa-cluster wa-gap-3xs">
<code>::part({{ cssPart.name }})</code>
<wa-copy-button value="::part({{ cssPart.name }})"></wa-copy-button>
</span>
</td>
</tr>
{% endfor %}
</tbody>

View File

@@ -35,9 +35,23 @@ export function anchorHeadingsTransformer(options = {}) {
return doc;
}
// Look for headings
let selector = `:is(${options.headingSelector}):not([data-no-anchor], [data-no-anchor] *)`;
// Check if the document or container has data-no-anchor (view-level)
const hasNoAnchorOnDocument = doc.querySelector('html')?.hasAttribute('data-no-anchor') || false;
const hasNoAnchorOnContainer = container.closest('[data-no-anchor]') !== null;
// If view-level data-no-anchor is set, skip processing all headings
if (hasNoAnchorOnDocument || hasNoAnchorOnContainer) {
return doc;
}
// Look for headings (selector excludes headings with data-no-anchor attribute)
let selector = `:is(${options.headingSelector}):not([data-no-anchor])`;
container.querySelectorAll(selector).forEach(heading => {
// Skip if heading is a descendant of an element with data-no-anchor
// (selector already excludes headings with the attribute directly)
if (heading.closest('[data-no-anchor]') !== null) {
return;
}
const hasAnchor = heading.querySelector('a');
const existingId = heading.getAttribute('id');
const clone = parse(heading.outerHTML);
@@ -65,7 +79,7 @@ export function anchorHeadingsTransformer(options = {}) {
const anchor = parse(`
<a href="#${encodeURIComponent(id)}">
<span class="wa-visually-hidden"></span>
<span aria-hidden="true">#</span>
<wa-icon variant="regular" name="hashtag" class="icon-shrink"></wa-icon>
</a>
`);
anchor.querySelector('.wa-visually-hidden').textContent = options.anchorLabel;

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 27 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 27 KiB

View File

@@ -64,6 +64,24 @@ wa-page > [slot='banner'] {
}
}
/* Site-Wide Dialog */
#dialog-site {
/* custom brand colors carrried over from theme-site for the banner */
--wa-color-brand-95: #fef0ec;
--wa-color-brand-90: #fce0d8;
--wa-color-brand-80: #f8bcac;
--wa-color-brand-70: #fa9378;
--wa-color-brand-60: #f46a45;
--wa-color-brand-50: #cb4b27;
--wa-color-brand-40: #9d371a;
--wa-color-brand-30: #7c2a13;
--wa-color-brand-20: #5d1d0b;
--wa-color-brand-10: #3b0f05;
--wa-color-brand-05: #270802;
--wa-color-brand: var(--wa-color-brand-60);
--wa-color-brand-on: var(--wa-color-brand-10);
}
/* Header */
wa-page::part(header) {
background-color: var(--wa-color-surface-default);
@@ -351,26 +369,6 @@ h1.title {
}
}
/* Anchor headings */
.anchor-heading a {
visibility: hidden;
text-decoration: none;
}
@media (hover: hover) {
.anchor-heading:hover a {
visibility: visible;
padding: 0 0.125em;
}
}
@media print {
/* Show URLs for printed links */
a:not(.anchor-heading)[href]::after {
content: ' (' attr(href) ')';
}
}
/* Callouts */
.callout {
display: flex;
@@ -665,6 +663,10 @@ wa-scroller:has(.component-table) {
min-width: var(--line-length-xs);
}
.table-selector .wa-cluster {
flex-wrap: nowrap;
}
.table-reflect {
text-align: center;
}

View File

@@ -70,9 +70,8 @@
/* #region shared UI */
/* pro badge */
wa-badge.pro {
color: white;
color: var(--wa-color-brand-on-loud);
background-color: var(--wa-brand-orange);
border-color: var(--wa-brand-orange);
+ wa-tooltip {
font-size: var(--wa-font-size-xs);
@@ -80,6 +79,14 @@
}
}
/* free badge */
wa-badge.free {
+ wa-tooltip {
font-size: var(--wa-font-size-xs);
--max-width: unset;
}
}
/* planned badge */
wa-badge.planned {
background-color: var(--wa-color-neutral-fill-quiet);
@@ -123,6 +130,28 @@
}
}
/* anchor headings */
.anchor-heading a {
opacity: 0;
visibility: hidden;
text-decoration: none;
transition: opacity var(--wa-transition-normal) var(--wa-transition-easing);
}
@media (hover: hover) {
.anchor-heading:hover a {
opacity: 1;
visibility: visible;
padding: var(--wa-space-3xs);
}
}
@media print {
/* show URLs for printed links */
a:not(.anchor-heading)[href]::after {
content: ' (' attr(href) ')';
}
}
/* #endregion */
/* #region funsies + cosmetics */
@@ -149,76 +178,107 @@
}
}
/* grid background */
.background-grid {
--grid-spacing: var(--wa-space-2xl);
--grid-line-color: color-mix(in oklab, var(--wa-color-text-normal), transparent 90%);
--grid-line-width: var(--wa-border-width-s);
--subgrid-spacing: calc(var(--grid-spacing) / 2);
--subgrid-line-color: color-mix(in oklab, var(--wa-color-text-normal), transparent 95%);
--subgrid-line-width: var(--wa-border-width-s);
background-image:
/* main grid - vertical lines */
linear-gradient(to right, var(--grid-line-color) var(--grid-line-width), transparent var(--grid-line-width)),
/* main grid - horizontal lines */
linear-gradient(to bottom, var(--grid-line-color) var(--grid-line-width), transparent var(--grid-line-width)),
/* sub-grid - vertical lines (offset by half the main grid spacing) */
linear-gradient(
to right,
var(--subgrid-line-color) var(--subgrid-line-width),
transparent var(--subgrid-line-width)
),
/* sub-grid - horizontal lines (offset by half the main grid spacing) */
linear-gradient(
to bottom,
var(--subgrid-line-color) var(--subgrid-line-width),
transparent var(--subgrid-line-width)
);
background-size:
var(--grid-spacing) var(--grid-spacing),
var(--grid-spacing) var(--grid-spacing),
var(--subgrid-spacing) var(--subgrid-spacing),
var(--subgrid-spacing) var(--subgrid-spacing);
background-position:
0 0,
0 0,
calc(var(--grid-spacing) / 2) calc(var(--grid-spacing) / 2),
calc(var(--grid-spacing) / 2) calc(var(--grid-spacing) / 2);
}
/* dot grid background */
.background-dot-grid {
--dot-spacing: 1.5rem;
--dot-radius: 1.5px;
--dot-color: color-mix(in oklab, var(--wa-color-text-normal), transparent 85%);
background-image: radial-gradient(circle, var(--dot-color) var(--dot-radius), transparent var(--dot-radius));
background-size: var(--dot-spacing) var(--dot-spacing);
}
/* wa illustration background pattern */
.background-wa-pattern {
/* background overlay base */
.background-overlay,
.background-overlay-grid,
.background-overlay-dot-grid,
.background-overlay-wa-pattern {
--background-blend-mode: normal;
--background-blend-mode-dark: var(--background-blend-mode);
--background-color: transparent;
--background-overlay-content-z-index: 1;
--background-overlay-z-index: 0;
position: relative;
& > * {
position: relative;
z-index: 1;
z-index: var(--background-overlay-content-z-index);
}
&::before,
&::after {
background-color: var(--background-color);
content: '';
inset: 0;
mix-blend-mode: var(--background-blend-mode);
position: absolute;
z-index: var(--background-overlay-z-index);
}
.wa-dark & {
&::before,
&::after {
mix-blend-mode: var(--background-blend-mode-dark);
}
}
}
/* grid background overlay */
.background-overlay-grid {
--grid-line-color: var(--wa-color-text-normal);
--grid-line-width: var(--wa-border-width-s);
--grid-opacity: 0.1;
--grid-spacing: var(--wa-space-2xl);
--subgrid-line-color: var(--grid-line-color);
--subgrid-line-width: var(--grid-line-width);
--subgrid-opacity: 0.05;
--subgrid-spacing: calc(var(--grid-spacing) / 2);
&::before {
background-image:
linear-gradient(to right, var(--grid-line-color) var(--grid-line-width), transparent var(--grid-line-width)),
linear-gradient(to bottom, var(--grid-line-color) var(--grid-line-width), transparent var(--grid-line-width));
background-size: var(--grid-spacing) var(--grid-spacing);
opacity: var(--grid-opacity);
}
&::after {
--background-pattern-image: url('/assets/images/bg-wa-pattern.svg');
background-image:
linear-gradient(to right, var(--subgrid-line-color) var(--subgrid-line-width), transparent var(--subgrid-line-width)),
linear-gradient(to bottom, var(--subgrid-line-color) var(--subgrid-line-width), transparent var(--subgrid-line-width));
background-position: calc(var(--grid-spacing) / 2) calc(var(--grid-spacing) / 2);
background-size: var(--subgrid-spacing) var(--subgrid-spacing);
opacity: var(--subgrid-opacity);
}
position: absolute;
inset: 0;
background-color: var(--background-pattern-color, transparent);
background-image: var(--background-pattern-image);
background-repeat: repeat;
content: '';
opacity: var(--background-pattern-opacity, 0.3);
z-index: 0;
.wa-dark & {
--grid-opacity: 0.15;
--subgrid-opacity: 0.08;
}
}
/* dot grid background overlay */
.background-overlay-dot-grid {
--dot-color: var(--wa-color-text-normal);
--dot-opacity: 0.15;
--dot-radius: 1.5px;
--dot-spacing: 1.5rem;
&::after {
background-image: radial-gradient(circle, var(--dot-color) var(--dot-radius), transparent var(--dot-radius));
background-size: var(--dot-spacing) var(--dot-spacing);
opacity: var(--dot-opacity);
}
.wa-dark & {
--dot-opacity: 0.2;
}
}
/* wa illustration background pattern overlay */
.background-overlay-wa-pattern {
--background-image: url('/assets/images/bg-wa-pattern.svg');
--background-opacity: 0.15;
--background-size: 640px;
&::after {
background-image: var(--background-image);
background-size: var(--background-size);
opacity: var(--background-opacity);
}
.wa-dark & {
--background-opacity: 0.2;
}
}
/* #endregion */

View File

@@ -141,7 +141,7 @@ Dropdowns can be placed into button groups.
### Split Buttons
Create a split button using a button and a dropdown. Use a [visually hidden](/docs/components/visually-hidden) label to ensure the dropdown is accessible to users with assistive devices.
Create a split button using a button and a dropdown. Use a [visually hidden](/docs/utilities/visually-hidden) label to ensure the dropdown is accessible to users with assistive devices.
```html {.example}
<wa-button-group label="Example Button Group">

View File

@@ -5,7 +5,7 @@ layout: component
category: Imagery
---
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/)).
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 page](/docs/themes)).
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.)

View File

@@ -53,9 +53,9 @@ The default orientation for dividers is `horizontal`. Set `orientation` attribut
</div>
```
### Menu Dividers
### Dropdown Dividers
Use dividers in [menus](/docs/components/menu) to visually group menu items.
Use dividers in [dropdowns](/docs/components/dropdown) to visually group dropdown items.
```html {.example}
<wa-dropdown style="max-width: 200px;">

View File

@@ -102,7 +102,7 @@ The default orientation for radio items is `vertical`. Set the `orientation` to
### Sizing Options
The size of [Radios](/docs/components/radio) will be determined by the Radio Group's `size` attribute.
The size of radios will be determined by the Radio Group's `size` attribute.
```html {.example}
<wa-radio-group label="Select an option" size="medium" value="medium" onchange="this.size = this.value">
@@ -113,7 +113,7 @@ The size of [Radios](/docs/components/radio) will be determined by the Radio Gro
```
:::info
[Radios](/docs/components/radio) and [Radio Buttons](/docs/components/radio-button) also have a `size` attribute,
[Radios](/docs/components/radio) also have a `size` attribute,
which will override the inherited size when used.
:::

View File

@@ -1,15 +1,20 @@
---
title: Changelog
dateLastUpdated: 2025-11-07
description: Changes to each version of the project are documented here.
layout: page-outline
---
<p class="wa-caption-s">Last updated: <wa-format-date month="long" day="numeric" year="numeric" date="{{ lastUpdatedISO }}"></wa-format-date></p>
<p class="wa-caption-s">Last updated: <wa-format-date month="long" day="numeric" year="numeric" date="{{ dateLastUpdated }}"></wa-format-date></p>
Web Awesome follows [Semantic Versioning](https://semver.org/). Breaking changes in components with the <wa-badge variant="brand">Stable</wa-badge> badge will not be accepted until the next major version. As such, all contributions must consider the project's roadmap and take this into consideration. Features that are deemed no longer necessary will be deprecated but not removed.
Components with the <wa-badge variant="warning">Experimental</wa-badge> badge should not be used in production. They are made available as release candidates for development and testing purposes. As such, changes to experimental components will not be subject to semantic versioning.
## Next
- Fixed a bug in `<wa-slider>` that caused some touch devices to end up with the incorrect value [issue:1703]
## 3.0.0
- 🚨 BREAKING: Changed `appearance="filled outlined"` to `appearance="filled-outlined"` in the following elements [issue:1127]
@@ -245,7 +250,7 @@ Many of these changes and improvements were the direct result of feedback from u
- 🚨 BREAKING: Renamed `<image-comparer>` to `<wa-comparison>` and improved compatibility for non-image content
- 🚨 BREAKING: Added slot detection to `<wa-dialog>` and `<wa-drawer>` so you don't need to specify `with-header` and `with-footer`; headers are on by default now, but you can use the `without-header` attribute to turn them off
- 🚨 BREAKING: Renamed the `image` slot to `media` for a more appropriate naming convention
- Added [a theme builder](/docs/themes/edit/) to create your own themes
- Added Theme Builder to create your own themes
- Added a new Blog & News pattern category
- Added a new free component: `<wa-scroller>` (#1 of 14 per stretch goals)
- Added support for Duotone Thin, Light, and Regular styles and the Sharp Duotone family of styles to `<wa-icon>`
@@ -262,7 +267,7 @@ Many of these changes and improvements were the direct result of feedback from u
### Enhancements {data-no-outline}
- Added `appearance` to [`<wa-details>`](/docs/components/details) and [`<wa-card>`](/docs/components/card) and support for the [appearance utilities](/docs/utilities/appearance/) in the [`<details>` native styles](/docs/utilities/native/details).
- Added `appearance` to [`<wa-details>`](/docs/components/details) and [`<wa-card>`](/docs/components/card) and support for the appearance utilities in the [`<details>` native styles](/docs/utilities/native/#details).
- Added an `orange` scale to all color palettes
- Added the [`.wa-cloak` utility](/docs/utilities/fouce) to prevent FOUCE
- Added the [`allDefined()` utility](/docs/usage/#all-defined) for awaiting component registration
@@ -287,7 +292,7 @@ Many of these changes and improvements were the direct result of feedback from u
- Revert `<wa-dialog>` structure and CSS to fix clipped content in dialogs (WA-A #123) and light dismiss in iOS Safari (WA-A #201)
- Fixed a bug in `<wa-color-picker>` that prevented light dismiss from working when clicking immediately above the color picker dropdown
- Fixed a bug in `<wa-progress>` that prevented Safari from animation progress changes
- Fixed the missing indeterminate icon in [native checkbox styles](/docs/utilities/native/checkbox)
- Fixed the missing indeterminate icon in [native checkbox styles](/docs/utilities/native/#form-controls)
- Fixed a bug in `<wa-radio>` where elements would stack instead of display inline
- Docs fixes:
- Fixed the search dialog's styles so it doesn't jump around as you search
@@ -434,13 +439,13 @@ Many of these changes and improvements were the direct result of feedback from u
### Native styles {data-no-outline}
- Added native styles for
[buttons](/docs/utilities/native/button),
[input fields](/docs/utilities/native/input),
[dialogs](/docs/utilities/native/dialog),
[details](/docs/utilities/native/details),
[tables](/docs/utilities/native/table),
[lists](/docs/utilities/native/lists),
and most [content elements](/docs/utilities/native/content).
[buttons](/docs/utilities/native/#buttons),
[input fields](/docs/utilities/native/#form-controls),
[dialogs](/docs/utilities/native/#dialog),
[details](/docs/utilities/native/#details),
[tables](/docs/utilities/native/#tables),
[lists](/docs/utilities/native/#lists),
and most [content elements](/docs/utilities/native/#typography).
### Style utilities {data-no-outline}
@@ -448,7 +453,7 @@ Many of these changes and improvements were the direct result of feedback from u
- Added [appearance utilities](/docs/utilities/appearance/)
- Added [size utilities](/docs/utilities/size/)
- Added [layout utilities](/docs/layout/#utilities)
- Added [`.wa-visually hidden`](/docs/utilities/a11y/#visually-hidden) utility
- Added [`.wa-visually hidden`](/docs/utilities/visually-hidden) utility
- Added [`<wa-page>`](/docs/components/page/#styles) native styles and utilities
### Components {data-no-outline}
@@ -492,4 +497,4 @@ Many of these changes and improvements were the direct result of feedback from u
</details>
Did we miss something? [Let us know!](https://github.com/shoelace-style/webawesome/discussions)
Did we miss something? [Let us know!](https://github.com/shoelace-style/webawesome/discussions)

View File

@@ -164,7 +164,7 @@ This section can be a lot to digest in one sitting, so don't feel like you need
Web Awesome is built with accessibility in mind. Creating generic components that are fully accessible to users with varying capabilities across a multitude of circumstances is a daunting challenge. Oftentimes, the solution to an a11y problem is not written in black and white and, therefore, we may not get it right the first time around. There are, however, guidelines we can follow in our effort to make Web Awesome an accessible foundation from which applications and websites can be built.
We take this commitment seriously, so please ensure your contributions have this goal in mind. If you need help with anything a11y-related, please [reach out to the community](/resources/community) for assistance. If you discover an accessibility concern within the library, please file a bug on the [issue tracker](https://github.com/shoelace-style/webawesome/issues).
We take this commitment seriously, so please ensure your contributions have this goal in mind. If you need help with anything a11y-related, please reach out on the [community chat](https://discord.gg/mg8f26C) for assistance. If you discover an accessibility concern within the library, please file a bug on the [issue tracker](https://github.com/shoelace-style/webawesome/issues).
It's important to remember that, although accessibility starts with foundational components, it doesn't end with them. It everyone's responsibility to encourage best practices and ensure we're providing an optimal experience for all of our users.
@@ -364,7 +364,7 @@ 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 and use the [system library](/docs/components/icon#customizing-the-system-library):
```html
<wa-icon library="system" name="..." variant="..."></wa-icon>

View File

@@ -11,7 +11,7 @@ layout: page
<div class="wa-stack wa-gap-xl">
<div class="wa-stack wa-align-items-start">
<div class="wa-stack wa-gap-s">
<h2 class="anchor-heading wa-cluster wa-gap-xs" data-no-anchor><wa-icon name="github" family="brands" style="font-size: 2ch;"></wa-icon>Github</h2>
<h2 class="anchor-heading wa-cluster wa-gap-xs" data-no-anchor><wa-icon name="github" family="brands" style="font-size: 2ch;"></wa-icon>GitHub</h2>
<span class="wa-heading-m">Feature requests & bugs</span>
<p class="wa-caption-l">Notice a bug or have an idea? Open an issue on GitHub so we can triage, track, and ship fixes.</p>
</div>

View File

@@ -4,6 +4,9 @@ description: Style (and restyle) your website at will with any of Web Awesome's
layout: page
---
{% from "pro-badge.njk" import proBadge %}
{% from "free-badge.njk" import freeBadge %}
<h1>{{ title }}</h1>
<p>Themes are collections of <a href="/docs/tokens">design tokens</a> that give a cohesive look and feel to the entire Web Awesome library. Style and restyle your website at will by loading any pre-built theme.</p>
@@ -64,8 +67,9 @@ to create a project with any one of these themes.
<header class="wa-stack">
<div class="wa-cluster">
<h2 data-theme-name="name">Theme</h2>
<wa-badge data-free-badge appearance="outlined" variant="neutral" hidden>FREE</wa-badge>
<wa-badge data-pro-badge appearance="accent" hidden>PRO</wa-badge>
{{ freeBadge({ id: "free-badge", description: "This theme is available in the free version of Web Awesome." }) }}
{{ proBadge({ id: "pro-badge", description: "This theme requires access to Web Awesome Pro." }) }}
</div>
<p data-theme-description>Description</p>
</header>
@@ -162,8 +166,12 @@ Then apply the following classes to the `<html>` element:
const beforeFrame = document.querySelector('wa-zoomable-frame[slot="before"]');
const nameElement = document.querySelector('[data-theme-name]');
const descriptionElement = document.querySelector('[data-theme-description]');
const freeBadge = document.querySelector('[data-free-badge]');
const proBadge = document.querySelector('[data-pro-badge]');
const freeBadge = document.getElementById('free-badge');
const proBadge = document.getElementById('pro-badge');
// Hide both badges initially
if (freeBadge) freeBadge.hidden = true;
if (proBadge) proBadge.hidden = true;
function updateFrames(selectedValue, title, description, isPro, palette, brand) {
// Update theme classes on both frames

View File

@@ -4,6 +4,173 @@ description: Learn more about using custom elements.
layout: page-outline
---
<!-- WA Pattern Background Examples -->
<div class="example">
<h3>WA Pattern: Default</h3>
<div class="background-overlay-wa-pattern example-content"
style="--background-color: var(--wa-color-neutral-border-quiet);">
<p>Default settings with normal blend mode</p>
</div>
</div>
<div class="example">
<h3>WA Pattern: Custom Opacity (Subtle)</h3>
<div class="background-overlay-wa-pattern example-content"
style="--background-color: var(--wa-color-neutral-border-quiet);
--background-opacity: 0.1;">
<p>Very subtle pattern with 0.1 opacity</p>
</div>
</div>
<div class="example">
<h3>WA Pattern: Custom Opacity (Prominent)</h3>
<div class="background-overlay-wa-pattern example-content"
style="--background-color: var(--wa-color-neutral-border-quiet);
--background-opacity: 0.3;">
<p>More prominent pattern with 0.3 opacity</p>
</div>
</div>
<div class="example">
<h3>WA Pattern: Background Color - Brand</h3>
<div class="background-overlay-wa-pattern example-content"
style="--background-color: var(--wa-color-brand-border-normal);">
<p>Pattern with brand color - automatically blends with multiply (light) or screen (dark)</p>
</div>
</div>
<div class="example">
<h3>WA Pattern: Multiply/Screen Blend Mode</h3>
<div class="background-overlay-wa-pattern example-content"
style="--background-blend-mode: multiply;
--background-blend-mode-dark: screen;
--background-color: var(--wa-color-neutral-border-quiet);
--background-opacity: 0.15;">
<p>Multiply blend mode in light mode, automatically switches to screen blend mode in dark mode. Set both <code>--background-blend-mode: multiply</code> and <code>--background-blend-mode-dark: screen</code>.</p>
</div>
</div>
<div class="example">
<h3>WA Pattern: Grayscale (Luminosity Blend Mode)</h3>
<div class="background-overlay-wa-pattern example-content"
style="--background-blend-mode: luminosity;
--background-color: transparent;
--background-opacity: 0.2;">
<p>Luminosity blend mode creates a grayscale effect that uses the pattern's brightness to modulate the content behind it, while preserving the color of the background content.</p>
</div>
</div>
<!-- Grid Background Examples -->
<div class="example">
<h3>Grid: Default</h3>
<div class="background-overlay-grid example-content">
<p>Default settings with normal blend mode</p>
</div>
</div>
<div class="example">
<h3>Grid: Custom Opacity (Subtle)</h3>
<div class="background-overlay-grid example-content"
style="--grid-opacity: 0.05;
--subgrid-opacity: 0.025;">
<p>Very subtle grid with reduced opacity</p>
</div>
</div>
<div class="example">
<h3>Grid: Custom Opacity (Prominent)</h3>
<div class="background-overlay-grid example-content"
style="--grid-opacity: 0.2;
--subgrid-opacity: 0.1;">
<p>More prominent grid with increased opacity</p>
</div>
</div>
<div class="example">
<h3>Grid: Background Color - Brand</h3>
<div class="background-overlay-grid example-content"
style="--background-color: var(--wa-color-brand-fill-quiet);
--grid-line-color: var(--wa-color-brand-border-normal);
--subgrid-line-color: var(--wa-color-brand-border-normal);">
<p>Grid with brand background and line colors - automatically blends with multiply (light) or screen (dark)</p>
</div>
</div>
<div class="example">
<h3>Grid: Multiply/Screen Blend Mode</h3>
<div class="background-overlay-grid example-content"
style="--background-blend-mode: multiply;
--background-blend-mode-dark: screen;
--grid-opacity: 0.15;
--subgrid-opacity: 0.08;">
<p>Multiply blend mode in light mode, automatically switches to screen blend mode in dark mode. Set both <code>--background-blend-mode: multiply</code> and <code>--background-blend-mode-dark: screen</code>.</p>
</div>
</div>
<div class="example">
<h3>Grid: Grayscale (Luminosity Blend Mode)</h3>
<div class="background-overlay-grid example-content"
style="--background-blend-mode: luminosity;
--background-color: transparent;
--grid-opacity: 0.2;
--subgrid-opacity: 0.1;">
<p>Luminosity blend mode creates a grayscale effect that uses the grid's brightness to modulate the content behind it, while preserving the color of the background content.</p>
</div>
</div>
<!-- Dot Grid Background Examples -->
<div class="example">
<h3>Dot Grid: Default</h3>
<div class="background-overlay-dot-grid example-content">
<p>Default settings with normal blend mode</p>
</div>
</div>
<div class="example">
<h3>Dot Grid: Custom Opacity (Subtle)</h3>
<div class="background-overlay-dot-grid example-content"
style="--dot-opacity: 0.05;">
<p>Very subtle dots with 0.05 opacity</p>
</div>
</div>
<div class="example">
<h3>Dot Grid: Custom Opacity (Prominent)</h3>
<div class="background-overlay-dot-grid example-content"
style="--dot-opacity: 0.3;">
<p>More prominent dots with 0.3 opacity</p>
</div>
</div>
<div class="example">
<h3>Dot Grid: Background Color - Brand</h3>
<div class="background-overlay-dot-grid example-content"
style="--background-color: var(--wa-color-brand-fill-quiet);
--dot-color: var(--wa-color-brand-border-normal);">
<p>Dots with brand background and dot colors - automatically blends with multiply (light) or screen (dark)</p>
</div>
</div>
<div class="example">
<h3>Dot Grid: Multiply/Screen Blend Mode</h3>
<div class="background-overlay-dot-grid example-content"
style="--background-blend-mode: multiply;
--background-blend-mode-dark: screen;
--dot-opacity: 0.2;">
<p>Multiply blend mode in light mode, automatically switches to screen blend mode in dark mode. Set both <code>--background-blend-mode: multiply</code> and <code>--background-blend-mode-dark: screen</code>.</p>
</div>
</div>
<div class="example">
<h3>Dot Grid: Grayscale (Luminosity Blend Mode)</h3>
<div class="background-overlay-dot-grid example-content"
style="--background-blend-mode: luminosity;
--background-color: transparent;
--dot-opacity: 0.2;">
<p>Luminosity blend mode creates a grayscale effect that uses the dot grid's brightness to modulate the content behind it, while preserving the color of the background content.</p>
</div>
</div>
Web Awesome components are just regular HTML elements, or [custom elements](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements) to be precise. You can use them like any other element. Each component has detailed documentation that describes its full API, including properties, events, methods, and more.
If you're new to custom elements, often referred to as "web components," this section will familiarize you with how to use them.
@@ -225,3 +392,4 @@ If you are using types from multiple projects, you can add an array of reference
### Other Editors
Most popular editors support custom code completion with a bit of configuration. Please [submit a feature request](https://github.com/shoelace-style/webawesome/issues/new/choose) for your editor of choice. PRs are also welcome!

View File

@@ -84,7 +84,7 @@ Clusters are great for inline lists and aligning items of varying sizes.
## Align Items
By default, items are centered in the block direction of the `wa-cluster` container. You can add any of the following [`wa-align-items-*`](/docs/style-utilities/align-items) classes to an element with `wa-cluster` to specify how items are aligned in the block direction:
By default, items are centered in the block direction of the `wa-cluster` container. You can add any of the following [`wa-align-items-*`](/docs/utilities/align-items) classes to an element with `wa-cluster` to specify how items are aligned in the block direction:
- `wa-align-items-start`
- `wa-align-items-end`
@@ -119,7 +119,7 @@ By default, items are centered in the block direction of the `wa-cluster` contai
## Gap
By default, the gap between cluster items uses `--wa-space-m` from your theme. You can add any of the following [`wa-gap-*`](/docs/style-utilities/gap) classes to an element with `wa-cluster` to specify the gap between items:
By default, the gap between cluster items uses `--wa-space-m` from your theme. You can add any of the following [`wa-gap-*`](/docs/utilities/gap) classes to an element with `wa-cluster` to specify the gap between items:
- `wa-gap-0`
- `wa-gap-3xs`

View File

@@ -137,7 +137,7 @@ The main content fills the remaining inline space of the container. By default,
## Align Items
By default, items are centered in the block direction of the `wa-flank` container. You can add any of the following [`wa-align-items-*`](/docs/style-utilities/align-items) classes to an element with `wa-flank` to specify how items are aligned in the block direction:
By default, items are centered in the block direction of the `wa-flank` container. You can add any of the following [`wa-align-items-*`](/docs/utilities/align-items) classes to an element with `wa-flank` to specify how items are aligned in the block direction:
- `wa-align-items-start`
- `wa-align-items-end`
@@ -168,7 +168,7 @@ By default, items are centered in the block direction of the `wa-flank` containe
## Gap
By default, the gap between flank items uses `--wa-space-m` from your theme. You can add any of the following [`wa-gap-*`](/docs/style-utilities/gap) classes to an element with `wa-flank` to specify the gap between items:
By default, the gap between flank items uses `--wa-space-m` from your theme. You can add any of the following [`wa-gap-*`](/docs/utilities/gap) classes to an element with `wa-flank` to specify the gap between items:
- `wa-gap-0`
- `wa-gap-3xs`

View File

@@ -139,7 +139,7 @@ Frames have a square aspect ratio by default. You can append `:square` (1 / 1),
## Border Radius
Frames have a square border radius by default. You can add any of the following [`wa-border-radius-*`](/docs/style-utilities/border-radius) classes to an element with `wa-frame` to specify the border radius:
Frames have a square border radius by default. You can add any of the following [`wa-border-radius-*`](/docs/utilities/rounding) classes to an element with `wa-frame` to specify the border radius:
- `wa-border-radius-s`
- `wa-border-radius-m`

View File

@@ -14,7 +14,7 @@ tags: layoutUtilities
}
</style>
Web Awesome includes classes to set the `gap` property of flex and grid containers. They can be used alongside other Web Awesome layout utilities, like [cluster](/docs/layout/cluster) and [stack](/docs/layout/stack), to change the space between items.
Web Awesome includes classes to set the `gap` property of flex and grid containers. They can be used alongside other Web Awesome layout utilities, like [cluster](/docs/utilities/cluster) and [stack](/docs/utilities/stack), to change the space between items.
Or even by themselves — all gap properties also set `display: flex` with a specificity of 0 so that it can be trivially overridden.
Besides `wa-gap-0`, which sets `gap` to zero, each class corresponds to one of the [`--wa-space-*`](/docs/tokens/space) tokens in your theme.

View File

@@ -169,7 +169,7 @@ By default, grid items will wrap when the grid's column size is less than `20ch`
## Gap
By default, the gap between grid items uses `--wa-space-m` from your theme. You can add any of the following [`wa-gap-*`](/docs/style-utilities/gap) classes to an element with `wa-grid` to specify the gap between items:
By default, the gap between grid items uses `--wa-space-m` from your theme. You can add any of the following [`wa-gap-*`](/docs/utilities/gap) classes to an element with `wa-grid` to specify the gap between items:
- `wa-gap-0`
- `wa-gap-3xs`

View File

@@ -12,7 +12,7 @@ tags: styleUtilities
}
</style>
Web Awesome includes classes to set an element's `border-radius` property. They can be used alongside Web Awesome layout utilities, like [frame](/docs/layout/frame), to round all corners of an element.
Web Awesome includes classes to set an element's `border-radius` property. They can be used alongside Web Awesome layout utilities, like [frame](/docs/utilities/frame), to round all corners of an element.
Each class corresponds to one of the [`--wa-border-radius-*`](/docs/tokens/borders/#radius) tokens in your theme.

View File

@@ -106,7 +106,7 @@ Items can be split across a row or a column by appending `:row` or `:column` to
## Align Items
By default, items are centered on the cross axis of the `wa-split` container. You can add any of the following [`wa-align-items-*`](/docs/style-utilities/align-items) classes to an element with `wa-split` to specify how items are aligned:
By default, items are centered on the cross axis of the `wa-split` container. You can add any of the following [`wa-align-items-*`](/docs/utilities/align-items) classes to an element with `wa-split` to specify how items are aligned:
- `wa-align-items-start`
- `wa-align-items-end`
@@ -139,7 +139,7 @@ These modifiers specify how items are aligned in the block direction for `wa-spl
## Gap
A split's gap determines how close items can be before they wrap. By default, the gap between split items uses `--wa-space-m` from your theme. You can add any of the following [`wa-gap-*`](/docs/style-utilities/gap) classes to an element with `wa-split` to specify the gap between items:
A split's gap determines how close items can be before they wrap. By default, the gap between split items uses `--wa-space-m` from your theme. You can add any of the following [`wa-gap-*`](/docs/utilities/gap) classes to an element with `wa-split` to specify the gap between items:
- `wa-gap-0`
- `wa-gap-3xs`

View File

@@ -62,7 +62,7 @@ Stacks are well suited for forms, text, and ensuring consistent spacing between
## Align Items
By default, items stretch to fill the inline size of the `wa-stack` container. You can add any of the following [`wa-align-items-*`](/docs/style-utilities/align-items) classes to an element with `wa-stack` to specify how items are aligned in the inline direction:
By default, items stretch to fill the inline size of the `wa-stack` container. You can add any of the following [`wa-align-items-*`](/docs/utilities/align-items) classes to an element with `wa-stack` to specify how items are aligned in the inline direction:
- `wa-align-items-start`
- `wa-align-items-end`
@@ -92,7 +92,7 @@ By default, items stretch to fill the inline size of the `wa-stack` container. Y
## Gap
By default, the gap between stack items uses `--wa-space-m` from your theme. You can add any of the following [`wa-gap-*`](/docs/style-utilities/gap) classes to an element with `wa-stack` to specify the gap between items:
By default, the gap between stack items uses `--wa-space-m` from your theme. You can add any of the following [`wa-gap-*`](/docs/utilities/gap) classes to an element with `wa-stack` to specify the gap between items:
- `wa-gap-0`
- `wa-gap-3xs`

View File

@@ -4,7 +4,7 @@
"access": "public"
},
"description": "A forward-thinking library of web components.",
"version": "3.0.0-beta.6",
"version": "3.0.0",
"homepage": "https://webawesome.com/",
"author": "Web Awesome",
"license": "MIT",

View File

@@ -3,7 +3,6 @@ import { html } from 'lit';
import { customElement, property, query, state } from 'lit/decorators.js';
import { classMap } from 'lit/directives/class-map.js';
import WebAwesomeElement from '../../internal/webawesome-element.js';
import variantStyles from '../../styles/utilities/variants.css';
import type WaButton from '../button/button.js';
import styles from './button-group.css';
@@ -19,7 +18,7 @@ import styles from './button-group.css';
*/
@customElement('wa-button-group')
export default class WaButtonGroup extends WebAwesomeElement {
static css = [variantStyles, styles];
static css = [styles];
@query('slot') defaultSlot: HTMLSlotElement;
@@ -35,9 +34,6 @@ export default class WaButtonGroup extends WebAwesomeElement {
/** The button group's orientation. */
@property({ reflect: true }) orientation: 'horizontal' | 'vertical' = 'horizontal';
/** The button group's theme variant. Defaults to `neutral` if not within another element with a variant. */
@property({ reflect: true }) variant: 'neutral' | 'brand' | 'success' | 'warning' | 'danger' = 'neutral';
updated(changedProperties: PropertyValues<this>) {
super.updated(changedProperties);

View File

@@ -174,7 +174,7 @@
}
:host([appearance='button']:state(checked):focus-visible) {
outline: var(--wa-focus-ring-style) var(--wa-focus-ring-width) var(--wa-color-brand-border-loud);
outline: var(--wa-focus-ring);
outline-offset: var(--wa-focus-ring-offset);
}

View File

@@ -84,8 +84,8 @@ export class DraggableElement {
}
private handleDragStart = (event: PointerEvent | TouchEvent) => {
const clientX = supportsTouch && 'touches' in event ? event.touches[0].clientX : (event as PointerEvent).clientX;
const clientY = supportsTouch && 'touches' in event ? event.touches[0].clientY : (event as PointerEvent).clientY;
const clientX = 'touches' in event ? event.touches[0].clientX : (event as PointerEvent).clientX;
const clientY = 'touches' in event ? event.touches[0].clientY : (event as PointerEvent).clientY;
if (
this.isDragging ||
@@ -105,8 +105,8 @@ export class DraggableElement {
};
private handleDragStop = (event: PointerEvent | TouchEvent) => {
const clientX = supportsTouch && 'touches' in event ? event.touches[0].clientX : (event as PointerEvent).clientX;
const clientY = supportsTouch && 'touches' in event ? event.touches[0].clientY : (event as PointerEvent).clientY;
const clientX = 'touches' in event ? event.touches[0].clientX : (event as PointerEvent).clientX;
const clientY = 'touches' in event ? event.touches[0].clientY : (event as PointerEvent).clientY;
this.isDragging = false;
document.removeEventListener('pointerup', this.handleDragStop);
@@ -117,8 +117,8 @@ export class DraggableElement {
};
private handleDragMove = (event: PointerEvent | TouchEvent) => {
const clientX = supportsTouch && 'touches' in event ? event.touches[0].clientX : (event as PointerEvent).clientX;
const clientY = supportsTouch && 'touches' in event ? event.touches[0].clientY : (event as PointerEvent).clientY;
const clientX = 'touches' in event ? event.touches[0].clientX : (event as PointerEvent).clientX;
const clientY = 'touches' in event ? event.touches[0].clientY : (event as PointerEvent).clientY;
// Prevent text selection while dragging
window.getSelection()?.removeAllRanges();