From 79bafc513ad07dccc6134794198518b30d8f9051 Mon Sep 17 00:00:00 2001 From: Konnor Rogers Date: Tue, 18 Mar 2025 13:04:24 -0400 Subject: [PATCH 01/23] 11ty for webawesome-app (#803) * working on integration * 11ty for webawesome + app * add flashes * additional changes * prettier * add note about nunjucks * prettier --- docs/.eleventy.js | 22 +++++++++++++++++++--- docs/_includes/base.njk | 8 ++++++++ docs/_includes/head.njk | 4 ++++ docs/_layouts/page.njk | 9 +++++++-- docs/index.md | 4 +--- scripts/build.js | 38 ++++++++++++++++++++++++++++++++++++++ 6 files changed, 77 insertions(+), 8 deletions(-) diff --git a/docs/.eleventy.js b/docs/.eleventy.js index c32049193..29d755be3 100644 --- a/docs/.eleventy.js +++ b/docs/.eleventy.js @@ -1,3 +1,4 @@ +import * as path from 'node:path'; import { anchorHeadingsPlugin } from './_utils/anchor-headings.js'; import { codeExamplesPlugin } from './_utils/code-examples.js'; import { copyCodePlugin } from './_utils/copy-code.js'; @@ -16,7 +17,10 @@ import { searchPlugin } from './_utils/search.js'; import process from 'process'; -const packageData = JSON.parse(await readFile('./package.json', 'utf-8')); +import * as url from 'url'; +const __dirname = url.fileURLToPath(new URL('.', import.meta.url)); + +const packageData = JSON.parse(await readFile(path.join(__dirname, '..', 'package.json'), 'utf-8')); const isAlpha = process.argv.includes('--alpha'); const isDev = process.argv.includes('--develop'); @@ -24,6 +28,12 @@ const globalData = { package: packageData, isAlpha, layout: 'page.njk', + + server: { + head: '', + loginOrAvatar: '', + flashes: '', + }, }; const passThroughExtensions = ['js', 'css', 'png', 'svg', 'jpg', 'mp4']; @@ -55,7 +65,12 @@ export default function (eleventyConfig) { // Shortcodes - {% shortCode arg1, arg2 %} eleventyConfig.addShortcode('cdnUrl', location => { - return `https://early.webawesome.com/webawesome@${packageData.version}/dist/` + location.replace(/^\//, ''); + return `https://early.webawesome.com/webawesome@${packageData.version}/dist/` + (location || '').replace(/^\//, ''); + }); + + // Turns `{% server_variable "foo" %} into `{{ server.foo | safe }}` + eleventyConfig.addShortcode('server', function (property) { + return `{{ server.${property} | safe }}`; }); // Paired shortcodes - {% shortCode %}content{% endShortCode %} @@ -131,7 +146,8 @@ export default function (eleventyConfig) { .filter(component => !omittedModules.includes(component.tagName.split(/wa-/)[1])) .map(component => { const name = component.tagName.split(/wa-/)[1]; - return `./dist/components/${name}/${name}.js`; + const componentDirectory = process.env.UNBUNDLED_DIST_DIRECTORY || path.join('.', 'dist'); + return path.join(componentDirectory, 'components', name, `${name}.js`); }); eleventyConfig.addPlugin(litPlugin, { diff --git a/docs/_includes/base.njk b/docs/_includes/base.njk index 0a01532bf..6cbfea2de 100644 --- a/docs/_includes/base.njk +++ b/docs/_includes/base.njk @@ -50,6 +50,9 @@ Search / + + {# Login #} + {% server "loginOrAvatar" %} @@ -76,14 +79,19 @@ {% endif %} + {# Main #}
{# Expandable outline #} + {% if hasOutline %} + {% endif %} + +
{% server "flashes" %}
{% block header %} {% include 'breadcrumbs.njk' %} diff --git a/docs/_includes/head.njk b/docs/_includes/head.njk index 695aa836b..89a303d24 100644 --- a/docs/_includes/head.njk +++ b/docs/_includes/head.njk @@ -47,3 +47,7 @@ + + +{# Used by Web Awesome App to inject other assets into the head. #} +{% server "head" %} diff --git a/docs/_layouts/page.njk b/docs/_layouts/page.njk index a55a02c21..203527a44 100644 --- a/docs/_layouts/page.njk +++ b/docs/_layouts/page.njk @@ -1,4 +1,9 @@ -{% set hasSidebar = true %} -{% set hasOutline = false %} +{% if hasSidebar == undefined %} + {% set hasSidebar = true %} +{% endif %} + +{% if hasOutline == undefined %} + {% set hasOutline = false %} +{% endif %} {% extends "../_includes/base.njk" %} diff --git a/docs/index.md b/docs/index.md index aa3d060f5..9ae2e8a82 100644 --- a/docs/index.md +++ b/docs/index.md @@ -4,8 +4,6 @@ description: Build better with Web Awesome, the open source library of web compo layout: page --- - - -{% endset %} - - -
- - -
- - -{% markdown %} -## How does it work? - -The utility consists of a timeout (`2s` by default) and a fade duration (`200ms` by default). -- If the element is _ready_ before the timeout, it will appear immediately. -- If it takes longer than _timeout_ + _fade_, it will fade in over the fade duration. -- If it takes somewhere between _timeout_ and _timeout_ + _fade_, you will get an interrupted fade. - -An element is considered ready when both of these are true: -1. Either It has been registered or has a `did-ssr` attribute (indicating it was pre-rendered) -2. If it’s a Web Awesome component, its contents are also ready - -## Customization - -You can use the following CSS variables to customize the behavior: - -| Variable | Description | Default | -| --- | --- | --- | -| `--wa-fouce-fade` | The transition duration for the fade effect. | `200ms` | -| `--wa-fouce-timeout` | The timeout after which elements will appear even if not registered | `2s` | - -The fade duration cannot be longer than the timeout. -This means that you can disable FOUCE reduction on an element by setting `--wa-fouce-timeout: 0s`. - -For example, if instead of `did-ssr` you used an `ssr` attribute to mark elements that were pre-rendered, you can do this to get them to appear immediately: - -```css -[ssr] { - --wa-fouce-timeout: 0s; -} -``` - -You can also opt-out from FOUCE reduction for an element and its contents by adding the `.wa-fouce-off` class to it. -Applying this class to the root element will disable the utility for the entire page. -{% endmarkdown %} diff --git a/src/styles/utilities/fouce.css b/src/styles/utilities/fouce.css index 75e44d807..84ab08e48 100644 --- a/src/styles/utilities/fouce.css +++ b/src/styles/utilities/fouce.css @@ -1,20 +1,17 @@ /* * Utility to minimize FOUCE and show custom elements only after they're registered */ +:not(:defined), +:state(wa-defined):has(:not(:defined)), +.wa-cloak:has(:not(:defined)) { + animation: 2s step-end wa-fouce-cloak; +} -@keyframes wa-fade-in { +@keyframes wa-fouce-cloak { from { opacity: 0; } -} - -:not(:defined), -:state(wa-defined):has(:not(:defined)) { - /* The clamp() ensures that if --wa-fouce-timeout is set to 0s, the whole effect is disabled */ - --wa-fouce-animation: clamp(0s, var(--wa-fouce-fade, 200ms), var(--wa-fouce-timeout, 2s)) var(--wa-fouce-timeout, 2s) - wa-fade-in both; - - &:where(:not([did-ssr], .wa-fouce-off, .wa-fouce-off *)) { - animation: var(--wa-fouce-animation); + to { + opacity: 1; } } diff --git a/src/utilities/autoloader.ts b/src/utilities/autoloader.ts index 4ff6463ad..c40127ce5 100644 --- a/src/utilities/autoloader.ts +++ b/src/utilities/autoloader.ts @@ -50,6 +50,12 @@ export async function discover(root: Element | ShadowRoot) { console.warn(imp.reason); // eslint-disable-line no-console } } + + // Wait a cycle to allow the first Lit update to run + await new Promise(requestAnimationFrame); + + // Dispatch an event when discovery is complete. + document.dispatchEvent(new CustomEvent('wa-discovery-complete')); } /** @@ -69,3 +75,22 @@ function register(tagName: string): Promise { import(path).then(() => resolve()).catch(() => reject(new Error(`Unable to autoload <${tagName}> from ${path}`))); }); } + +/** + * Acts as a middleware for Turbo's `turbo:before-render` event to ensure components are auto-loaded before showing the + * next page, eliminating page-to-page FOUCE in a Turbo environment. + */ +export function preventTurboFouce(timeout = 2000) { + document.addEventListener('turbo:before-render', async (event: CustomEvent) => { + const newBody = event.detail.newBody; + + event.preventDefault(); + + try { + // Wait until all elements are registered or two seconds, whichever comes first + await Promise.race([discover(newBody), new Promise(resolve => setTimeout(resolve, timeout))]); + } finally { + event.detail.resume(); + } + }); +} diff --git a/src/utilities/defined.ts b/src/utilities/defined.ts new file mode 100644 index 000000000..277693085 --- /dev/null +++ b/src/utilities/defined.ts @@ -0,0 +1,64 @@ +interface AllDefinedOptions { + /** + * A callback that accepts a custom element tag name and returns `true` if the custom element should be defined before + * resolving or `false` to skip it. The tag name is always in lowercase. + */ + match: (tagName: string) => boolean; + + /** + * To wait for additional custom elements that may not be on the page when the function is called, provide them here. + */ + additionalElements: string | string[]; + + /** + * The root in which to look for custom elements. Defaults to `document`. By design, shadow roots are not traversed, + * but you can call this function and set `root` to a custom element's shadow root if needed. + */ + root: Document | ShadowRoot; +} + +/** + * Waits for custom elements that are currently on the page to be registered before resolving. This is sugar for + * awaiting `customElements.whenDefined()` multiple times. By default, the function waits for all undefined Web Awesome + * elements, but you can pass a custom match function to wait for other custom elements instead. + * + * The function returns with `Promise.all()`, so any loading errors will cause it to reject. Make sure you handle errors + * accordingly using a try/catch block or a `.catch()`. + * + * @example + * // Wait for Web Awesome elements + * await allDefined(); + * + * // Wait for all custom elements that start with `foo-` as well as the `` element + * await allDefined({ + * match: tagName => tagName.startsWith('foo-'), + * additionalElements: ['bar-button', 'baz-dialog'] + * }); + */ +export async function allDefined(options?: Partial) { + const opts: AllDefinedOptions = { + match: tagName => tagName.startsWith('wa-'), + additionalElements: [], + root: document, + ...options, + }; + + // Ensure additional elements is an array + const additionalElements = Array.isArray(opts.additionalElements) + ? opts.additionalElements + : [opts.additionalElements]; + + // Discover undefined elements in the document + const undefinedElements = [...opts.root.querySelectorAll(':not(:defined)')] + .map(el => el.localName) + .filter((tag, index, arr) => arr.indexOf(tag) === index) // make it unique + .filter(tag => opts.match(tag)); // make sure it matches + + const tagsToAwait = [...undefinedElements, ...additionalElements]; + + // Wait for all to be registered + await Promise.all(tagsToAwait.map(tag => customElements.whenDefined(tag))); + + // Wait a cycle for the first update + await new Promise(requestAnimationFrame); +} diff --git a/src/webawesome.ts b/src/webawesome.ts index ae2a4bee2..287f9d02a 100644 --- a/src/webawesome.ts +++ b/src/webawesome.ts @@ -1,6 +1,7 @@ export { registerIconLibrary, unregisterIconLibrary } from './components/icon/library.js'; -export { discover, startLoader, stopLoader } from './utilities/autoloader.js'; +export { discover, preventTurboFouce, startLoader, stopLoader } from './utilities/autoloader.js'; export { getBasePath, getKitCode, setBasePath, setKitCode } from './utilities/base-path.js'; +export { allDefined } from './utilities/defined.js'; export { registerTranslation } from './utilities/localize.js'; // Utilities diff --git a/web-test-runner.config.js b/web-test-runner.config.js index 0ce1a4570..599e2f43e 100644 --- a/web-test-runner.config.js +++ b/web-test-runner.config.js @@ -1,4 +1,3 @@ -import { litSsrPlugin } from '@lit-labs/testing/web-test-runner-ssr-plugin.js'; import { esbuildPlugin } from '@web/dev-server-esbuild'; import { playwrightLauncher } from '@web/test-runner-playwright'; import { readFileSync } from 'fs'; @@ -53,7 +52,6 @@ export default { ts: true, target: 'es2020', }), - litSsrPlugin(), ], browsers: [ playwrightLauncher({ product: 'chromium', concurrency }), @@ -78,12 +76,10 @@ export default { ${componentImports.map(str => `"${str}"`).join(',\n')} ] - window.SSR_ONLY = ${process.env['SSR_ONLY'] === 'true'} window.CSR_ONLY = ${process.env['CSR_ONLY'] === 'true'} From c9979e15f8046c6c4bac6251c63164e85c256803 Mon Sep 17 00:00:00 2001 From: Cory LaViska Date: Mon, 24 Mar 2025 16:49:08 -0400 Subject: [PATCH 10/23] adds a hard coded delay to drastically reduce theme picker jank (#829) --- docs/assets/scripts/theme-picker.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/docs/assets/scripts/theme-picker.js b/docs/assets/scripts/theme-picker.js index e9220a235..b22a60414 100644 --- a/docs/assets/scripts/theme-picker.js +++ b/docs/assets/scripts/theme-picker.js @@ -4,9 +4,12 @@ export function domChange(fn, { behavior = 'smooth' } = {}) { document.startViewTransition && !window.matchMedia('(prefers-reduced-motion: reduce)').matches; if (canUseViewTransitions && behavior === 'smooth') { - document.startViewTransition(fn); - } else { - fn(true); + 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; } } From 8214ff6b2d090034659add00a3983d3b84a71c70 Mon Sep 17 00:00:00 2001 From: Lea Verou Date: Tue, 25 Mar 2025 11:39:04 -0400 Subject: [PATCH 11/23] Several fixes around overviews, outlines etc (#825) * Fix outline for headings that have links Previously produced blank items because it assumed any link in a heading is an anchor * Filter unlisted items from overviews Previously they were filtered only when the card was rendered, so their heading was still shown * [Overview] Add id to group headings * Hide headings from empty groups Should never happen but you never know * [Overview] Ensure "Other" is always last even when no sorting --- docs/_includes/grouped-pages.njk | 4 ++-- docs/_includes/sidebar-link.njk | 2 +- docs/_utils/filters.js | 17 ++++++++++++++++- docs/_utils/outline.js | 2 +- 4 files changed, 20 insertions(+), 5 deletions(-) diff --git a/docs/_includes/grouped-pages.njk b/docs/_includes/grouped-pages.njk index 7c06d0b3a..2273f4c98 100644 --- a/docs/_includes/grouped-pages.njk +++ b/docs/_includes/grouped-pages.njk @@ -3,8 +3,8 @@
{% set groupedPages = allPages | groupPages(categories, page) %} {% for category, pages in groupedPages -%} - {% if groupedPages.meta.groupCount > 1 %} -

+ {% if groupedPages.meta.groupCount > 1 and pages.length > 0 %} +

{% if pages.meta.url %}{{ pages.meta.title }} {% else %} {{ pages.meta.title }} diff --git a/docs/_includes/sidebar-link.njk b/docs/_includes/sidebar-link.njk index 261a272ec..d44628b07 100644 --- a/docs/_includes/sidebar-link.njk +++ b/docs/_includes/sidebar-link.njk @@ -1,4 +1,4 @@ -{% if not (isAlpha and page.data.noAlpha) and not page.data.unlisted -%} +{% if page | show -%}
  • {{ page.data.title }} {% if page.data.status == 'experimental' %}{% endif %} diff --git a/docs/_utils/filters.js b/docs/_utils/filters.js index f81e5b909..9747a78b3 100644 --- a/docs/_utils/filters.js +++ b/docs/_utils/filters.js @@ -178,6 +178,10 @@ export function sort(arr, by = { 'data.order': 1, 'data.title': '' }) { }); } +export function show(page) { + return !(page.data.noAlpha && page.data.isAlpha) && !page.data.unlisted; +} + /** * Group an 11ty collection (or any array of objects with a `data.tags` property) by certain tags. * @param {object[]} collection @@ -198,7 +202,7 @@ export function groupPages(collection, options = {}, page) { options = { tags: options }; } - let { tags, groups, titles = {}, other = 'Other' } = options; + let { tags, groups, titles = {}, other = 'Other', filter = show } = options; if (groups === undefined && Array.isArray(tags)) { groups = tags; @@ -237,6 +241,10 @@ export function groupPages(collection, options = {}, page) { let byUrl = {}; let byParentUrl = {}; + if (filter) { + collection = collection.filter(filter); + } + for (let item of collection) { let url = item.page.url; let parentUrl = item.data.parentUrl; @@ -313,6 +321,13 @@ export function groupPages(collection, options = {}, page) { if (sortedGroups) { ret = sortObject(ret, sortedGroups); + } else { + // At least make sure other is last + if (ret.other) { + let otherGroup = ret.other; + delete ret.other; + ret.other = otherGroup; + } } Object.defineProperty(ret, 'meta', { diff --git a/docs/_utils/outline.js b/docs/_utils/outline.js index 0281ee66b..026a59e00 100644 --- a/docs/_utils/outline.js +++ b/docs/_utils/outline.js @@ -39,7 +39,7 @@ export function outlinePlugin(options = {}) { } // Create a clone of the heading so we can remove links and [data-no-outline] elements from the text content - clone.querySelectorAll('a').forEach(a => a.remove()); + clone.querySelectorAll('.wa-visually-hidden, [hidden], [aria-hidden="true"]').forEach(el => el.remove()); clone.querySelectorAll('[data-no-outline]').forEach(el => el.remove()); // Generate the link From 17cf902f5396b0d3df2966e5c528ebb79f32dbb9 Mon Sep 17 00:00:00 2001 From: Lea Verou Date: Tue, 25 Mar 2025 12:51:36 -0400 Subject: [PATCH 12/23] Add `appearance` to details, closes #569 Except `accent` as that's a) far less useful and b) trickier due to the icon color --- docs/docs/components/details.md | 25 +++++++++++++++++++++++++ docs/docs/native/details.md | 29 +++++++++++++++++++++++++++++ src/components/details/details.ts | 6 +++++- src/styles/native/details.css | 6 ++++-- 4 files changed, 63 insertions(+), 3 deletions(-) diff --git a/docs/docs/components/details.md b/docs/docs/components/details.md index 07cd53b33..9f6d94107 100644 --- a/docs/docs/components/details.md +++ b/docs/docs/components/details.md @@ -77,6 +77,31 @@ The details component automatically adapts to right-to-left languages: ``` +### Appearance + +Use the `appearance` attribute to change the element’s visual appearance. + +```html {.example} + +``` + ### Grouping Details Details are designed to function independently, but you can simulate a group or "accordion" where only one is shown at a time by listening for the `wa-show` event. diff --git a/docs/docs/native/details.md b/docs/docs/native/details.md index 767904bc2..d633e8430 100644 --- a/docs/docs/native/details.md +++ b/docs/docs/native/details.md @@ -19,6 +19,35 @@ file: styles/native/details.css ## Examples +### Appearance + +Use the [appearance utility classes](/docs/utilities/appearance) to change the element's visual appearance: + +```html {.example} + +``` + ### Right-to-Left Languages The details styling automatically adapts to right-to-left languages: diff --git a/src/components/details/details.ts b/src/components/details/details.ts index 175a394eb..5c34adeed 100644 --- a/src/components/details/details.ts +++ b/src/components/details/details.ts @@ -9,6 +9,7 @@ import { getTargetElement, waitForEvent } from '../../internal/event.js'; import { watch } from '../../internal/watch.js'; import WebAwesomeElement from '../../internal/webawesome-element.js'; import nativeStyles from '../../styles/native/details.css'; +import appearanceStyles from '../../styles/utilities/appearance.css'; import { LocalizeController } from '../../utilities/localize.js'; import '../icon/icon.js'; import styles from './details.css'; @@ -45,7 +46,7 @@ import styles from './details.css'; */ @customElement('wa-details') export default class WaDetails extends WebAwesomeElement { - static shadowStyle = [nativeStyles, styles]; + static shadowStyle = [appearanceStyles, nativeStyles, styles]; private detailsObserver: MutationObserver; private readonly localize = new LocalizeController(this); @@ -67,6 +68,9 @@ export default class WaDetails extends WebAwesomeElement { /** Disables the details so it can't be toggled. */ @property({ type: Boolean, reflect: true }) disabled = false; + /** The element's visual appearance. */ + @property({ reflect: true }) appearance: 'filled' | 'outlined' | 'plain' = 'outlined'; + firstUpdated() { this.body.style.height = this.open ? 'auto' : '0'; if (this.open) { diff --git a/src/styles/native/details.css b/src/styles/native/details.css index 0dd4779b2..ab50445af 100644 --- a/src/styles/native/details.css +++ b/src/styles/native/details.css @@ -2,10 +2,12 @@ details:where(:not(:host *)), :host { --icon-color: var(--wa-color-text-quiet); --spacing: var(--wa-space-m); + --outlined-border-color: var(--wa-color-surface-border); - background-color: var(--wa-color-surface-default); - border: var(--wa-panel-border-width) var(--wa-color-surface-border) var(--wa-panel-border-style); + background-color: var(--background-color, var(--wa-color-surface-default)); + border: var(--wa-panel-border-width) var(--border-color, var(--wa-color-surface-border)) var(--wa-panel-border-style); border-radius: var(--wa-panel-border-radius); + color: var(--text-color, inherit); padding: var(--spacing); /* Print styles */ From faed8da3cd14da143bf6d6f4d238fe822bb8c5cc Mon Sep 17 00:00:00 2001 From: Lea Verou Date: Tue, 25 Mar 2025 12:51:42 -0400 Subject: [PATCH 13/23] Fix broken link --- docs/docs/native/button.md | 2 +- docs/docs/native/callout.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/docs/native/button.md b/docs/docs/native/button.md index b9740a711..3c050199f 100644 --- a/docs/docs/native/button.md +++ b/docs/docs/native/button.md @@ -33,7 +33,7 @@ Use the [variant utility classes](../utilities/color.md) to set the button's sem ### Appearance -Use the [appearance utility classes](../utilities/appearance.md) to change the button's visual appearance: +Use the [appearance utility classes](/docs/utilities/appearance) to change the button's visual appearance: ```html {.example}
    diff --git a/docs/docs/native/callout.md b/docs/docs/native/callout.md index e3ef62b88..ae8a5cdbf 100644 --- a/docs/docs/native/callout.md +++ b/docs/docs/native/callout.md @@ -57,7 +57,7 @@ Use the [variant utility classes](../utilities/color.md) to set the callout's co ### Appearance -Use the [appearance utility classes](../utilities/appearance.md) to change the callout's visual appearance (the default is `outlined filled`). +Use the [appearance utility classes](/docs/utilities/appearance) to change the callout's visual appearance (the default is `outlined filled`). ```html {.example}
    - ``` - +### Appearance + +Use the `appearance` attribute to change the card's visual appearance. + +```html {.example} + +``` diff --git a/src/components/card/card.css b/src/components/card/card.css index 8be8a0d9a..8b2f193f9 100644 --- a/src/components/card/card.css +++ b/src/components/card/card.css @@ -5,15 +5,15 @@ --spacing: var(--wa-space); --border-width: var(--wa-panel-border-width); - --border-color: var(--wa-color-surface-border); + --outlined-border-color: var(--wa-color-surface-border); --border-radius: var(--wa-panel-border-radius); - --inner-border-radius: calc(var(--border-radius) - var(--border-width)); + --inner-border-color: var(--outlined-border-color); display: flex; flex-direction: column; - background-color: var(--wa-color-surface-default); - border-color: var(--border-color); + background-color: var(--background-color, var(--wa-color-surface-default)); + border-color: var(--border-color, var(--wa-color-surface-border)); border-radius: var(--border-radius); border-style: var(--wa-panel-border-style); box-shadow: var(--wa-shadow-s); @@ -21,6 +21,20 @@ color: var(--wa-color-text-normal); } +:host(:is([appearance~='accent'], .wa-accent)) { + color: var(--text-color, var(--wa-color-text-normal)); +} + +:host([appearance~='filled']), +:host(.wa-filled) { + --inner-border-color: oklab(from var(--outlined-border-color) l a b / 65%); +} + +:host([appearance='plain']) { + --inner-border-color: transparent; + box-shadow: none; +} + /* Take care of top and bottom radii */ .image, :host(:not([with-image])) .header, @@ -49,7 +63,7 @@ .header { display: block; border-block-end-style: inherit; - border-block-end-color: var(--border-color); + border-block-end-color: var(--inner-border-color); border-block-end-width: var(--border-width); padding: calc(var(--spacing) / 2) var(--spacing); } @@ -62,7 +76,7 @@ .footer { display: block; border-block-start-style: inherit; - border-block-start-color: var(--border-color); + border-block-start-color: var(--inner-border-color); border-block-start-width: var(--border-width); padding: var(--spacing); } diff --git a/src/components/card/card.ts b/src/components/card/card.ts index 2c9317d9a..6bf2693ce 100644 --- a/src/components/card/card.ts +++ b/src/components/card/card.ts @@ -2,6 +2,7 @@ import { html } from 'lit'; import { customElement, property } from 'lit/decorators.js'; import { HasSlotController } from '../../internal/slot.js'; import WebAwesomeElement from '../../internal/webawesome-element.js'; +import appearanceStyles from '../../styles/utilities/appearance.css'; import sizeStyles from '../../styles/utilities/size.css'; import styles from './card.css'; @@ -22,19 +23,24 @@ import styles from './card.css'; * @csspart footer - The container that wraps the card's footer. * * @cssproperty [--border-radius=var(--wa-panel-border-radius)] - The radius for the card's corners. Expects a single value. - * @cssproperty [--border-color=var(--wa-color-surface-border)] - The color of the card's borders, including inner borders. Expects a single value. + * @cssproperty [--border-color=var(--wa-color-surface-border)] - The color of the card's borders. Expects a single value. + * @cssproperty [--inner-border-color=var(--wa-color-surface-border)] - The color of the card's inner borders, e.g. those separating headers and footers from the main content. Expects a single value. * @cssproperty [--border-width=var(--wa-panel-border-width)] - The width of the card's borders. Expects a single value. * @cssproperty [--spacing=var(--wa-space)] - The amount of space around and between sections of the card. Expects a single value. */ @customElement('wa-card') export default class WaCard extends WebAwesomeElement { - static shadowStyle = [sizeStyles, styles]; + static shadowStyle = [sizeStyles, appearanceStyles, styles]; private readonly hasSlotController = new HasSlotController(this, 'footer', 'header', 'image'); /** The component's size. Will be inherited by any descendants with a `size` attribute. */ @property({ reflect: true, initial: 'medium' }) size: 'small' | 'medium' | 'large' | 'inherit' = 'inherit'; + /** The card's visual appearance. */ + @property({ reflect: true }) + appearance: 'accent' | 'filled' | 'outlined' | 'plain' = 'outlined'; + /** Renders the card with a header. Only needed for SSR, otherwise is automatically added. */ @property({ attribute: 'with-header', type: Boolean, reflect: true }) withHeader = false; From 9e84274a93dffff707a508658edecbd495b25159 Mon Sep 17 00:00:00 2001 From: Lea Verou Date: Tue, 25 Mar 2025 14:11:16 -0400 Subject: [PATCH 15/23] [Card] Round all corners of the image for `appearance=plain` --- docs/docs/components/card.md | 10 ++++++++++ src/components/card/card.css | 9 +++++++++ 2 files changed, 19 insertions(+) diff --git a/docs/docs/components/card.md b/docs/docs/components/card.md index 87a99abc2..bb50a96cd 100644 --- a/docs/docs/components/card.md +++ b/docs/docs/components/card.md @@ -186,11 +186,21 @@ Use the `appearance` attribute to change the card's visual appearance. ```html {.example} @@ -72,4 +70,4 @@ tags: app Save -``` \ No newline at end of file +``` diff --git a/docs/docs/patterns/app/data-display.md b/docs/docs/patterns/app/data-display.md index b0cc3bf3c..6e722e0e2 100644 --- a/docs/docs/patterns/app/data-display.md +++ b/docs/docs/patterns/app/data-display.md @@ -1,8 +1,6 @@ --- title: Data Display description: TODO -parent: app -tags: app --- ## Examples @@ -157,4 +155,4 @@ tags: app
  • -``` \ No newline at end of file +``` diff --git a/docs/docs/patterns/app/description-list.md b/docs/docs/patterns/app/description-list.md index 8736a671f..3593d4c16 100644 --- a/docs/docs/patterns/app/description-list.md +++ b/docs/docs/patterns/app/description-list.md @@ -1,8 +1,6 @@ --- title: Description List description: 'Shows the user information with labels and values in an easy to read format.' -parent: app -tags: app --- ## Examples @@ -241,4 +239,4 @@ tags: app -``` \ No newline at end of file +``` diff --git a/docs/docs/patterns/app/empty-state.md b/docs/docs/patterns/app/empty-state.md index 820d48c4e..61d757ce5 100644 --- a/docs/docs/patterns/app/empty-state.md +++ b/docs/docs/patterns/app/empty-state.md @@ -1,8 +1,6 @@ --- title: Empty State description: TODO -parent: app -tags: app --- ## Examples @@ -110,4 +108,4 @@ tags: app Or start from an empty project → -``` \ No newline at end of file +``` diff --git a/docs/docs/patterns/app/faq.md b/docs/docs/patterns/app/faq.md index b30fddc08..1d4b00c74 100644 --- a/docs/docs/patterns/app/faq.md +++ b/docs/docs/patterns/app/faq.md @@ -1,8 +1,6 @@ --- title: FAQ description: 'The user has questions concerning a site and its related services' -parent: app -tags: app --- ## Examples @@ -122,4 +120,4 @@ tags: app -``` \ No newline at end of file +``` diff --git a/docs/docs/patterns/app/feed.md b/docs/docs/patterns/app/feed.md index 3d9ada8d0..0383de8d3 100644 --- a/docs/docs/patterns/app/feed.md +++ b/docs/docs/patterns/app/feed.md @@ -1,8 +1,6 @@ --- title: Feed description: TODO -parent: app -tags: app --- ```html {.example} @@ -83,4 +81,4 @@ tags: app } } -``` \ No newline at end of file +``` diff --git a/docs/docs/patterns/app/grid.md b/docs/docs/patterns/app/grid.md index 0cc7aab2f..4f88761ac 100644 --- a/docs/docs/patterns/app/grid.md +++ b/docs/docs/patterns/app/grid.md @@ -1,8 +1,6 @@ --- title: Grid description: TODO -parent: app -tags: app --- ```html {.example} @@ -163,4 +161,4 @@ tags: app -``` \ No newline at end of file +``` diff --git a/docs/docs/patterns/app/index.njk b/docs/docs/patterns/app/index.njk index 9efa42648..1d8996549 100644 --- a/docs/docs/patterns/app/index.njk +++ b/docs/docs/patterns/app/index.njk @@ -1,28 +1,8 @@ --- title: App description: TODO -layout: page -categories: ["app"] +parent: patterns +layout: overview +override:tags: [] listChildren: true --- - -{% set appPages = collections.app %} - -
    - {%- for page in appPages -%} - - - -
    - {% include "svgs/" + (page.data.icon or "thumbnail-placeholder") + ".njk" %} -
    - {{ page.data.title }} - {% if pageSubtitle -%} - - {%- endif %} -
    -
    - - {%- endfor -%} - -
    diff --git a/docs/docs/patterns/app/leaderboard.md b/docs/docs/patterns/app/leaderboard.md index 944ab6c8f..61dd9982f 100644 --- a/docs/docs/patterns/app/leaderboard.md +++ b/docs/docs/patterns/app/leaderboard.md @@ -1,8 +1,6 @@ --- title: Leaderboard description: TODO -parent: app -tags: app --- ```html{.example} @@ -366,4 +364,4 @@ tags: app -``` \ No newline at end of file +``` diff --git a/docs/docs/patterns/app/pagination.md b/docs/docs/patterns/app/pagination.md index fa6291cbc..6b597457f 100644 --- a/docs/docs/patterns/app/pagination.md +++ b/docs/docs/patterns/app/pagination.md @@ -1,8 +1,6 @@ --- title: Pagination description: TODO -parent: app -tags: app --- ## Simple Pagination @@ -41,4 +39,4 @@ tags: app Next -``` \ No newline at end of file +``` diff --git a/docs/docs/patterns/app/pricing.md b/docs/docs/patterns/app/pricing.md index 995dd9127..7f34936fa 100644 --- a/docs/docs/patterns/app/pricing.md +++ b/docs/docs/patterns/app/pricing.md @@ -1,8 +1,6 @@ --- title: Pricing description: TODO -parent: app -tags: app --- ```html{.example} @@ -29,23 +27,23 @@ tags: app @@ -72,7 +70,7 @@ tags: app @@ -99,7 +97,7 @@ tags: app @@ -134,4 +132,4 @@ tags: app ### With templates -### With recommendations grid \ No newline at end of file +### With recommendations grid diff --git a/docs/docs/patterns/ecommerce/category-filter.md b/docs/docs/patterns/ecommerce/category-filter.md index 7df961f8f..081450020 100644 --- a/docs/docs/patterns/ecommerce/category-filter.md +++ b/docs/docs/patterns/ecommerce/category-filter.md @@ -1,8 +1,6 @@ --- title: Category Filter description: 'Helps the user find the right products with filters to refine search results by specific attributes.' -parent: ecommerce -tags: e-commerce icon: checkbox --- @@ -42,7 +40,7 @@ icon: checkbox XXL - + @@ -82,4 +80,4 @@ icon: checkbox -``` \ No newline at end of file +``` diff --git a/docs/docs/patterns/ecommerce/category-preview.md b/docs/docs/patterns/ecommerce/category-preview.md index 721789cdf..9efeb7cb5 100644 --- a/docs/docs/patterns/ecommerce/category-preview.md +++ b/docs/docs/patterns/ecommerce/category-preview.md @@ -1,8 +1,6 @@ --- title: Category Preview description: 'Help shoppers discover your product offerings with showcases of product categories.' -parent: ecommerce -tags: e-commerce icon: preview --- @@ -17,20 +15,20 @@ icon: preview
    - -
    - {% include "svgs/" + (page.data.icon or "thumbnail-placeholder") + ".njk" %} -
    - {{ page.data.title }} - {% if pageSubtitle -%} - - {%- endif %} -
    -
    - - {%- endfor -%} - -
    diff --git a/docs/docs/patterns/ecommerce/order-history.md b/docs/docs/patterns/ecommerce/order-history.md index a12e51e69..a889d9c66 100644 --- a/docs/docs/patterns/ecommerce/order-history.md +++ b/docs/docs/patterns/ecommerce/order-history.md @@ -1,8 +1,6 @@ --- title: Order History description: 'Empower your customers to view past purchases and track upcoming orders with comprehensive order histories.' -parent: ecommerce -tags: e-commerce --- ## List @@ -32,7 +30,7 @@ tags: e-commerce + diff --git a/docs/docs/patterns/ecommerce/order-summary.md b/docs/docs/patterns/ecommerce/order-summary.md index e2bd0721d..566e1ca31 100644 --- a/docs/docs/patterns/ecommerce/order-summary.md +++ b/docs/docs/patterns/ecommerce/order-summary.md @@ -1,8 +1,6 @@ --- title: Order Summary description: 'Give shoppers confidence in their purchases with summaries of everything included in their order.' -parent: ecommerce -tags: e-commerce --- ## Simple @@ -26,7 +24,7 @@ tags: e-commerce