@expressivecss/mcp-server 0.2.2 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +1 -1
- package/.cursor-plugin/plugin.json +1 -1
- package/CHANGELOG.md +91 -0
- package/README.md +485 -3
- package/capability-roadmap.json +1908 -221
- package/component-decisions.json +4406 -667
- package/component-guides.json +158 -42
- package/contract.json +6 -4
- package/package.json +2 -1
- package/semantics-data.json +601 -3
- package/server.js +488 -46
- package/smoke.mjs +1238 -3
package/component-guides.json
CHANGED
|
@@ -1,171 +1,287 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"generatedBy": "mcp/expressivecss/scripts/sync-guides.mjs",
|
|
4
|
-
"frameworkVersion": "0.
|
|
4
|
+
"frameworkVersion": "0.13.1",
|
|
5
5
|
"guides": [
|
|
6
|
+
{
|
|
7
|
+
"file": "accordion.md",
|
|
8
|
+
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Accordion\nStacked disclosures that open one section of content at a time, on native details.\n\nComponent ID: `accordion`\n\n[Component documentation](https://www.expressivecss.com/accordion.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/accordion.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.13.1)\n\nContract: ExpressiveCSS 0.13.1\n\nContract SHA-256: `18ec4146d300280bbcb74eb3b23f16d1015a0a3db86a8c59fc8348dbdf9d96c2`\n\n#### Selection and adaptation\n\nRuntime ownership: `css-only`. No dedicated entry in the reviewed Google component inventory.\n\nExample: Account, Billing and Security as three peer views of a settings page are tabs. An accordion would hide two of them behind headers when people switch between them.\n\n#### Material mapping\n\nRelationship: none. Web extension; no dedicated entry was observed in the reviewed current M3 component inventory.\n\nUpstream: web-extension-inventory-reviewed (2026-10-06); [evidence](https://m3.material.io/components).\n\nRequirements and boundaries: design reference web-extensions-review. The inventory page renders client-side, so this review relies on the 2026-09-13 inventory review. Full visual parity and spoken output unverified.\n\nSupport (2026-10-06, `llm.md#accordion`): Joined tiles on native details and summary, exclusive groups through the name attribute, a leading icon, a turning chevron and an animated open where the engine supports it.\n\nWeb adaptation: Native <details> and <summary>; the browser owns the toggle, its keyboard support and its expanded state.\n\nKnown boundary: Tile corners, the chevron turn, exclusive groups and the expanded state Chromium reports are checked in a browser; the open animation, enlarged text and spoken output remain unverified.\n\nFull parity remains unassessed. [Capability evidence](../references/capability-roadmap.md#accordion).\n\n#### Contract\n\nA `.accordion` holds `<details>` items. An item's `<summary>` is its header, and everything after the summary is its panel. The browser owns the behavior: the summary is a button that reports its expanded state, Enter and Space toggle it, and `open` sets the initial state. Give the items one shared `name` to make them exclusive, so opening one closes the others. CSS only; there is no JavaScript.\n\nThe items join as tiles. Outer corners take the full shape and inner corners the grouped shape, and an open item rounds every corner. Opening animates in engines that can transition to `auto` height, and opens at once elsewhere or under reduced motion. Keep links and buttons out of the summary, which is already a button; put actions in the panel.\n\n#### Syntax\n\n```html\n<div class=\"accordion\">\n <details name=\"faq\" open>\n <summary>Can I change my plan later?</summary>\n <p>Yes. The new plan starts with your next billing period.</p>\n </details>\n <details name=\"faq\">\n <summary>How do I export my data?</summary>\n <p>Open Settings, then Export. The download includes every project you own.</p>\n </details>\n</div>\n```\n\n#### Also documented\n\nThe Accordion section of the [API reference](https://www.expressivecss.com/llm.md) also covers Leading icon, Tokens.\n\n#### Rules\n\n- `accordion-items-are-details`: Write every accordion item as <details> with a <summary>. The element is the disclosure: a div with a click handler has no expanded state and no keyboard support.\n- `accordion-summary-holds-no-controls`: A <summary> is already the toggle button. A link or control inside it is a control nested in a control: pressing it also opens or closes the item, and screen readers flatten it into the summary name. Put actions in the panel.\n- `accordion-summary-state-is-native`: The browser exposes <summary> as a button and reports its expanded state from the open attribute. An authored role=\"button\" repeats it, and an authored aria-expanded goes stale the first time the item toggles.\n"
|
|
9
|
+
},
|
|
6
10
|
{
|
|
7
11
|
"file": "app-bar.md",
|
|
8
|
-
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### App bar\nMaterial Design 3 top app bars, from the HTML.\n\nComponent ID: `app-bar`\n\n[Component documentation](https://www.expressivecss.com/navbar.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/navbar.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.
|
|
12
|
+
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### App bar\nMaterial Design 3 top app bars, from the HTML.\n\nComponent ID: `app-bar`\n\n[Component documentation](https://www.expressivecss.com/navbar.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/navbar.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.13.1)\n\nContract: ExpressiveCSS 0.13.1\n\nContract SHA-256: `18ec4146d300280bbcb74eb3b23f16d1015a0a3db86a8c59fc8348dbdf9d96c2`\n\n#### Selection and adaptation\n\nRuntime ownership: `auto-init`. [Google guidance](https://m3.material.io/components/app-bars/overview)\n\nExample: A Home/Search/Profile row belongs in navigation, even if placed at the top. Keep the app bar for the title and screen actions.\n\n#### Material mapping\n\nRelationship: component. Google replaces baseline medium/large bars with flexible variants. ExpressiveCSS medium and large already mean medium-flexible and large-flexible; keep those classes.\n\nUpstream: requirements-prose-reviewed (2026-09-13); [evidence](https://m3.material.io/components/app-bars/overview).\n\nRequirements and boundaries: design reference layout-material-review. Full visual parity and spoken output unverified.\n\n[Specification link](https://m3.material.io/components/app-bars/specs); full specs unreviewed.\n\n[Google guidelines](https://m3.material.io/components/app-bars/guidelines).\n\nSupport (2026-09-17, `llm.md#navbar`): Small, medium flexible, large flexible, and search app bars.\n\nWeb adaptation: Native header/nav composition; AppBar owns scroll collapse and search expansion.\n\nKnown boundary: Native zoom, screen-reader announcements, contrast themes and physical mobile keyboard behavior remain pending manual review.\n\nFull parity remains unassessed. [Capability evidence](../references/capability-roadmap.md#app-bar).\n\n#### Contract\n\nThe bar is the markup. A `<header>` whose child is a `<nav>` is a top app bar. Top app bars stay pinned to the top by default, without a helper class. The heading is the headline; wrap it in `<hgroup>` with a `<p>` for a subtitle. Icon-only links and buttons are the leading and trailing actions. A `<menu>` (or `<ul>`) holds text destinations. A `<search class=\"search-bar\">` in the nav is the search app bar. There is no `navbar`, `nav-wrapper`, or `brand-logo` class.\n\nSmall is 64dp at `title-large`. `medium` is the Expressive medium-flexible bar: 112dp / `headline-medium`, 136dp with a subtitle. `large` is large-flexible: 120dp / `display-small`, 152dp with a subtitle. `AppBar` (started by `AutoInit()`) collapses those two on scroll and opens the related search view when the search field is selected. Icons are 24dp in a 48dp target and inherit the header color so a fill + `on-*` pair stays readable.\n\nNavigation rails and tabs are separate components. Do not nest either one in the header.\n\n#### Syntax\n\n```html\n<header>\n <nav aria-label=\"Main\">\n <button type=\"button\" aria-label=\"Menu\">\n <span class=\"material-symbols\" aria-hidden=\"true\">menu</span>\n </button>\n <h2>Title</h2>\n <button type=\"button\" aria-label=\"Search\"><span class=\"material-symbols\" aria-hidden=\"true\">search</span></button>\n <button type=\"button\" aria-label=\"More\"><span class=\"material-symbols\" aria-hidden=\"true\">more_vert</span></button>\n </nav>\n</header>\n```\n\n#### Also documented\n\nThe Navbar section of the [API reference](https://www.expressivecss.com/llm.md) also covers Small, Destinations, Center-aligned, Medium and large flexible, Subtitle, Collapse, Fixed, Color, Menu, Search, Responsive navigation.\n\n#### Rules\n\nNo component-specific `semantics.json` rules apply to this guide.\n\n#### Guide checks\n\n- Label every <nav> landmark with aria-label or aria-labelledby.\n"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"file": "aura.md",
|
|
16
|
+
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Aura\nA light that runs around the border of the one button or card a screen most needs you to see.\n\nComponent ID: `aura`\n\n[Component documentation](https://www.expressivecss.com/aura.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/aura.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.13.1)\n\nContract: ExpressiveCSS 0.13.1\n\nContract SHA-256: `18ec4146d300280bbcb74eb3b23f16d1015a0a3db86a8c59fc8348dbdf9d96c2`\n\n#### Selection and adaptation\n\nRuntime ownership: `css-only`. No dedicated entry in the reviewed Google component inventory.\n\nExample: A pricing page with one recommended plan gets one aura on that plan's card. Putting an aura on every plan, or on each new item in a list, removes the contrast the effect depends on; use a badge for those.\n\n#### Material mapping\n\nRelationship: none. Web extension; no dedicated entry was observed in the reviewed current M3 component inventory.\n\nUpstream: web-extension-inventory-reviewed (2026-10-08); [evidence](https://m3.material.io/components).\n\nRequirements and boundaries: design reference web-extensions-review. The inventory page renders client-side, so this review relies on the 2026-09-13 inventory review. Full visual parity and spoken output unverified.\n\nSupport (2026-10-08, `llm.md#aura`): A turning light on a ring around one wrapped element, with dual, rainbow and glow styles, token color, width, corner and speed, a reversed turn in RTL, a static ring under reduced motion and a Highlight ring in forced colors.\n\nWeb adaptation: A wrapper whose pseudo-element draws a masked conic gradient; it adds nothing to the accessibility tree.\n\nKnown boundary: The ring geometry, the turning angle, its reverse in RTL, its stop under reduced motion and the absence of the ring from the accessibility tree are checked in a browser; forced colors and spoken output remain unverified.\n\nFull parity remains unassessed. [Capability evidence](../references/capability-roadmap.md#aura).\n\n#### Contract\n\nAn `.aura` wraps one element and runs a light around a 2px ring just outside it. Use it on the button, card or panel a screen most needs the user to notice, and keep it to one per screen. `dual` sends primary and tertiary lights around opposite sides, `rainbow` turns the whole hue wheel, and `glow` draws a blurred halo behind the element in place of a ring; give a glowing element an opaque container. The ring has 12px corners to match a card and is fully rounded around a button; set `--md-comp-aura-shape` to the corner size of anything else. Reduced motion stops the turning and keeps the ring. A shadow root ignores `@property`, so with the sheet adopted only there the light turns in 7.2deg steps. CSS only; there is no JavaScript.\n\nThe light is decoration drawn by a pseudo-element, so the wrapper adds nothing to the accessibility tree. Say in the text why the element matters, for example with a \"Recommended\" heading. In forced colors the ring is a solid `Highlight` outline.\n\n#### Syntax\n\n```html\n<div class=\"aura\">\n <button class=\"filled\">Upgrade</button>\n</div>\n\n<div class=\"aura rainbow\">\n <article>\n <h3>Pro plan</h3>\n <p>Unlimited projects and priority support.</p>\n </article>\n</div>\n```\n\n#### Also documented\n\nThe Aura section of the [API reference](https://www.expressivecss.com/llm.md) also covers Tokens.\n\n#### Rules\n\n- `aura-wraps-one-element`: An .aura wraps one element. Give each highlighted element its own .aura, and keep to one per screen.\n"
|
|
9
17
|
},
|
|
10
18
|
{
|
|
11
19
|
"file": "autocomplete.md",
|
|
12
|
-
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Autocomplete\nSuggest values under a text field as the user types.\n\nComponent ID: `autocomplete`\n\n[Component documentation](https://www.expressivecss.com/autocomplete.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/autocomplete.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.
|
|
20
|
+
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Autocomplete\nSuggest values under a text field as the user types.\n\nComponent ID: `autocomplete`\n\n[Component documentation](https://www.expressivecss.com/autocomplete.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/autocomplete.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.13.1)\n\nContract: ExpressiveCSS 0.13.1\n\nContract SHA-256: `18ec4146d300280bbcb74eb3b23f16d1015a0a3db86a8c59fc8348dbdf9d96c2`\n\n#### Selection and adaptation\n\nRuntime ownership: `auto-init`. [Related Google guidance](https://m3.material.io/components/menus/overview)\n\n#### Material mapping\n\nRelationship: related. Related guidance is not a dedicated component specification.\n\nUpstream: requirements-prose-reviewed (2026-09-13); [evidence](https://m3.material.io/components/menus/overview).\n\nRequirements and boundaries: design reference inputs-material-review. Full visual parity and spoken output unverified.\n\n[Specification link](https://m3.material.io/components/menus/specs); full specs unreviewed.\n\n[Google guidelines](https://m3.material.io/components/menus/guidelines).\n\nSupport (2026-09-13, `llm.md#autocomplete`): Single and multi-select suggestions.\n\nWeb adaptation: Runtime combobox behavior attached to a text field; no standalone upstream component claim.\n\nKnown boundary: Generated status-info does not announce new suggestion counts or asynchronous results.\n\nFull parity remains unassessed. [Capability evidence](../references/capability-roadmap.md#autocomplete).\n\n#### Contract\n\nAutocomplete preserves the original suggestion dataset and selected display label while editing. Escape and Tab close from the input, cancel pending opening, and keep listbox options outside sequential Tab order.\n\n`isOpen` follows the suggestion menu through rerenders and dismissal. Selection changes emit a bubbling, composed `change` event from the input, so delegated form listeners can observe them.\n\nAdd `autocomplete` to a text input inside a `.field`. `AutoInit()` starts every `.autocomplete` except `no-autoinit`, but the default `data` list is empty — pass options (or call `init`) to give it something to suggest.\n\n#### Syntax\n\n```html\n<div class=\"field\">\n <span class=\"material-symbols prefix\" aria-hidden=\"true\">textsms</span>\n <input type=\"text\" id=\"autocomplete-input\" class=\"autocomplete\" placeholder=\" \">\n <label for=\"autocomplete-input\">Autocomplete</label>\n</div>\n```\n\n#### Options\n\n| Name | Type | Default | Description |\n| --- | --- | --- | --- |\n| `data` | Array | `[]` | Suggestion list. Each item needs an `id`; `text`, `image`, and `description` are optional. |\n| `isMultiSelect` | Boolean | `false` | If true, several values can be selected. `onAutocomplete` receives an array. |\n| `maxMenuHeight` | String | `'300px'` | Max height of the suggestion menu. |\n| `i18n` | Object | See below | Partial overrides for live status messages. |\n| `onAutocomplete` | Function | `null` | Called after a selection (and when a default value is applied). Receives the selected entries. |\n| `onSearch` | Function | filters `id` and `text` | Called when input events change the search text, including paste, replacement and deletion without key events. Load or filter data, then call `setMenuItems`. |\n| `minLength` | Number | `1` | Characters required before suggestions open. `0` shows the list on click or focus. |\n| `menuOptions` | Object | see note | Options for Menu. Defaults include `autoFocus: false`, `closeOnClick: false`, and `coverTrigger: false`. |\n| `allowUnsafeHTML` | Boolean | `false` | If true, matched text is inserted as HTML. Only use sanitized data. |\n| `selected` | Array | `[]` | Initial selected ids, strings or numbers. An id without loaded data displays the id. Pass the selected ids to `setMenuItems` with the loaded data to resolve their labels. |\n\n#### Methods\n\n- `.open()`: Open the suggestion menu if the input meets `minLength`.\n- `.close()`: Close the suggestion menu.\n- `.selectOption()`: Select (or toggle, when multi-select) the entry with this id.\n- `.setMenuItems()`: Replace the visible suggestions. Optionally pass selected ids and whether to open the menu (default `true`).\n- `.destroy()`: Destroy the plugin instance, remove the menu, and tear down its event handlers.\n\n#### Also documented\n\nThe Autocomplete section of the [API reference](https://www.expressivecss.com/llm.md) also covers Initialization, Properties.\n\n#### Rules\n\n- `autocomplete-options-are-options`: The suggestion list is a listbox; every entry in it is an option.\n- `field-container-class`: The field container is `.field`. `.input-field` matches nothing in the sheet - the only `.input-field` rule is `.chips.input-field`.\n- `field-icon-not-i`: <i> means idiomatic text, not icon. Use <span class=\"material-symbols\">.\n- `field-icon-hidden`: The ligature is read aloud. A field icon is decoration; the label names the control.\n- `field-icon-side`: A field icon must name its side with .prefix or .suffix. The positional fallback counts elements of the same *type*, which only ever worked for <i>.\n- `field-label-for`: A label that does not wrap its control has to name it. The floating label sits *after* the control and cannot wrap it, so it always needs `for`; a label that does wrap its control (the file input trigger) does not.\n- `field-control-id`: Without an id the label has nothing to point at.\n- `field-supporting-text-id`: Supporting and error text has to be referenceable to be announced with the control.\n- `field-supporting-text-linked`: A field with supporting text must point at it with aria-describedby, or the text is never read out with the control.\n- `password-toggle-reports-its-state`: Give a .password-toggle aria-pressed=\"false\". The behavior flips it, and its name stays \"Show password\", so the pressed state is how a screen reader user hears that the password is visible.\n- `otp-autocompletes`: Set autocomplete=\"one-time-code\" on a one-time code input so the browser can offer the code from a message.\n- `otp-is-not-a-number`: A code is not a quantity: type=\"number\" drops leading zeros and adds a spinner. Use type=\"text\" with inputmode=\"numeric\".\n"
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"file": "avatar.md",
|
|
24
|
+
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Avatar\nA person or account as a circular image, initials or icon, alone or in an overlapping group.\n\nComponent ID: `avatar`\n\n[Component documentation](https://www.expressivecss.com/avatar.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/avatar.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.13.1)\n\nContract: ExpressiveCSS 0.13.1\n\nContract SHA-256: `18ec4146d300280bbcb74eb3b23f16d1015a0a3db86a8c59fc8348dbdf9d96c2`\n\n#### Selection and adaptation\n\nRuntime ownership: `css-only`. No dedicated entry in the reviewed Google component inventory.\n\nExample: A red dot for unread messages on the inbox icon is a badge. An avatar would suggest a person rather than a count.\n\n#### Material mapping\n\nRelationship: none. Web extension; no dedicated entry was observed in the reviewed current M3 component inventory.\n\nUpstream: web-extension-inventory-reviewed (2026-10-06); [evidence](https://m3.material.io/components).\n\nRequirements and boundaries: design reference web-extensions-review. The inventory page renders client-side, so this review relies on the 2026-09-13 inventory review. Full visual parity and spoken output unverified.\n\nSupport (2026-10-06, `llm.md#avatar`): Image, initials and icon avatars in three sizes, and overlapping groups with a surface ring and a count avatar.\n\nWeb adaptation: An <img> or a <span> with role=\"img\" and a name; initials scale with the size token.\n\nKnown boundary: Sizes, the circle, the group overlap and named initials are checked in a browser; image loading failures, enlarged text and spoken output remain unverified.\n\nFull parity remains unassessed. [Capability evidence](../references/capability-roadmap.md#avatar).\n\n#### Contract\n\nA `.avatar` is an `<img>`, or a `<span>` holding initials or an icon. It is 40px by default; `small` is 32px and `large` is 56px, and the initials and icon scale with it. Give an image alt text naming the person, or `alt=\"\"` when the name is already beside it. Initials read as separate letters, so give a `<span>` avatar `role=\"img\"` and an `aria-label`, or `aria-hidden=\"true\"` when the name is visible next to it. CSS only; there is no JavaScript.\n\n#### Syntax\n\n```html\n<img class=\"avatar\" src=\"ada.jpg\" alt=\"Ada Lovelace\">\n<span class=\"avatar\" role=\"img\" aria-label=\"Grace Hopper\">GH</span>\n<span class=\"avatar large\" role=\"img\" aria-label=\"Guest\">\n <span class=\"material-symbols\" aria-hidden=\"true\">person</span>\n</span>\n```\n\n#### Also documented\n\nThe Avatar section of the [API reference](https://www.expressivecss.com/llm.md) also covers Avatar group, Tokens.\n\n#### Rules\n\n- `avatar-image-has-alt`: Give an avatar image alt text naming the person, or alt=\"\" when their name is already beside it.\n- `avatar-is-an-image-or-hidden`: Initials read as separate letters and an icon reads as its ligature name. Give a non-image avatar role=\"img\" and a name, or aria-hidden=\"true\" when the person is named next to it.\n- `avatar-image-role-is-named`: An avatar with role=\"img\" needs aria-label or aria-labelledby. Its initials are hidden from the name computation of an image.\n"
|
|
13
25
|
},
|
|
14
26
|
{
|
|
15
27
|
"file": "badges.md",
|
|
16
|
-
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Badges\nNotifications, counts, or status on navigation items and icons.\n\nComponent ID: `badges`\n\n[Component documentation](https://www.expressivecss.com/badges.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/badges.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.
|
|
28
|
+
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Badges\nNotifications, counts, or status on navigation items and icons.\n\nComponent ID: `badges`\n\n[Component documentation](https://www.expressivecss.com/badges.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/badges.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.13.1)\n\nContract: ExpressiveCSS 0.13.1\n\nContract SHA-256: `18ec4146d300280bbcb74eb3b23f16d1015a0a3db86a8c59fc8348dbdf9d96c2`\n\n#### Selection and adaptation\n\nRuntime ownership: `css-only`. [Google guidance](https://m3.material.io/components/badges/overview)\n\nExample: An unread count belongs on the inbox icon as a badge. A selectable Unread filter needs a chip or selection control with its own name and state.\n\n#### Material mapping\n\nRelationship: component.\n\nUpstream: requirements-prose-reviewed (2026-09-13); [evidence](https://m3.material.io/components/badges/overview).\n\nRequirements and boundaries: design reference feedback-material-review. Full visual parity and spoken output unverified.\n\n[Specification link](https://m3.material.io/components/badges/specs); full specs unreviewed.\n\n[Google guidelines](https://m3.material.io/components/badges/guidelines).\n\nSupport (2026-09-13, `llm.md#badges`): Small dot and large count badges.\n\nWeb adaptation: Expose counts through the enclosing control name when its icon is hidden.\n\nKnown boundary: A named count fixture is checked; changing counts, clipping and spoken updates remain unverified.\n\nFull parity remains unassessed. [Capability evidence](../references/capability-roadmap.md#badges).\n\n#### Contract\n\nA `<span class=\"badge\">` is the badge. Empty is the **small** 6dp dot; text is the **large** 16dp stadium. Nest it in the icon so it sits on the upper trailing edge of the 24dp glyph. Limit the label to four characters, including `+`. The default mapping is `error` / `on-error`.\n\nNesting has a consequence worth stating: **a badge inside a hidden icon is hidden with it.** `aria-hidden=\"true\"` covers the whole subtree, and `aria-hidden=\"false\"` on a descendant does not undo that — so a count that only exists inside a decorative icon is a count nobody hears. Two ways out, depending on what surrounds it:\n\n- Inside a control, put the count in the control's name — `<a aria-label=\"Inbox, 3 unread\">` — and leave the icon decorative. - Standing alone, make the icon the image: `role=\"img\"` with a label that includes the count.\n\n#### Syntax\n\n```html\n<span class=\"material-symbols\" role=\"img\" aria-label=\"Mail, unread\">mail<span class=\"badge\"></span></span>\n<span class=\"material-symbols\" role=\"img\" aria-label=\"Mail, 1 unread\">mail<span class=\"badge\">1</span></span>\n<span class=\"material-symbols\" role=\"img\" aria-label=\"Mail, 999+ unread\">mail<span class=\"badge\">999+</span></span>\n```\n\n#### Also documented\n\nThe Badges section of the [API reference](https://www.expressivecss.com/llm.md) also covers Small, Large, On navigation, In a list, Color.\n\n#### Rules\n\n- `badge-count-survives-a-hidden-icon`: A badge inside a hidden icon is hidden with it - aria-hidden=\"false\" on a descendant does not undo that. Put the count in the control name (aria-label=\"Inbox, 3 unread\"), or move the badge out of the icon into the flow.\n"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"file": "bar-chart.md",
|
|
32
|
+
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Bar chart\nRanked or long-labelled categories drawn as horizontal bars from a data table, grouped, stacked or as a meter.\n\nComponent ID: `bar-chart`\n\n[Component documentation](https://www.expressivecss.com/bar-chart.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/bar-chart.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.13.1)\n\nContract: ExpressiveCSS 0.13.1\n\nContract SHA-256: `18ec4146d300280bbcb74eb3b23f16d1015a0a3db86a8c59fc8348dbdf9d96c2`\n\n#### Selection and adaptation\n\nRuntime ownership: `auto-init`. No dedicated entry in the reviewed Google component inventory.\n\nExample: Visitors from five traffic sources, longest first, is a bar chart with values inside the bars. Visitors per month for a year is a column chart.\n\n#### Material mapping\n\nRelationship: none. Web extension; no dedicated entry was observed in the reviewed current M3 component inventory.\n\nUpstream: web-extension-inventory-reviewed (2026-10-07); [evidence](https://m3.material.io/components).\n\nRequirements and boundaries: design reference web-extensions-review. The inventory page renders client-side, so this review relies on the 2026-09-13 inventory review. Full visual parity and spoken output unverified.\n\nSupport (2026-10-07, `llm.md#bar-chart`): Horizontal bars drawn from zero from a figure's data table, grouped by row or stacked, with split, track, values and sparkline modifiers, negative values, gaps, a fixed value range, row labels, a legend, and a pointer and keyboard tooltip with a highlight band.\n\nWeb adaptation: The <table> stays in the page as the text alternative and the no-JavaScript fallback; the bars and their values are aria-hidden and the plot is one Tab stop with a polite live tooltip.\n\nKnown boundary: Drawing, the hidden table, grouped, stacked and split geometry, values, gaps, negative values, the value range, RTL, the tooltip, the band, keyboard movement and destroy are checked in a browser; screen reader output, forced colors and enlarged text remain unverified. There are no value axis labels.\n\nFull parity remains unassessed. [Capability evidence](../references/capability-roadmap.md#bar-chart).\n\n#### Contract\n\nA `<figure class=\"bar-chart\">` takes the same `<figcaption>` and `<table>` as a column chart. `AutoInit()` draws each row as a band of bars growing from zero along the inline axis, one bar per series, with the row's label at the inline start, a legend when there is more than one series, and a highlight band and tooltip that follow the pointer. The table stays in the page, visually hidden, as the chart's text alternative; without JavaScript it shows instead of the chart.\n\n#### Syntax\n\n```html\n<figure class=\"bar-chart track values\">\n <figcaption>Visitors by source</figcaption>\n <table>\n <thead>\n <tr><th>Source</th><th>Visitors</th></tr>\n </thead>\n <tbody>\n <tr><th>Search</th><td data-value=\"900000\">900K</td></tr>\n <tr><th>Social</th><td data-value=\"650000\">650K</td></tr>\n <tr><th>Email</th><td data-value=\"420000\">420K</td></tr>\n </tbody>\n </table>\n</figure>\n```\n\n#### Also documented\n\nThe Bar chart section of the [API reference](https://www.expressivecss.com/llm.md) also covers Tokens.\n\n#### Rules\n\n- `bar-chart-is-a-figure-with-a-table`: Write .bar-chart as a <figure> holding a <table> of the data. The drawn chart is hidden from assistive technology, so the table is all a screen reader reads, and it is what shows without JavaScript.\n- `bar-chart-has-a-caption`: Name a .bar-chart with a <figcaption>. It names the focusable plot as well as the figure; hide it with .visually-hidden when nearby text already names the chart.\n"
|
|
17
33
|
},
|
|
18
34
|
{
|
|
19
35
|
"file": "bottom-sheet.md",
|
|
20
|
-
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Bottom sheet\nSecondary content anchored to the bottom of the screen.\n\nComponent ID: `bottom-sheet`\n\n[Component documentation](https://www.expressivecss.com/bottom-sheet.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/bottom-sheet.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.
|
|
36
|
+
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Bottom sheet\nSecondary content anchored to the bottom of the screen.\n\nComponent ID: `bottom-sheet`\n\n[Component documentation](https://www.expressivecss.com/bottom-sheet.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/bottom-sheet.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.13.1)\n\nContract: ExpressiveCSS 0.13.1\n\nContract SHA-256: `18ec4146d300280bbcb74eb3b23f16d1015a0a3db86a8c59fc8348dbdf9d96c2`\n\n#### Selection and adaptation\n\nRuntime ownership: `shared-runtime`. [Google guidance](https://m3.material.io/components/bottom-sheets/overview)\n\n- expanded window: replace-with [side-sheet](./side-sheet.md). Persistent secondary content fits beside the main task.\n\n#### Material mapping\n\nRelationship: component.\n\nUpstream: requirements-prose-reviewed (2026-09-13); [evidence](https://m3.material.io/components/bottom-sheets/overview).\n\nRequirements and boundaries: design reference layout-material-review. Full visual parity and spoken output unverified.\n\n[Specification link](https://m3.material.io/components/bottom-sheets/specs); full specs unreviewed.\n\n[Google guidelines](https://m3.material.io/components/bottom-sheets/guidelines).\n\nSupport (2026-09-18, `llm.md#bottom-sheet`): Standard and modal bottom sheets.\n\nWeb adaptation: Native dialog.show()/showModal(); shared runtime handles dragging and dismissal.\n\nKnown boundary: The shared handle dismisses rather than cycling sheet heights. Long-content scrolling and enlarged-text action access are checked; native zoom and physical touch remain unverified.\n\nFull parity remains unassessed. [Capability evidence](../references/capability-roadmap.md#bottom-sheet).\n\n#### Contract\n\nA recent flick dismisses the sheet even if the pointer stops moving at release. For short drags, pausing for at least 100ms or reversing direction lets it snap back.\n\nA `dialog.bottom-sheet` (or `.bottom`) is secondary content anchored to the bottom. Use it on Compact and Medium windows. `showModal()` is the modal variant (scrim). `show()` is the standard variant (no scrim). Same sheet either way: `surface-container-low`, 28dp top corners, 640dp max, 56dp side inset from the Medium breakpoint, 72dp top inset, 32×4 drag handle in a 48dp hit target. Drag the handle down to dismiss. A named `<button>` handle also dismisses on click, tap, <kbd>Enter</kbd>, or <kbd>Space</kbd>. A decorative handle needs another non-drag pointer and keyboard dismiss path, such as a `form method=\"dialog\"` close button. Verify native Escape against the opening mode, browser, and `closedby` policy.\n\n#### Syntax\n\n```html\n<dialog class=\"bottom-sheet\" aria-labelledby=\"open-file-title\">\n <h2 id=\"open-file-title\">Open file</h2>\n <div>…</div>\n <form method=\"dialog\">\n <button type=\"submit\" class=\"text\" value=\"cancel\">Cancel</button>\n </form>\n</dialog>\n```\n\n#### Rules\n\n- `dialog-is-named`: A <dialog> takes no name from its heading. Point aria-labelledby at that heading, or give it an aria-label - otherwise it opens announced as just \"dialog\".\n- `drag-handle-is-hidden-or-a-control`: A drag handle has no text and reports nothing, so on any element but a <button> it is decoration and needs aria-hidden=\"true\". Left exposed it arrives as an unlabelled blank in the reading order. Make it a <button> with a name if it is meant to be operated.\n- `drag-handle-button-is-not-hidden`: aria-hidden on a <button> hides it from assistive technology without taking it out of the tab order, so keyboard focus lands on something that is not there. Decoration is a <span>; a control stays exposed.\n- `drag-handle-button-is-named`: The handle is a bar drawn in CSS - there is no text inside it to be named by. A handle worth making a control is worth an aria-label saying what it moves.\n"
|
|
21
37
|
},
|
|
22
38
|
{
|
|
23
39
|
"file": "breadcrumbs.md",
|
|
24
|
-
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Breadcrumbs\nThe path to this page, from the HTML.\n\nComponent ID: `breadcrumbs`\n\n[Component documentation](https://www.expressivecss.com/breadcrumbs.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/breadcrumbs.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.
|
|
40
|
+
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Breadcrumbs\nThe path to this page, from the HTML.\n\nComponent ID: `breadcrumbs`\n\n[Component documentation](https://www.expressivecss.com/breadcrumbs.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/breadcrumbs.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.13.1)\n\nContract: ExpressiveCSS 0.13.1\n\nContract SHA-256: `18ec4146d300280bbcb74eb3b23f16d1015a0a3db86a8c59fc8348dbdf9d96c2`\n\n#### Selection and adaptation\n\nRuntime ownership: `css-only`. No dedicated entry in the reviewed Google component inventory.\n\nExample: Overview/Activity/Billing are peers, not ancestors. Use tabs for their same-page panels, not a breadcrumb trail.\n\n#### Material mapping\n\nRelationship: none. Web extension; no dedicated entry was observed in the reviewed current M3 component inventory.\n\nUpstream: web-extension-inventory-reviewed (2026-09-13); [evidence](https://m3.material.io/components).\n\nRequirements and boundaries: design reference web-extensions-review. Full visual parity and spoken output unverified.\n\nSupport (2026-09-13, `llm.md#breadcrumbs`): Breadcrumb links and app-bar composition.\n\nWeb adaptation: Labelled navigation with an ordered list and current-page link.\n\nKnown boundary: Named navigation and current-page markup are checked; CSS separator speech and long-path reflow remain unverified.\n\nFull parity remains unassessed. [Capability evidence](../references/capability-roadmap.md#breadcrumbs).\n\n#### Contract\n\nShow the current location when the page sits several layers deep.\n\nBreadcrumbs are a good way to display your current location. This is usually used when you have multiple layers of content.\n\n#### Syntax\n\n```html\n<nav aria-label=\"Breadcrumb\">\n <ol>\n <li><a href=\"/\">Home</a></li>\n <li><a href=\"/library\">Library</a></li>\n <li><a href=\"/library/data\" aria-current=\"page\">Data</a></li>\n </ol>\n</nav>\n```\n\n#### Also documented\n\nThe Breadcrumbs section of the [API reference](https://www.expressivecss.com/llm.md) also covers Basic, Navbar style.\n\n#### Rules\n\n- `breadcrumb-is-ordered-list`: Crumbs are an ordered list - nav > ol > li > a. Loose anchors give no count and no position, so \"3 of 4\" is never announced.\n- `breadcrumb-marks-current`: The last crumb is the page you are on; it needs aria-current=\"page\" to say so.\n\n#### Guide checks\n\n- Label every <nav> landmark with aria-label or aria-labelledby.\n"
|
|
25
41
|
},
|
|
26
42
|
{
|
|
27
43
|
"file": "button-groups.md",
|
|
28
|
-
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Button groups\nRelated buttons that bump and reshape against each other, in two variants and five sizes.\n\nComponent ID: `button-groups`\n\n[Component documentation](https://www.expressivecss.com/button-groups.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/button-groups.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.
|
|
44
|
+
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Button groups\nRelated buttons that bump and reshape against each other, in two variants and five sizes.\n\nComponent ID: `button-groups`\n\n[Component documentation](https://www.expressivecss.com/button-groups.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/button-groups.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.13.1)\n\nContract: ExpressiveCSS 0.13.1\n\nContract SHA-256: `18ec4146d300280bbcb74eb3b23f16d1015a0a3db86a8c59fc8348dbdf9d96c2`\n\n#### Selection and adaptation\n\nRuntime ownership: `auto-init`. [Google guidance](https://m3.material.io/components/button-groups/overview)\n\nExample: Standard groups fit Cut/Copy/Paste commands; connected groups fit toggle choices. A pressed button does not submit a radio value: use native inputs for form values, or explicitly integrate persistence.\n\n#### Material mapping\n\nRelationship: component. Google recommends the connected button group instead of segmented buttons. Match toggle state, required selection and persistence explicitly; native form submission is not supplied by aria-pressed.\n\nUpstream: requirements-prose-reviewed (2026-09-13); [evidence](https://m3.material.io/components/button-groups/overview).\n\nRequirements and boundaries: design reference layout-material-review. Full visual parity and spoken output unverified.\n\n[Specification link](https://m3.material.io/components/button-groups/specs); full specs unreviewed.\n\n[Google guidelines](https://m3.material.io/components/button-groups/guidelines).\n\nSupport (2026-09-18, `llm.md#button-groups`): Standard and connected groups; commands and opt-in selection.\n\nWeb adaptation: Runtime coordinates neighboring widths and selection; CSS handles connected shapes.\n\nKnown boundary: Native zoom, spoken output and physical-device behavior remain unverified.\n\nFull parity remains unassessed. [Capability evidence](../references/capability-roadmap.md#button-groups).\n\n#### Contract\n\nA Material 3 Expressive button group places related commands or toggle buttons in one visual cluster. The root is a `<div class=\"button-group\">`; direct children can be common buttons or `.icon-button` controls. `ButtonGroup` is initialized by `Expressive.AutoInit()` to coordinate standard press widths. Selection management is opt-in through `data-selection`.\n\nCommon buttons and icon buttons both inherit group sizing and participate in standard growth and connected shape morphing. Standard press growth is 15% of the item's measured width and is taken from its immediate neighbors, keeping the group's total width stable. An explicit size class on a child overrides the group size. Use a visible container style: filled, tonal, outlined, or elevated for common buttons; filled, tonal, or outlined for icon buttons. Material warns against text buttons and standard icon buttons in groups because their transparent containers hide the shape interaction.\n\nAction-group items are direct controls: a `<button>`, or an `<a class=\"button\" href>` when it navigates. Selection groups accept only native `<button type=\"button\">` children, each with `aria-pressed`. A wrapper loses the gap and connected corners. Every icon-only item carries its own accessible name because the icon is `aria-hidden`.\n\n#### Syntax\n\n```html\n<div class=\"button-group\" data-selection=\"multiple\" role=\"group\" aria-label=\"Text formatting\">\n <button type=\"button\" class=\"button tonal\" aria-pressed=\"false\">\n <span class=\"material-symbols\" aria-hidden=\"true\">format_bold</span>\n Bold\n </button>\n <button type=\"button\" class=\"button tonal\" aria-pressed=\"false\">\n <span class=\"material-symbols\" aria-hidden=\"true\">format_italic</span>\n Italic\n </button>\n</div>\n```\n\n#### Rules\n\n- `button-group-is-not-an-authored-composite-widget`: A button group takes no composite role; its buttons are reached with Tab, not arrow keys. `role=\"group\"` with an aria-label is the role that fits, and it promises no keyboard model.\n- `button-group-items-are-controls`: Every item in a group is a control the user can reach, and one the sheet styles as a button: a <button>, or an <a class=\"button\" href> when it navigates. A bare <a href> passes for a control and renders as a link - none of the group's corners, press behaviour or inherited size reach it. A wrapper element is refused too: the gap and the connected corners are written against direct children, so a nested <div> loses both.\n- `button-group-items-have-containers`: Material warns against text buttons in a group because they have no visible container for the shape interaction. Use filled, tonal, outlined, or elevated buttons.\n- `button-group-icon-items-have-containers`: Material warns against standard icon buttons in a group because they have no visible container for the shape interaction. Use a filled, tonal, or outlined icon button.\n- `button-group-selection-mode-is-valid`: ButtonGroup supports data-selection=\"single\" or data-selection=\"multiple\". Any other value exposes toggle state without a defined selection model.\n- `button-group-required-needs-selection`: data-selection-required only has meaning on a single- or multiple-selection ButtonGroup.\n- `button-group-connected-is-selectable`: Material connected button groups contain toggleable choices. Add data-selection=\"single|multiple\", or use the standard group for one-shot actions.\n- `button-group-selection-items-are-buttons`: Selection items are native buttons. Links navigate and cannot represent toggle-button state.\n- `button-group-selection-button-type`: A selection button inside a form must not submit it; set type=\"button\".\n- `button-group-selection-state-is-pressed`: Author each toggle button's initial aria-pressed value. ButtonGroup maintains that state after initialization.\n- `button-group-selection-state-is-boolean`: A toggle button's initial aria-pressed value is true or false; mixed is not a Button Group state.\n- `button-group-selection-does-not-use-choice-state`: These are toggle buttons, not radios, checkboxes, options, or tabs. Expose their state with aria-pressed.\n- `button-group-action-does-not-claim-selection`: aria-pressed requires ButtonGroup selection behavior. Add data-selection=\"single|multiple\" or remove the toggle state.\n"
|
|
29
45
|
},
|
|
30
46
|
{
|
|
31
47
|
"file": "buttons.md",
|
|
32
|
-
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Buttons\nMaterial Design 3 common buttons, icon buttons, and FABs — from the HTML.\n\nComponent ID: `buttons`\n\n[Component documentation](https://www.expressivecss.com/buttons.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/buttons.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.
|
|
48
|
+
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Buttons\nMaterial Design 3 common buttons, icon buttons, and FABs — from the HTML.\n\nComponent ID: `buttons`\n\n[Component documentation](https://www.expressivecss.com/buttons.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/buttons.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.13.1)\n\nContract: ExpressiveCSS 0.13.1\n\nContract SHA-256: `18ec4146d300280bbcb74eb3b23f16d1015a0a3db86a8c59fc8348dbdf9d96c2`\n\n#### Selection and adaptation\n\nRuntime ownership: `css-only`. [Google guidance](https://m3.material.io/components/buttons/overview)\n\nExample: Use radios for saved form values; buttons run commands.\n\n#### Material mapping\n\nRelationship: component. Google no longer recommends 24dp padding for small buttons; use the current 16dp inset. This is spacing guidance, not removal of a button style.\n\nUpstream: overview-specs-guidelines-accessibility-prose-reviewed (2026-09-13); [evidence](https://m3.material.io/components/buttons/overview).\n\nRendered prose reviewed; collapsed token tables, image-only measurements, runtime and full parity unverified. See the design priority component review for requirement-level findings.\n\n[Specification link](https://m3.material.io/components/buttons/specs); full specs unreviewed.\n\n[Google guidelines](https://m3.material.io/components/buttons/guidelines).\n\nSupport (2026-09-07, `llm.md#buttons`): Filled, tonal, outlined, elevated, and text; xsmall through xlarge.\n\nWeb adaptation: Native buttons run commands; a.button links navigate.\n\nKnown boundary: Standalone common buttons do not document a square/toggle shape-morph contract; do not borrow icon-button or connected-group modifiers.\n\nFull parity remains unassessed. [Capability evidence](../references/capability-roadmap.md#buttons).\n\n#### Contract\n\nA `<button>` is a filled common button. An `<a class=\"button\">` is the same thing for a link — on an anchor the class is `button`; the older `.btn` spelling only ever worked on a `<button>`, which is styled as one anyway. Put a `<span class=\"material-symbols\" aria-hidden=\"true\">` icon before or after the label and wrap the label in its own `<span>` — there is no `icon-left` / `icon-right` class, the order of the two spans is the placement. Add `circle` for a round common button and name it with `aria-label`, since the icon is hidden. Icon buttons are `.icon-button`.\n\nTwo independent axes dress it: a style (`filled`, `tonal`, `outlined`, `elevated`, `text`) and a size (`xsmall` through `xlarge`). Any style combines with any size — nine classes, not twenty-five, because the size sets the geometry and the style sets the color.\n\nTokens follow the [M3 button spec](https://m3.material.io/components/buttons/specs). The default is the small size: 40dp tall, label `label-large`, fully round corners, a 20dp icon on an 8dp gap, and a symmetric 16dp inset. State layers are 8% hover and 10% focus or press. Disabled is `on-surface` at 38% on a 12% container.\n\n#### Syntax\n\n```html\n<button>Create</button>\n<button>\n <span class=\"material-symbols\" aria-hidden=\"true\">add</span><span>Create</span>\n</button>\n<button>\n <span>Send</span><span class=\"material-symbols\" aria-hidden=\"true\">send</span>\n</button>\n<button class=\"circle\" aria-label=\"Add\">\n <span class=\"material-symbols\" aria-hidden=\"true\">add</span>\n</button>\n\n<a class=\"button\" href=\"/docs\">Docs</a>\n```\n\n#### Also documented\n\nThe Buttons section of the [API reference](https://www.expressivecss.com/llm.md) also covers Filled, Tonal, Outlined, Elevated, Text, Icon buttons, Floating, Sizes, Disabled.\n\n#### Rules\n\n- `icon-only-control-is-named`: Every icon is hidden from assistive technology, so a control whose only content is one has no name left. Give it an aria-label.\n- `fab-no-small-variant`: The 40dp small FAB has been removed. Use circle extra for a 56dp FAB.\n- `extended-fab-requires-size`: The baseline extended FAB has been removed. Specify extend small, extend medium or extend large.\n- `fab-no-surface-color`: Use a primary, secondary or tertiary container color for a FAB instead of a surface color.\n"
|
|
33
49
|
},
|
|
34
50
|
{
|
|
35
51
|
"file": "cards.md",
|
|
36
|
-
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Cards\nMaterial Design 3 cards, from the HTML.\n\nComponent ID: `cards`\n\n[Component documentation](https://www.expressivecss.com/cards.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/cards.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.
|
|
52
|
+
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Cards\nMaterial Design 3 cards, from the HTML.\n\nComponent ID: `cards`\n\n[Component documentation](https://www.expressivecss.com/cards.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/cards.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.13.1)\n\nContract: ExpressiveCSS 0.13.1\n\nContract SHA-256: `18ec4146d300280bbcb74eb3b23f16d1015a0a3db86a8c59fc8348dbdf9d96c2`\n\n#### Selection and adaptation\n\nRuntime ownership: `auto-init`. [Google guidance](https://m3.material.io/components/cards/overview)\n\nExample: A hundred compact activity records need a list. Giving every row a card adds separation without a separate content or action group.\n\n#### Material mapping\n\nRelationship: component.\n\nUpstream: requirements-prose-reviewed (2026-09-13); [evidence](https://m3.material.io/components/cards/overview).\n\nRequirements and boundaries: design reference feedback-material-review. Full visual parity and spoken output unverified.\n\n[Specification link](https://m3.material.io/components/cards/specs); full specs unreviewed.\n\n[Google guidelines](https://m3.material.io/components/cards/guidelines).\n\nSupport (2026-09-13, `llm.md#cards`): Elevated, filled, outlined, horizontal, and directly actionable cards.\n\nWeb adaptation: Native article anatomy; primary-action links preserve navigation semantics.\n\nKnown boundary: Existing card lifecycle and geometry evidence does not cover every selected variant, contrast and reflow state.\n\nFull parity remains unassessed. [Capability evidence](../references/capability-roadmap.md#cards).\n\n#### Contract\n\nAn `<article>` is an elevated card. Any heading is the headline, `<p class=\"subhead\">` is the optional subhead, a `<p>` is supporting copy, direct `<img>`, `<picture>`, or `<figure>` is media, and direct `<div class=\"actions\">` is the action row. Include only the slots the content needs. There is no `card-content`, `card-title`, `card-action`, `card` or `card-panel` class—the element is the component, and the action row is not a `<nav>`. Tokens follow the [M3 card spec](https://m3.material.io/components/cards/specs): all variants use 12dp corners; elevated rests at level 1, filled and outlined at level 0. A directly actionable card wraps its primary content in one direct `a.primary-action[href]` and has no second link or control. Horizontal cards preserve that source order, use content height with a 240px minimum unless a size helper fixes the expanded height, and stack below 600px with fixed heights reset to content. During reordering, `.dragged` or `.picked-up` preserves the 16% state layer and dragged elevation while the primary action remains hovered or pressed.\n\nA Card reveal is a disclosure with exactly one identified direct `<aside>` and an enabled, accessibly named `button.card-reveal-trigger[type=\"button\"]` outside that panel. Its `aria-controls` must resolve to the panel, and its closest `<article>` owns it—even when a complete nested card sits inside an outer reveal panel. After accepting the contract, Cards writes `aria-expanded`, marks the closed panel `inert`, closes it with Escape, and returns focus from the panel to the trigger. Rejected, disabled-only, unidentified, and multi-panel disclosures remain visible and unmanaged.\n\n#### Syntax\n\n```html\n<article>\n <header>\n <h3>Weekend in the mountains</h3>\n <p class=\"subhead\">Three-day itinerary</p>\n </header>\n <p>Explore trails and overlooks.</p>\n <img src=\"images/mountains.jpg\" alt=\"Mountain valley beneath a cloudy sky\">\n <div class=\"actions\">\n <button type=\"button\" class=\"text\">Share</button>\n <button type=\"button\" class=\"tonal\">View trip</button>\n </div>\n</article>\n```\n\n#### Also documented\n\nThe Cards section of the [API reference](https://www.expressivecss.com/llm.md) also covers Variants, Primary action and states, Collections, Media, Horizontal, Reveal, Expanding card, Sizes.\n\n#### Rules\n\n- `card-action-row-not-nav`: Cards do not contain navigation or tabs. Put navigation outside the card and use <div class=\"actions\"> for its action buttons.\n- `card-primary-action-is-control`: A directly actionable card uses one native link as its primary action. Use the action row for commands; common button styles do not turn a button into the card container.\n- `card-disabled-link-has-no-href`: A disabled card destination must not retain href. aria-disabled does not stop a link from navigating when activated from the keyboard.\n- `card-disabled-link-not-tabbable`: A disabled card destination is not operable, so remove it from the tab order with tabindex=\"-1\".\n- `card-primary-action-is-only-action`: A directly actionable card contains no second action. Use one primary-action link, or make the card non-actionable and put its controls in the action row.\n- `card-primary-action-is-single`: A directly actionable card has exactly one direct primary-action link. Remove duplicate primary actions or use a non-actionable card with an action row.\n- `card-reveal-has-trigger`: A card with a direct reveal panel requires a native card-reveal-trigger button whose closest article is that card and which sits outside the reveal panel.\n- `card-reveal-has-one-panel`: A card reveal has exactly one direct aside panel. With multiple direct panels, Cards rejects disclosure setup and every panel remains visible and unmanaged.\n- `card-reveal-trigger-is-button`: The reveal trigger is a native button so Enter, Space, disabled state, and focus work without synthetic keyboard handling.\n- `card-reveal-trigger-type-button`: A card reveal button may appear inside a form, so set type=\"button\" to prevent accidental submission.\n- `card-reveal-trigger-has-label`: The reveal button needs a non-empty accessible name describing the details it toggles; icon-only triggers should use aria-label.\n- `card-reveal-trigger-controls-panel`: Name the reveal panel from aria-controls so assistive technology can relate the disclosure button to its content.\n- `card-reveal-panel-has-id`: The reveal panel needs the id referenced by its trigger's aria-controls.\n- `card-reveal-expanded-is-not-authored`: Cards writes aria-expanded on the disclosure button. Do not author a stale initial value, and do not put aria-expanded on the panel.\n- `expanding-card-trigger-is-button`: The media-sized trigger opens a dialog, so use a button for native keyboard and disabled behavior.\n- `expanding-card-close-is-button`: The back affordance closes the expanded surface, so use a button for native keyboard behavior.\n"
|
|
37
53
|
},
|
|
38
54
|
{
|
|
39
55
|
"file": "carousel.md",
|
|
40
|
-
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Carousel\nMaterial 3 adaptive carousels for visual collections.\n\nComponent ID: `carousel`\n\n[Component documentation](https://www.expressivecss.com/carousel.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/carousel.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.
|
|
56
|
+
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Carousel\nMaterial 3 adaptive carousels for visual collections.\n\nComponent ID: `carousel`\n\n[Component documentation](https://www.expressivecss.com/carousel.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/carousel.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.13.1)\n\nContract: ExpressiveCSS 0.13.1\n\nContract SHA-256: `18ec4146d300280bbcb74eb3b23f16d1015a0a3db86a8c59fc8348dbdf9d96c2`\n\n#### Selection and adaptation\n\nRuntime ownership: `auto-init`. [Google guidance](https://m3.material.io/components/carousel/overview)\n\nExample: A transaction history must support scanning rows and values. Use a list instead of hiding records across carousel slides.\n\n#### Material mapping\n\nRelationship: component.\n\nUpstream: requirements-prose-reviewed (2026-09-13); [evidence](https://m3.material.io/components/carousel/overview).\n\nRequirements and boundaries: design reference feedback-material-review. Full visual parity and spoken output unverified.\n\n[Specification link](https://m3.material.io/components/carousel/specs); full specs unreviewed.\n\n[Google guidelines](https://m3.material.io/components/carousel/guidelines).\n\nSupport (2026-09-17, `llm.md#carousel`): Adaptive visual collections with documented layout variants.\n\nWeb adaptation: Runtime owns sizing and navigation; reduced motion removes parallax and morphing.\n\nKnown boundary: Rendered navigation and application-owned pause controls have browser coverage; screen readers, native zoom and physical-device review remain pending.\n\nFull parity remains unassessed. [Capability evidence](../references/capability-roadmap.md#carousel).\n\n#### Contract\n\nA Material 3 adaptive carousel for visual collections.\n\nThe default is multi-browse: one large, one medium, and one small item adapt as the active item changes. `AutoInit()` starts every `.carousel` except those marked `no-autoinit`. Give the container an accessible name and every direct item the `carousel-item` class.\n\n#### Syntax\n\n```html\n<div class=\"carousel\" aria-label=\"Featured landscapes\">\n <a class=\"carousel-item\" href=\"mountain-lake.html\">\n <img src=\"images/mountain-lake.jpg\" alt=\"Mountain lake\">\n <span class=\"carousel-item-content\">Mountain lake</span>\n </a>\n <a class=\"carousel-item\" href=\"forest-path.html\">\n <img src=\"images/forest-path.jpg\" alt=\"Forest path\">\n <span class=\"carousel-item-content\">Forest path</span>\n </a>\n <a class=\"carousel-item\" href=\"rocky-coastline.html\">\n <img src=\"images/rocky-coastline.jpg\" alt=\"Rocky coastline\">\n <span class=\"carousel-item-content\">Rocky coastline</span>\n </a>\n</div>\n<div class=\"mt-1\"><a class=\"button text\" href=\"all-landscapes.html\">Show all</a></div>\n```\n\n#### Options\n\n| Name | Type | Default | Description |\n| --- | --- | --- | --- |\n| `duration` | Number | `200` | Milliseconds allowed for a programmatic scroll to land, and the part of each auto-advance rest that is not `interval`. |\n| `fullWidth` | Boolean | `false` | Full-width compatibility layout used by swipeable tabs. Prefer an M3 layout class for new carousels. |\n| `indicators` | Boolean | `false` | Legacy paging dots. M3 recommends a nearby Show all path instead of overlay controls. |\n| `noWrap` | Boolean | `false` | Stop auto-advance at the last item instead of looping back to the first. Arrow keys and `set()` stop at the ends either way — a scroll track has ends. |\n| `interval` | Number | `0` | Milliseconds to rest between automatic advances, on top of `duration`; a full cycle takes `duration + interval`. `0` leaves auto-advance off. Each rest is armed by the move before it rather than on a fixed phase. |\n| `height` | Number | `null` | Fixed track height in pixels. `null` sizes the carousel from its content. |\n| `onCycleTo` | Function | `null` | Called when the active item changes. |\n| `i18n` | Object | `{ carousel: 'Carousel', item: 'Item', of: 'of', indicators: 'Slides', slide: 'Slide' }` | Generated accessible label strings. `indicators` names the indicator row and `slide` prefixes each dot, giving \"Slide 1\". Partial objects are merged with the defaults. |\n\n#### Methods\n\n`next()` and `prev()` move one item or an optional item count. `set(index, callback)` moves to a zero-based item index. `pause()` and `start()` stop and resume auto-advance, and do nothing without an `interval`. `destroy()` removes generated labels, size roles, indicators, listeners, the scroll-track wrapper, and the auto-advance timer.\n\nAn `interval` makes the carousel advance on its own, so the pause contract is mandatory: it always pauses on hover, on focus within, and while the tab is hidden, and `prefers-reduced-motion: reduce` suppresses auto-advance entirely. No option disables any of that. An explicit `noWrap: true` stops auto-advance after one pass instead of looping. Arrow keys and `set()` stop at the ends either way — a scroll track has ends, and auto-advance is the one caller that can loop back past them. A `height` gives the indicators their own row below the track (`.fixed-height`) instead of laying them over the media; markup can do the same by carrying `.fixed-height` and setting `--carousel-height`.\n\n#### Also documented\n\nThe Carousel section of the [API reference](https://www.expressivecss.com/llm.md) also covers Layouts, Tokens, Initialization, Properties.\n\n#### Rules\n\n- `hidden-subtree-holds-nothing-focusable`: Hidden from assistive technology but still in the tab order: focus lands on a control the user has no way to perceive. Take away the tab stop (tabindex=\"-1\"), or mark the subtree inert when it is genuinely not displayed - inert also removes it from hit-testing, which is wrong for anything still visible.\n- `carousel-not-a-composite-widget`: A carousel takes no composite role; it is a group of slides with independent controls.\n- `carousel-indicators-are-not-tabs`: Carousel indicators are independent controls, not tabs.\n"
|
|
41
57
|
},
|
|
42
58
|
{
|
|
43
59
|
"file": "checkboxes.md",
|
|
44
|
-
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Checkboxes\nMaterial Design 3 checkboxes, from the HTML.\n\nComponent ID: `checkboxes`\n\n[Component documentation](https://www.expressivecss.com/checkboxes.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/checkboxes.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.
|
|
60
|
+
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Checkboxes\nMaterial Design 3 checkboxes, from the HTML.\n\nComponent ID: `checkboxes`\n\n[Component documentation](https://www.expressivecss.com/checkboxes.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/checkboxes.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.13.1)\n\nContract: ExpressiveCSS 0.13.1\n\nContract SHA-256: `18ec4146d300280bbcb74eb3b23f16d1015a0a3db86a8c59fc8348dbdf9d96c2`\n\n#### Selection and adaptation\n\nRuntime ownership: `css-only`. [Google guidance](https://m3.material.io/components/checkbox/overview)\n\n#### Material mapping\n\nRelationship: component.\n\nUpstream: requirements-prose-reviewed (2026-09-13); [evidence](https://m3.material.io/components/checkbox/overview).\n\nRequirements and boundaries: design reference inputs-material-review. Full visual parity and spoken output unverified.\n\n[Specification link](https://m3.material.io/components/checkbox/specs); full specs unreviewed.\n\n[Google guidelines](https://m3.material.io/components/checkbox/guidelines).\n\nSupport (2026-09-13, `llm.md#checkboxes`): Unchecked, checked, indeterminate, and disabled.\n\nWeb adaptation: Native checkbox; indeterminate is set through the DOM property.\n\nKnown boundary: Native state coverage does not verify parent aggregation or every target and forced-colors state.\n\nFull parity remains unassessed. [Capability evidence](../references/capability-roadmap.md#checkboxes).\n\n#### Contract\n\nA `<label>` wrapping `<input type=\"checkbox\">` is the control. The label text is a sibling of the input — no extra class, no required `<span>`. They are CSS only. There is no JavaScript component and nothing to AutoInit.\n\nTokens follow the [M3 checkbox spec](https://m3.material.io/components/checkbox/specs). The container is 18dp with 2dp corners. Unselected is a 2dp `on-surface-variant` outline. Selected is a `primary` fill with an `on-primary` check. Indeterminate is the same fill with a dash. The state layer is 40dp at 8% hover and 10% focus. The touch target is 48dp. The label is `body-large` / `on-surface`. Disabled is 38%.\n\nPut the input first. Add `checked` or `disabled` on the input. Indeterminate is not an HTML attribute — set `element.indeterminate = true` in script. `filled-in` is a no-op: the selected state is always the filled box.\n\n#### Syntax\n\n```html\n<label>\n <input type=\"checkbox\">\n Red\n</label>\n<label>\n <input type=\"checkbox\" checked>\n Yellow\n</label>\n<label>\n <input type=\"checkbox\" disabled>\n Brown\n</label>\n```\n\n#### Also documented\n\nThe Checkboxes section of the [API reference](https://www.expressivecss.com/llm.md) also covers In a row, Error.\n\n#### Rules\n\n- `checkbox-labelled`: A checkbox must be inside its <label> or carry an id a label points at.\n"
|
|
45
61
|
},
|
|
46
62
|
{
|
|
47
63
|
"file": "chips.md",
|
|
48
|
-
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Chips\nSmall blocks for contacts, tags, and filters.\n\nComponent ID: `chips`\n\n[Component documentation](https://www.expressivecss.com/chips.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/chips.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.
|
|
64
|
+
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Chips\nSmall blocks for contacts, tags, and filters.\n\nComponent ID: `chips`\n\n[Component documentation](https://www.expressivecss.com/chips.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/chips.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.13.1)\n\nContract: ExpressiveCSS 0.13.1\n\nContract SHA-256: `18ec4146d300280bbcb74eb3b23f16d1015a0a3db86a8c59fc8348dbdf9d96c2`\n\n#### Selection and adaptation\n\nRuntime ownership: `auto-init`. [Google guidance](https://m3.material.io/components/chips/overview)\n\nExample: A passive unread count is a badge. Turning it into a filter chip falsely suggests a selectable state unless filtering is implemented.\n\n#### Material mapping\n\nRelationship: component.\n\nUpstream: requirements-prose-reviewed (2026-09-13); [evidence](https://m3.material.io/components/chips/overview).\n\nRequirements and boundaries: design reference inputs-material-review. Full visual parity and spoken output unverified.\n\n[Specification link](https://m3.material.io/components/chips/specs); full specs unreviewed.\n\n[Google guidelines](https://m3.material.io/components/chips/guidelines).\n\nSupport (2026-09-13, `llm.md#chips`): Assist, suggestion, filter, input, and display chips.\n\nWeb adaptation: Element choice defines behavior; display chips are a framework extension.\n\nKnown boundary: Rendered deletion, focus recovery and wrapped action/delete controls are checked. Full effective-target sizing, spoken output and physical-device behavior remain unverified.\n\nFull parity remains unassessed. [Capability evidence](../references/capability-roadmap.md#chips).\n\n#### Contract\n\nA chip is a `.chip`, and **the element says which kind it is** — the four Material 3 chip types, plus a non-interactive display chip, across three root elements. Add `outlined` for a bordered style. Static chips are CSS. The JavaScript plugin lives on a `.chips` wrapper.\n\nA long label wraps and the chip grows from its 32dp minimum, with 4dp above and below the lines. It never grows wider than its parent.\n\n#### Syntax\n\n```html\n<span class=\"chip outlined\">Information</span>\n\n<span class=\"chip\">\n <img src=\"photo.jpg\" alt=\"\"> Jane Doe\n</span>\n\n<button type=\"button\" class=\"chip\">\n <span class=\"material-symbols\" aria-hidden=\"true\">event</span>\n Add to calendar\n</button>\n\n<input type=\"checkbox\" class=\"chip-input\" id=\"filter-flights\">\n<label class=\"chip\" for=\"filter-flights\">\n <span class=\"material-symbols\" aria-hidden=\"true\">check</span>\n Flights\n</label>\n\n<span class=\"chip\">\n Tag\n <button type=\"button\" class=\"close\" aria-label=\"Remove Tag\">\n <span class=\"material-symbols\" aria-hidden=\"true\">close</span>\n </button>\n</span>\n```\n\n#### Options\n\n| Name | Type | Default | Description |\n| --- | --- | --- | --- |\n| `data` | Array | `[]` | Initial chips. Each item is a chip data object. |\n| `placeholder` | String | `''` | Placeholder when there are no chips. Requires `allowUserInput`. |\n| `secondaryPlaceholder` | String | `''` | Placeholder after at least one chip exists. |\n| `closeIconClass` | String | `'material-symbols'` | Class on the icon inside the delete button. |\n| `allowUserInput` | Boolean | `false` | If true, render a text field and a delete button per chip, so the user can add and remove chips. |\n| `i18n` | Object | `{ remove: 'Remove' }` | Strings the component generates. `remove` prefixes the delete button's accessible name, giving \"Remove Apple\". |\n| `autocompleteOptions` | Object | `{}` | Options passed to Autocomplete on the input. A non-empty object enables autocomplete. |\n| `autocompleteOnly` | Boolean | `false` | If true, Enter will not add a value that is not in the autocomplete list. |\n| `limit` | Number | `Infinity` | Maximum number of chips. |\n| `onChipAdd` | Function | `null` | Called after a chip is added. Receives the container and the chip element. |\n| `onChipSelect` | Function | `null` | Called when a chip is selected. Receives the container and the chip element. |\n| `onChipDelete` | Function | `null` | Called after a chip is deleted. Receives the container and the chip element. |\n\n#### Methods\n\n- `.addChip()`: Add a chip, including when `allowUserInput` is false. Numeric id `0` is valid. Ignored if `id` is missing, already present, or the limit is reached.\n- `.deleteChip()`: Delete the chip at this index.\n- `.selectChip()`: Focus the chip at this index.\n- `.getData()`: The current chips as an array of chip data objects.\n- `.destroy()`: Destroy the plugin instance, remove rendered chips, and tear down its event handlers. This also destroys the owned Autocomplete and its Menu.\n\n#### Also documented\n\nThe Chips section of the [API reference](https://www.expressivecss.com/llm.md) also covers Contacts, Tags, Javascript Plugin, Initialization, Properties.\n\n#### Rules\n\n- `chip-root-element`: A chip is <span> (display), <button> (assist/suggestion/input), or <label> (filter). Never <div>.\n- `chip-not-focusable-span`: A display chip is not a control and must not be in the tab order. Put the action on a nested <button>.\n- `chip-close-is-button`: The chip delete affordance is a control: use <button type=\"button\" class=\"close\">, not <i> or <span>.\n- `chip-close-type-button`: A <button> inside a form defaults to type=submit. Chip delete buttons must set type=\"button\".\n- `chip-close-has-label`: The delete button contains only an icon, so it needs an accessible name naming the chip it removes.\n- `chip-no-i-element`: <i> means idiomatic text, not icon. Use <span class=\"material-symbols\" aria-hidden=\"true\">.\n- `chip-icon-hidden`: A ligature icon is read aloud verbatim. Decorative icons inside a labelled control must be aria-hidden.\n- `filter-chip-label-for`: A filter chip is <input type=\"checkbox\"> + <label class=\"chip\">; the label must point at its input.\n"
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"file": "column-chart.md",
|
|
68
|
+
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Column chart\nValues compared across categories or periods, drawn as columns from a data table, grouped or stacked.\n\nComponent ID: `column-chart`\n\n[Component documentation](https://www.expressivecss.com/column-chart.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/column-chart.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.13.1)\n\nContract: ExpressiveCSS 0.13.1\n\nContract SHA-256: `18ec4146d300280bbcb74eb3b23f16d1015a0a3db86a8c59fc8348dbdf9d96c2`\n\n#### Selection and adaptation\n\nRuntime ownership: `auto-init`. No dedicated entry in the reviewed Google component inventory.\n\nExample: Signups per plan for each month of a half year is a grouped column chart. The same plans over three years of weekly data is a line chart.\n\n#### Material mapping\n\nRelationship: none. Web extension; no dedicated entry was observed in the reviewed current M3 component inventory.\n\nUpstream: web-extension-inventory-reviewed (2026-10-07); [evidence](https://m3.material.io/components).\n\nRequirements and boundaries: design reference web-extensions-review. The inventory page renders client-side, so this review relies on the 2026-09-13 inventory review. Full visual parity and spoken output unverified.\n\nSupport (2026-10-07, `llm.md#column-chart`): Columns drawn from zero from a figure's data table, grouped by row or stacked, with fade, track and sparkline modifiers, negative values, gaps, a fixed y range, x labels, a legend, and a pointer and keyboard tooltip with a highlight band.\n\nWeb adaptation: The <table> stays in the page as the text alternative and the no-JavaScript fallback; the columns are aria-hidden and the plot is one Tab stop with a polite live tooltip.\n\nKnown boundary: Drawing, the hidden table, grouped and stacked geometry, gaps, negative values, the y range, RTL, the tooltip, the band, keyboard movement and destroy are checked in a browser; screen reader output, forced colors and enlarged text remain unverified. There is no y axis labelling and no range (low to high) column; horizontal bars are the bar chart.\n\nFull parity remains unassessed. [Capability evidence](../references/capability-roadmap.md#column-chart).\n\n#### Contract\n\nA `<figure class=\"column-chart\">` takes the same `<figcaption>` and `<table>` as a line chart. The first column labels each group; each further column is a series named by its header cell. `AutoInit()` draws each row as a group of columns growing from zero, one per series, with x labels, a legend when there is more than one series, and a highlight band and tooltip that follow the pointer. The table stays in the page, visually hidden, as the chart's text alternative; without JavaScript it shows instead of the chart.\n\n#### Syntax\n\n```html\n<figure class=\"column-chart\">\n <figcaption>Subscriptions by plan</figcaption>\n <table>\n <thead>\n <tr><th>Month</th><th>Basic</th><th>Premium</th></tr>\n </thead>\n <tbody>\n <tr><th>Jan</th><td>5,500</td><td>3,500</td></tr>\n <tr><th>Feb</th><td>6,400</td><td>3,200</td></tr>\n <tr><th>Mar</th><td>7,600</td><td>3,400</td></tr>\n </tbody>\n </table>\n</figure>\n```\n\n#### Also documented\n\nThe Column chart section of the [API reference](https://www.expressivecss.com/llm.md) also covers Tokens.\n\n#### Rules\n\n- `column-chart-is-a-figure-with-a-table`: Write .column-chart as a <figure> holding a <table> of the data. The drawn chart is hidden from assistive technology, so the table is all a screen reader reads, and it is what shows without JavaScript.\n- `column-chart-has-a-caption`: Name a .column-chart with a <figcaption>. It names the focusable plot as well as the figure; hide it with .visually-hidden when nearby text already names the chart.\n"
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"file": "command-palette.md",
|
|
72
|
+
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Command palette\nA searchable list of commands in a dialog, opened from anywhere with a shortcut.\n\nComponent ID: `command-palette`\n\n[Component documentation](https://www.expressivecss.com/command-palette.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/command-palette.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.13.1)\n\nContract: ExpressiveCSS 0.13.1\n\nContract SHA-256: `18ec4146d300280bbcb74eb3b23f16d1015a0a3db86a8c59fc8348dbdf9d96c2`\n\n#### Selection and adaptation\n\nRuntime ownership: `auto-init`. No dedicated entry in the reviewed Google component inventory.\n\nExample: Finding an order by customer name is search over content. A command palette is for running actions and opening places in the app.\n\n#### Material mapping\n\nRelationship: none. Web extension; no dedicated entry was observed in the reviewed current M3 component inventory.\n\nUpstream: web-extension-inventory-reviewed (2026-10-06); [evidence](https://m3.material.io/components).\n\nRequirements and boundaries: design reference web-extensions-review. The inventory page renders client-side, so this review relies on the 2026-09-13 inventory review. Full visual parity and spoken output unverified.\n\nSupport (2026-10-06, `llm.md#command-palette`): A modal dialog with a filtering combobox and listbox, group headings, keywords, an empty message, a Ctrl or Command shortcut and native commandfor opening.\n\nWeb adaptation: A native modal <dialog>; commands are links and buttons, and the component adds the combobox and listbox roles and the active option.\n\nKnown boundary: The shortcut, filtering, arrow and Enter handling, closing, focus return, the native opener and destroy are checked in a browser; screen reader announcements and shortcut conflicts with assistive technology remain unverified.\n\nFull parity remains unassessed. [Capability evidence](../references/capability-roadmap.md#command-palette).\n\n#### Contract\n\nA `<dialog class=\"command-palette\">` holds a search `<input>`, a `<ul>` of commands and an optional `.command-palette-empty` message. Each command is an `<li>` holding a link or button: an optional leading icon, the label and an optional trailing `<kbd>` shortcut. An `<li class=\"label\">` heads a group. Running a command clicks its link or button, so commands are ordinary links and click handlers. Commands added to the list later are picked up. A command the page hides with `hidden` stays hidden, and one whose button is `disabled` is shown dimmed, marked `aria-disabled` and skipped by the arrow keys. `AutoInit()` starts every `dialog.command-palette`.\n\nCtrl+K, or Command+K on Apple platforms, opens and closes it from anywhere on the page unless the page has already handled the key, as an editor's own Ctrl+K does; a `commandfor` button with `command=\"show-modal\"` opens it too. Typing filters the commands by their text and any `data-keywords`, ignoring case and accents, and hides a group heading whose commands are all filtered out. Focus stays in the input: the arrow keys move the active command and Enter runs it. The dialog closes on Escape and returns focus. Each opening starts with an empty search.\n\nThe component makes the input a combobox and the list a listbox of options, and reports the active option with `aria-activedescendant`. Name the dialog and the input with `aria-label`. Give the empty message `role=\"status\"` so a search with no results is announced.\n\n#### Syntax\n\n```html\n<button type=\"button\" commandfor=\"commands\" command=\"show-modal\">\n Commands <kbd><kbd>Ctrl</kbd>+<kbd>K</kbd></kbd>\n</button>\n<dialog id=\"commands\" class=\"command-palette\" aria-label=\"Commands\">\n <input type=\"search\" placeholder=\"Type a command\" aria-label=\"Search commands\">\n <ul>\n <li class=\"label\">Files</li>\n <li>\n <button type=\"button\" data-keywords=\"create add\">\n <span class=\"material-symbols\" aria-hidden=\"true\">note_add</span>\n New file\n <kbd>Ctrl N</kbd>\n </button>\n </li>\n <li><a href=\"/settings\">\n <span class=\"material-symbols\" aria-hidden=\"true\">settings</span>\n Settings\n </a></li>\n </ul>\n <p class=\"command-palette-empty\" role=\"status\">No matching commands</p>\n</dialog>\n```\n\n#### Options\n\n| Name | Type | Default | Description |\n| --- | --- | --- | --- |\n| `shortcut` | String | `'k'` | Letter that toggles the palette with Command on Apple platforms or Ctrl elsewhere. A key event the page already handled is left alone. `null` turns it off. `data-shortcut` on the dialog sets it, and an empty `data-shortcut` turns it off. |\n| `closeOnRun` | Boolean | `true` | If true, the palette closes after a command runs. |\n\n#### Methods\n\n- `.open()`: Opens the palette as a modal dialog with the search input focused.\n- `.close()`: Closes the palette.\n- `.destroy()`: Removes the shortcut, listeners, roles and ids the component added.\n\n#### Also documented\n\nThe Command palette section of the [API reference](https://www.expressivecss.com/llm.md) also covers Tokens.\n\n#### Rules\n\n- `command-palette-is-a-dialog`: Write the palette as <dialog class=\"command-palette\">. The dialog makes it modal, closes it on Escape and returns focus.\n- `command-palette-is-named`: Name the palette with aria-label so it is announced when it opens.\n- `command-palette-input-is-named`: Name the search input with aria-label. A placeholder disappears as the user types and is not a reliable name.\n"
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"file": "countdown.md",
|
|
76
|
+
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Countdown\nA number from 0 to 999 whose digits roll to each new value.\n\nComponent ID: `countdown`\n\n[Component documentation](https://www.expressivecss.com/countdown.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/countdown.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.13.1)\n\nContract: ExpressiveCSS 0.13.1\n\nContract SHA-256: `18ec4146d300280bbcb74eb3b23f16d1015a0a3db86a8c59fc8348dbdf9d96c2`\n\n#### Selection and adaptation\n\nRuntime ownership: `css-only`. No dedicated entry in the reviewed Google component inventory.\n\nExample: Monthly revenue of $48,200 is a stat tile. A countdown only shows whole numbers up to 999 and has no label.\n\n#### Material mapping\n\nRelationship: none. Web extension; no dedicated entry was observed in the reviewed current M3 component inventory.\n\nUpstream: web-extension-inventory-reviewed (2026-10-08); [evidence](https://m3.material.io/components).\n\nRequirements and boundaries: design reference web-extensions-review. The inventory page renders client-side, so this review relies on the 2026-09-13 inventory review. Full visual parity and spoken output unverified.\n\nSupport (2026-10-08, `llm.md#countdown`): Digits from 0 to 999 that roll up or down when --md-comp-countdown-value changes, with zero padding to two or three digits and a reduced-motion fallback.\n\nWeb adaptation: The element's text is the accessible value; the digit strips are generated content with empty alternative text. A page script sets the property and the text together.\n\nKnown boundary: Width, digit positions, the baseline, the roll transition, reduced motion and the accessibility tree text are checked in a browser; spoken output inside role=\"timer\" and aria-live regions remains unverified.\n\nFull parity remains unassessed. [Capability evidence](../references/capability-roadmap.md#countdown).\n\n#### Contract\n\nA `.countdown` is a `<span>` with `--md-comp-countdown-value` set to a whole number and the same number as its text. When the value changes, each digit rolls up or down to the new one. The rolling digits are generated content hidden from assistive technology, so the text is what a screen reader reads; change the property and the text together. Values below 0 show 0 and values above 999 show 999. The box is as wide as the number; `--md-comp-countdown-digits` pads it with leading zeros to 2 or 3 digits. Size and color come from the surrounding text. With reduced motion the digits change without rolling. CSS only; the page's script sets the value.\n\n#### Syntax\n\n```html\n<span id=\"countdown\" class=\"countdown\" style=\"--md-comp-countdown-value: 60\">60</span>\n\n<script>\n const countdown = document.getElementById('countdown');\n let value = 60;\n setInterval(() => {\n value = value === 0 ? 60 : value - 1;\n countdown.style.setProperty('--md-comp-countdown-value', value);\n countdown.textContent = value;\n }, 1000);\n</script>\n```\n\n#### Also documented\n\nThe Countdown section of the [API reference](https://www.expressivecss.com/llm.md) also covers Clock, Tokens.\n\n#### Rules\n\n- `countdown-has-text`: Write the number as the countdown's text, matching --md-comp-countdown-value. The rolling digits are hidden from screen readers, so an empty countdown reads as nothing.\n"
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
"file": "data-table.md",
|
|
80
|
+
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Data table\nRows of records in a scrolling table with a sticky header, sort state and row selection.\n\nComponent ID: `data-table`\n\n[Component documentation](https://www.expressivecss.com/data-table.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/data-table.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.13.1)\n\nContract: ExpressiveCSS 0.13.1\n\nContract SHA-256: `18ec4146d300280bbcb74eb3b23f16d1015a0a3db86a8c59fc8348dbdf9d96c2`\n\n#### Selection and adaptation\n\nRuntime ownership: `css-only`. No dedicated entry in the reviewed Google component inventory.\n\nExample: A contacts screen where people tap a name to open a profile is a list. A data table would add column headers and sorting nobody uses on a phone.\n\n#### Material mapping\n\nRelationship: none. Web extension; no dedicated entry was observed in the reviewed current M3 component inventory.\n\nUpstream: web-extension-inventory-reviewed (2026-10-06); [evidence](https://m3.material.io/components).\n\nRequirements and boundaries: design reference web-extensions-review. The inventory page renders client-side, so this review relies on the 2026-09-13 inventory review. Full visual parity and spoken output unverified.\n\nSupport (2026-10-06, `llm.md#data-table`): Scrolling wrapper with an outline, sticky header row, numeric columns, dense rows, a sort arrow drawn from aria-sort and checkbox row selection.\n\nWeb adaptation: A plain div wraps a native <table>, so the table keeps its role; the page owns sorting, select-all and the scroll region name.\n\nKnown boundary: Table semantics, the sticky header, numeric alignment, the sort arrow and selected rows are checked in a browser; sort announcements, enlarged text and spoken output remain unverified.\n\nFull parity remains unassessed. [Capability evidence](../references/capability-roadmap.md#data-table).\n\n#### Contract\n\nA `.data-table` is a `<div>` around one native `<table>`. The wrapper scrolls and draws the outline, so the table keeps its table semantics. The header row sticks to the top of the wrapper once the wrapper scrolls vertically, so give the wrapper a `max-block-size` for long tables. Add `numeric` to the header and body cells of a number column to align them on the end edge with tabular figures, and `dense` to the wrapper for shorter rows. CSS only; there is no JavaScript.\n\nSorting is page state. Put a `<button>` in each sortable `<th>` and set `aria-sort=\"ascending\"` or `\"descending\"` on the header of the sorted column. The framework draws the arrow; the page reorders the rows and moves `aria-sort`.\n\n#### Syntax\n\n```html\n<div class=\"data-table\">\n <table>\n <caption>Orders</caption>\n <thead>\n <tr>\n <th scope=\"col\" aria-sort=\"ascending\"><button type=\"button\">Customer</button></th>\n <th scope=\"col\">Status</th>\n <th scope=\"col\" class=\"numeric\"><button type=\"button\">Total</button></th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <th scope=\"row\">Alvin</th>\n <td>Shipped</td>\n <td class=\"numeric\">$87.00</td>\n </tr>\n <tr>\n <th scope=\"row\">Shannon</th>\n <td>Pending</td>\n <td class=\"numeric\">$9.99</td>\n </tr>\n </tbody>\n </table>\n</div>\n```\n\n#### Also documented\n\nThe Data table section of the [API reference](https://www.expressivecss.com/llm.md) also covers Row selection, Scrolling, Tokens.\n\n#### Rules\n\n- `data-table-wraps-the-table`: Put .data-table on a <div> around the <table>. The wrapper scrolls; a table made to scroll itself needs display:block, which drops its table role in some engines.\n- `data-table-sort-is-on-a-header`: aria-sort belongs on the column header cell (<th>), not on the button inside it or on a data cell. Assistive technology reads the sort state from the header.\n- `data-table-row-is-not-selected`: aria-selected is not valid on a row of a plain table. It belongs to role=\"grid\" and its keyboard contract. Select a row with a labelled checkbox in its first cell.\n- `data-table-scroll-region-is-a-region`: A focusable scroll wrapper needs role=\"region\" so its name is announced. aria-label is not allowed on a plain div.\n- `data-table-scroll-region-is-named`: Name a focusable scroll wrapper with aria-label, or aria-labelledby pointing at the table caption, so keyboard users know what they are scrolling.\n"
|
|
49
81
|
},
|
|
50
82
|
{
|
|
51
83
|
"file": "date-picker.md",
|
|
52
|
-
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Date picker\nSelect a date, a range, or several dates from a calendar.\n\nComponent ID: `date-picker`\n\n[Component documentation](https://www.expressivecss.com/datepicker.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/datepicker.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.
|
|
84
|
+
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Date picker\nSelect a date, a range, or several dates from a calendar.\n\nComponent ID: `date-picker`\n\n[Component documentation](https://www.expressivecss.com/datepicker.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/datepicker.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.13.1)\n\nContract: ExpressiveCSS 0.13.1\n\nContract SHA-256: `18ec4146d300280bbcb74eb3b23f16d1015a0a3db86a8c59fc8348dbdf9d96c2`\n\n#### Selection and adaptation\n\nRuntime ownership: `auto-init`. [Google guidance](https://m3.material.io/components/date-pickers/overview)\n\nExample: A single date with no calendar-specific requirements can use a labeled native date input. Do not choose this picker expecting a popup: its calendar is inline and requires the documented visibility option.\n\n#### Material mapping\n\nRelationship: component.\n\nUpstream: requirements-prose-reviewed (2026-09-13); [evidence](https://m3.material.io/components/date-pickers/overview).\n\nRequirements and boundaries: design reference inputs-material-review. Full visual parity and spoken output unverified.\n\n[Specification link](https://m3.material.io/components/date-pickers/specs); full specs unreviewed.\n\n[Google guidelines](https://m3.material.io/components/date-pickers/guidelines).\n\nSupport (2026-09-17, `llm.md#date-picker`): Inline single-date, range, and multiple-date calendar.\n\nWeb adaptation: Default inline presentation with optional docked display plugin; no open()/close() methods or modal implementation.\n\nKnown boundary: Modal picker parity is not provided by the documented inline API.\n\nKnown boundary: Calendar keyboard navigation and range/multiple selection lifecycle have browser coverage. Spoken calendar context, native zoom and physical devices remain unverified.\n\nFull parity remains unassessed. [Capability evidence](../references/capability-roadmap.md#date-picker).\n\n#### Contract\n\nAdd `date-picker` to a text input. `AutoInit()` starts every `.date-picker` except those marked `no-autoinit`.\n\nThe calendar is inline, not a modal. `open()` and `close()` are not provided. With the default options the calendar is hidden (`openByDefault: false`) and clicking the input does not reveal it. Pass `openByDefault: true` to show the calendar under the field.\n\n#### Syntax\n\n```html\n<div class=\"field\">\n <input type=\"text\" class=\"date-picker\" id=\"birthdate\">\n <label for=\"birthdate\">Birthdate</label>\n</div>\n```\n\n#### Options\n\n| Name | Type | Default | Description |\n| --- | --- | --- | --- |\n| `format` | String or Function | `'mmm dd, yyyy'` | Output written to the input, or a function that takes a `Date` and returns a string. |\n| `parse` | Function | `null` | Turn the current input string back into a `Date`. Receives `(value, format)`, where `format` is the configured string or an empty string for a function formatter. Blank inputs skip parsing. |\n| `isDateRange` | Boolean | `false` | Select a start date and an end date. |\n| `dateRangeEndEl` | String | `null` | Selector for an existing end-date input. If omitted, a second input is created. |\n| `isMultipleSelection` | Boolean | `false` | Toggle several dates. Extra inputs are created as dates are added. |\n| `defaultDate` | Date | `null` | Initial date to view. Falls back to the input’s current value, then today. |\n| `defaultEndDate` | Date | `null` | Initial end date when `isDateRange` is true. |\n| `setDefaultDate` | Boolean | `false` | If true, `defaultDate` is also the selected value. |\n| `setDefaultEndDate` | Boolean | `false` | If true, `defaultEndDate` is also the selected end value. |\n| `disableWeekends` | Boolean | `false` | Prevent selecting Saturday and Sunday. |\n| `disableDayFn` | Function | `null` | Return true to disable that day. Receives a `Date`. |\n| `firstDay` | Number | `0` | First day of the week. `0` is Sunday, `1` is Monday. |\n| `minDate` | Date | `null` | Earliest selectable date. |\n| `maxDate` | Date | `null` | Latest selectable date. |\n| `yearRange` | Number or Array | `10` | Years on either side of the viewed year, or `[minYear, maxYear]`. |\n| `yearRangeReverse` | Boolean | `false` | Sort the year list in reverse order. |\n| `isRTL` | Boolean | `false` | Render the calendar right-to-left. |\n| `showMonthAfterYear` | Boolean | `false` | Show the month after the year in the title. |\n| `showDaysInNextAndPreviousMonths` | Boolean | `false` | Render days that fall in the adjoining months. |\n| `openByDefault` | Boolean | `false` | If true, the calendar is visible. If false, it is given `display: none`. |\n| `container` | Element or String | `null` | Element or selector to render the calendar into. When set, the calendar is not hidden. |\n| `showClearBtn` | Boolean | `false` | Show a Clear button in the footer. |\n| `autoSubmit` | Boolean | `true` | If true, selecting a day writes the input immediately. If false, Ok and Cancel buttons are added. |\n| `i18n` | Object | See below | Labels and month/weekday names. Partial objects are merged with the defaults. |\n| `events` | Array | `[]` | Strings from `Date.toDateString()`. Matching days get a `has-event` class. The default stylesheet does not style that class. |\n| `onSelect` | Function | `null` | Called when a date is selected. Receives the `Date`. |\n| `onDraw` | Function | `null` | Called after the calendar HTML is redrawn. |\n| `onInputInteraction` | Function | `null` | Called when the input is clicked or confirmed with Enter. |\n| `onConfirm` | Function | `null` | Called once when Ok is activated by click, Enter or Space. The button is created when `autoSubmit` is false. |\n| `onCancel` | Function | `null` | Called once when Cancel is activated by click, Enter or Space. The button is created when `autoSubmit` is false. |\n| `displayPlugin` | String | `null` | Set to `'docked'` for a click-to-open popover. Pair with `openByDefault: true`. |\n| `displayPluginOptions` | Object | `null` | Options for the docked plugin: `margin`, `transition`, `duration`, `align`. |\n\n#### Methods\n\n- `.toString()`: String form of the selected date, using `format`. You can pass another date and format.\n- `.setDate()`: Select a date and move the calendar to it.\n- `.gotoDate()`: Change the visible month without changing the selection.\n- `.destroy()`: Destroy the plugin instance, remove the calendar, and tear down its event handlers.\n- `.open(); / .close()`: These methods are not provided. The calendar defaults to inline presentation; `displayPlugin: 'docked'` enables docking. Both require `openByDefault: true` to show the calendar. No modal implementation is available.\n\n#### Also documented\n\nThe Date Picker section of the [API reference](https://www.expressivecss.com/llm.md) also covers Initialization, Display, Date format options, Internationalization, Calendar keyboard navigation, Date range, Multiple dates, Properties.\n\n#### Rules\n\nNo component-specific `semantics.json` rules apply to this guide.\n"
|
|
53
85
|
},
|
|
54
86
|
{
|
|
55
87
|
"file": "dialogs.md",
|
|
56
|
-
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Dialogs\nImportant prompts in a user flow. Dedicated to a single task.\n\nComponent ID: `dialogs`\n\n[Component documentation](https://www.expressivecss.com/dialogs.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/dialogs.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.
|
|
88
|
+
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Dialogs\nImportant prompts in a user flow. Dedicated to a single task.\n\nComponent ID: `dialogs`\n\n[Component documentation](https://www.expressivecss.com/dialogs.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/dialogs.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.13.1)\n\nContract: ExpressiveCSS 0.13.1\n\nContract SHA-256: `18ec4146d300280bbcb74eb3b23f16d1015a0a3db86a8c59fc8348dbdf9d96c2`\n\n#### Selection and adaptation\n\nRuntime ownership: `shared-runtime`. [Google guidance](https://m3.material.io/components/dialogs/overview)\n\nExample: Saving successfully should not force dismissal of a dialog. Use a snackbar for that feedback and inline feedback for a persistent nonblocking issue; reserve a confirmation dialog for a decision that must precede the action.\n\n#### Material mapping\n\nRelationship: component.\n\nUpstream: overview-specs-guidelines-accessibility-prose-reviewed (2026-09-13); [evidence](https://m3.material.io/components/dialogs/overview).\n\nRendered prose reviewed; collapsed token tables, image-only measurements, runtime and full parity unverified. See the design priority component review for requirement-level findings.\n\n[Specification link](https://m3.material.io/components/dialogs/specs); full specs unreviewed.\n\nSupport (2026-09-07, `llm.md#dialogs`): Basic and full-screen dialogs.\n\nWeb adaptation: Native dialog.showModal()/close(); shared dialog behavior is separate from AutoInit.\n\nFull parity remains unassessed. [Capability evidence](../references/capability-roadmap.md#dialogs).\n\n#### Contract\n\nMaterial Design 3 dialogs, from the HTML.\n\nA `<dialog>` is a basic dialog. A heading is the headline, a `<p>` (or a wrapping `<div>`) is supporting text, and the last child `<form method=\"dialog\">` or `<nav>` is the action row. `dialog.max` is the full-screen variant. There are no `.modal`, `modal-header`, `modal-content`, or `modal-footer` classes — the element is the component.\n\nTokens follow the [M3 dialog spec](https://m3.material.io/components/dialogs/specs). The container is `surface`, 28dp corners, 280–560dp wide, elevation 3. The headline is `headline-small` / `on-surface`; supporting text is `body-medium`. The scrim is `--md-comp-scrim-color`. Actions sit at the end with an 8dp gap.\n\n#### Syntax\n\n```html\n<button type=\"button\" onclick=\"document.getElementById('dialog1').showModal()\">\n Show\n</button>\n\n<dialog id=\"dialog1\" aria-labelledby=\"use-location-services-title\">\n <h2 id=\"use-location-services-title\">Use location services?</h2>\n <p>Let the app use your location to suggest nearby stops.</p>\n <form method=\"dialog\">\n <button type=\"submit\" class=\"text\" value=\"disagree\">Disagree</button>\n <button type=\"submit\" value=\"agree\">Agree</button>\n </form>\n</dialog>\n```\n\n#### Methods\n\n- `.showModal()`: Open as a modal, with the scrim.\n- `.close()`: Close the dialog.\n\n#### Also documented\n\nThe Dialogs section of the [API reference](https://www.expressivecss.com/llm.md) also covers Tokens, Full-screen.\n\n#### Rules\n\n- `dialog-is-named`: A <dialog> takes no name from its heading. Point aria-labelledby at that heading, or give it an aria-label - otherwise it opens announced as just \"dialog\".\n"
|
|
57
89
|
},
|
|
58
90
|
{
|
|
59
91
|
"file": "drag-handle.md",
|
|
60
|
-
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Drag handle\nThe bar that says a thing can be dragged — and nothing that does the dragging.\n\nComponent ID: `drag-handle`\n\n[Component documentation](https://www.expressivecss.com/drag-handle.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/drag-handle.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.
|
|
92
|
+
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Drag handle\nThe bar that says a thing can be dragged — and nothing that does the dragging.\n\nComponent ID: `drag-handle`\n\n[Component documentation](https://www.expressivecss.com/drag-handle.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/drag-handle.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.13.1)\n\nContract: ExpressiveCSS 0.13.1\n\nContract SHA-256: `18ec4146d300280bbcb74eb3b23f16d1015a0a3db86a8c59fc8348dbdf9d96c2`\n\n#### Selection and adaptation\n\nRuntime ownership: `css-only`. [Related Google guidance](https://m3.material.io/components/bottom-sheets/overview)\n\n#### Material mapping\n\nRelationship: related. Related guidance is not a dedicated component specification.\n\nUpstream: requirements-prose-reviewed (2026-09-13); [evidence](https://m3.material.io/components/bottom-sheets/overview).\n\nRequirements and boundaries: design reference layout-material-review. Full visual parity and spoken output unverified.\n\n[Specification link](https://m3.material.io/components/bottom-sheets/specs); full specs unreviewed.\n\n[Google guidelines](https://m3.material.io/components/bottom-sheets/guidelines).\n\nSupport (2026-09-13, `llm.md#drag-handle`): Decorative grip and actionable button grip.\n\nWeb adaptation: The host element determines semantics; no general dragging engine.\n\nKnown boundary: A grip outside supported sheets implements neither dragging nor keyboard or non-drag pointer alternatives.\n\nKnown boundary: Standalone grip styling supplies no drag, resize or reorder operation.\n\nFull parity remains unassessed. [Capability evidence](../references/capability-roadmap.md#drag-handle).\n\n#### Contract\n\nThe bar that makes something legible as draggable: a bottom sheet's grabber, the gutter between two panes, the grip on a reorderable row.\n\nWhich element you write *is* the semantic. A `<span class=\"drag-handle\" aria-hidden=\"true\">` is decoration and is the usual case — the handle draws a bar and contains no text, so exposed it arrives in the reading order as an unlabelled blank. A `<button class=\"drag-handle\">` with an `aria-label` is a control, written only when activating it does something — inside a bottom sheet it always does, and everywhere else that is the page's to arrange. Never `aria-hidden` a button: that hides it from assistive technology without taking it out of the tab order. The hover, focus and pressed states are scoped to the button spelling, so a decorative handle does not light up under a passing pointer.\n\n**Nothing here drags.** No script belongs to this component and ExpressiveCSS ships no reordering behaviour. The one thing a handle is wired to is the bottom sheet's, below. Provide both keyboard operation and a single-pointer alternative without dragging, such as clickable \"Move up\" / \"Move down\" buttons or a position input. Arrow keys alone cover only the keyboard path. [WCAG 2.2 SC 2.5.7](https://www.w3.org/WAI/WCAG22/Understanding/dragging-movements.html) requires the pointer alternative unless dragging is essential or the unmodified user agent owns the functionality. Verify the alternatives independently; the handle does not implement either one.\n\n#### Syntax\n\n```html\n<section aria-label=\"Editor\">…</section>\n<span class=\"drag-handle\" aria-hidden=\"true\"></span>\n<section aria-label=\"Preview\">…</section>\n```\n\n#### Rules\n\n- `drag-handle-is-hidden-or-a-control`: A drag handle has no text and reports nothing, so on any element but a <button> it is decoration and needs aria-hidden=\"true\". Left exposed it arrives as an unlabelled blank in the reading order. Make it a <button> with a name if it is meant to be operated.\n- `drag-handle-button-is-not-hidden`: aria-hidden on a <button> hides it from assistive technology without taking it out of the tab order, so keyboard focus lands on something that is not there. Decoration is a <span>; a control stays exposed.\n- `drag-handle-button-is-named`: The handle is a bar drawn in CSS - there is no text inside it to be named by. A handle worth making a control is worth an aria-label saying what it moves.\n"
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
"file": "drop-zone.md",
|
|
96
|
+
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Drop zone\nA large target for choosing or dropping files, with a list of what was chosen.\n\nComponent ID: `drop-zone`\n\n[Component documentation](https://www.expressivecss.com/drop-zone.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/drop-zone.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.13.1)\n\nContract: ExpressiveCSS 0.13.1\n\nContract SHA-256: `18ec4146d300280bbcb74eb3b23f16d1015a0a3db86a8c59fc8348dbdf9d96c2`\n\n#### Selection and adaptation\n\nRuntime ownership: `shared-runtime`. No dedicated entry in the reviewed Google component inventory.\n\nExample: An optional attachment on a support form is a file input in the form. A drop zone would give one small field the weight of the whole page.\n\n#### Material mapping\n\nRelationship: none. Web extension; no dedicated entry was observed in the reviewed current M3 component inventory.\n\nUpstream: web-extension-inventory-reviewed (2026-10-06); [evidence](https://m3.material.io/components).\n\nRequirements and boundaries: design reference web-extensions-review. The inventory page renders client-side, so this review relies on the 2026-09-13 inventory review. Full visual parity and spoken output unverified.\n\nSupport (2026-10-06, `llm.md#drop-zone`): A label-sized native file input with a dragover state and a list of chosen file names and locale-formatted sizes.\n\nWeb adaptation: The native input covers the label, so clicks, keys and drops are the browser's; the shared Forms behavior marks the drag and fills the list.\n\nKnown boundary: Input coverage, the dragover state, the file list and keyboard focus are checked in a browser with synthetic events; real operating-system drags and spoken output remain unverified.\n\nFull parity remains unassessed. [Capability evidence](../references/capability-roadmap.md#drop-zone).\n\n#### Contract\n\nA `.drop-zone` holds a `<label>` with an icon, a prompt and a native `<input type=\"file\">`, and an optional `<ul class=\"drop-zone-files\">` after it. The input is stretched invisibly over the whole label, so the browser takes a click, Enter, Space or a dropped file exactly as it does on a plain file input, and the label text names it. The Forms behavior adds `dragover` to the zone while files are dragged over it and lists each chosen file's name and size, formatted for the page's locale, in `.drop-zone-files`. The root bundle installs it on load, and the modular entry with `Forms.Init()`. It listens on the document, so zones added later work too. Resetting the form empties the list.\n\nThe browser does not apply `accept` to dropped files, so check type and size where the files are used. A dropped file replaces the selection, as it does on any file input; add `multiple` to take several at once.\n\n#### Syntax\n\n```html\n<div class=\"drop-zone\">\n <label>\n <span class=\"material-symbols\" aria-hidden=\"true\">upload_file</span>\n Drop files here or browse\n <small>PDF or PNG, up to 10 MB</small>\n <input type=\"file\" multiple accept=\".pdf,.png\">\n </label>\n <ul class=\"drop-zone-files\" aria-live=\"polite\"></ul>\n</div>\n```\n\n#### Also documented\n\nThe Drop zone section of the [API reference](https://www.expressivecss.com/llm.md) also covers Tokens.\n\n#### Rules\n\n- `drop-zone-input-is-in-the-label`: Put the file input inside the .drop-zone <label>. The label names the input and the input covers the label; a loose input is neither named nor a drop target.\n- `drop-zone-files-are-a-list`: Write .drop-zone-files as a <ul>. The behavior fills it with one <li> per file, and a list tells assistive technology how many files were chosen.\n"
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"file": "empty-state.md",
|
|
100
|
+
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Empty state\nWhat a view shows when it has nothing to list: an icon, a heading, text and actions.\n\nComponent ID: `empty-state`\n\n[Component documentation](https://www.expressivecss.com/empty-state.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/empty-state.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.13.1)\n\nContract: ExpressiveCSS 0.13.1\n\nContract SHA-256: `18ec4146d300280bbcb74eb3b23f16d1015a0a3db86a8c59fc8348dbdf9d96c2`\n\n#### Selection and adaptation\n\nRuntime ownership: `css-only`. No dedicated entry in the reviewed Google component inventory.\n\nExample: A failed save after pressing Save is feedback for a snackbar or the form. An empty state would replace the user's content with a placeholder.\n\n#### Material mapping\n\nRelationship: none. Web extension; no dedicated entry was observed in the reviewed current M3 component inventory.\n\nUpstream: web-extension-inventory-reviewed (2026-10-06); [evidence](https://m3.material.io/components).\n\nRequirements and boundaries: design reference web-extensions-review. The inventory page renders client-side, so this review relies on the 2026-09-13 inventory review. Full visual parity and spoken output unverified.\n\nSupport (2026-10-06, `llm.md#empty-state`): Centered icon in a tonal circle or an illustration, a heading, supporting text and a wrapping row of actions.\n\nWeb adaptation: A plain div around authored heading, text and buttons; the page picks the heading level.\n\nKnown boundary: Centering, the icon circle and the actions row are checked in a browser; announcements when a search empties a list and spoken output remain unverified.\n\nFull parity remains unassessed. [Capability evidence](../references/capability-roadmap.md#empty-state).\n\n#### Contract\n\nA `.empty-state` centers its content in the space the missing items would fill. In order, it holds an optional leading icon, which sits in a 96px tonal circle, or an `<img>` illustration; a heading at the level the page outline needs; a `<p>` of supporting text; and a `.empty-state-actions` row of buttons. CSS only; there is no JavaScript.\n\nSay what is empty and what the user can do about it. Use it for a first run, a search with no results, or content the user cannot see yet. Hide the icon from assistive technology, and give an illustration `alt=\"\"` when the heading already says what it shows.\n\n#### Syntax\n\n```html\n<div class=\"empty-state\">\n <span class=\"material-symbols\" aria-hidden=\"true\">inbox</span>\n <h2>No messages yet</h2>\n <p>Messages from your team show up here.</p>\n <div class=\"empty-state-actions\">\n <button class=\"filled\">New message</button>\n </div>\n</div>\n```\n\n#### Also documented\n\nThe Empty state section of the [API reference](https://www.expressivecss.com/llm.md) also covers Search with no results, Tokens.\n\n#### Rules\n\n- `empty-state-image-has-alt`: Give an empty-state illustration alt=\"\" when the heading already says what it shows, or alt text when the image adds information.\n"
|
|
61
101
|
},
|
|
62
102
|
{
|
|
63
103
|
"file": "fab.md",
|
|
64
|
-
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Floating action button\nA circular action that can open a menu of related shortcuts.\n\nComponent ID: `fab`\n\n[Component documentation](https://www.expressivecss.com/floating-action-button.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/floating-action-button.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.
|
|
104
|
+
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Floating action button\nA circular action that can open a menu of related shortcuts.\n\nComponent ID: `fab`\n\n[Component documentation](https://www.expressivecss.com/floating-action-button.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/floating-action-button.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.13.1)\n\nContract: ExpressiveCSS 0.13.1\n\nContract SHA-256: `18ec4146d300280bbcb74eb3b23f16d1015a0a3db86a8c59fc8348dbdf9d96c2`\n\n#### Selection and adaptation\n\nRuntime ownership: `auto-init`. [Google guidance](https://m3.material.io/components/floating-action-button/overview)\n\nExample: Speed dials have been removed. Replace legacy fab or fixed-action-btn wrappers with fab-menu and labelled actions; use a toolbar for persistent commands.\n\n- expanded window: variant. Use the extended FAB when the action benefits from a visible label.\n\n#### Material mapping\n\nRelationship: component. Google no longer recommends small FABs, baseline extended FABs or surface-colored FABs. The 40dp FAB and sizeless extend variant are removed. Use circle extra for the 56dp FAB, or its medium and large sizes. Extended FABs require extend small, extend medium or extend large; the small extended FAB remains recommended. Use primary/secondary/tertiary container roles instead of surface colors. Use fab-menu for labelled related actions; speed-dial APIs are removed.\n\nUpstream: requirements-prose-reviewed (2026-09-13); [evidence](https://m3.material.io/components/floating-action-button/overview).\n\nRequirements and boundaries: design reference layout-material-review. Full visual parity and spoken output unverified.\n\n[Specification link](https://m3.material.io/components/floating-action-button/specs); full specs unreviewed.\n\n[Google guidelines](https://m3.material.io/components/floating-action-button/guidelines).\n\n[Related Google component](https://m3.material.io/components/extended-fab/overview).\n\n[Related Google component](https://m3.material.io/components/fab-menu/overview).\n\nSupport (2026-09-13, `llm.md#floating-action-button`): FAB sizes, extended FABs, and menus of related actions.\n\nWeb adaptation: Floating action behavior uses the documented FAB runtime.\n\nKnown boundary: Full visual parity and spoken assistive-technology output remain unverified.\n\nFull parity remains unassessed. [Capability evidence](../references/capability-roadmap.md#fab).\n\n#### Contract\n\nUse a standalone FAB for the primary action or a FAB menu for related labelled actions.\n\n`fab-menu` is the FAB that expands into a list of labelled actions. The trigger is an ordinary FAB; the actions are a `<ul>` (or `<menu>`) of pills, each an icon and a label at 56dp on a full corner. Click the FAB to expand; click it again, click outside, or press Escape to collapse.\n\nExpanded is the framework's state, not the author's: the constructor stamps `aria-expanded` on the trigger and `open()` / `close()` move it along with the `active` class. Do not write either into the markup.\n\n#### Syntax\n\n```html\n<div class=\"fab-menu\">\n <button type=\"button\" class=\"button extra circle\" aria-label=\"Create\">\n <span class=\"material-symbols\" aria-hidden=\"true\">add</span>\n <span class=\"material-symbols\" aria-hidden=\"true\">close</span>\n </button>\n <ul>\n <li><button type=\"button\"><span class=\"material-symbols\" aria-hidden=\"true\">mode_edit</span><span>Compose</span></button></li>\n <li><button type=\"button\"><span class=\"material-symbols\" aria-hidden=\"true\">image</span><span>Add photo</span></button></li>\n </ul>\n</div>\n```\n\n#### Methods\n\n- `.open()`: Opens the FAB menu.\n- `.close()`: Closes the FAB menu.\n- `.destroy()`: Destroy the plugin instance and tear down its event handlers.\n\n#### Also documented\n\nThe Floating Action Button section of the [API reference](https://www.expressivecss.com/llm.md) also covers FAB menu, Initialization, Properties, Speed-dial migration.\n\n#### Rules\n\n- `icon-only-control-is-named`: Every icon is hidden from assistive technology, so a control whose only content is one has no name left. Give it an aria-label.\n- `fab-no-small-variant`: The 40dp small FAB has been removed. Use circle extra for a 56dp FAB.\n- `extended-fab-requires-size`: The baseline extended FAB has been removed. Specify extend small, extend medium or extend large.\n- `fab-no-surface-color`: Use a primary, secondary or tertiary container color for a FAB instead of a surface color.\n- `fab-menu-not-a-composite-widget`: The FAB menu takes no composite role; its actions are reached with Tab, not arrow keys.\n- `fab-menu-expanded-is-not-authored`: Expanded is dynamic state, so the framework owns it. The constructor stamps aria-expanded on the trigger and every open() and close() rewrites it; authoring it states a value that is about to be overwritten.\n"
|
|
65
105
|
},
|
|
66
106
|
{
|
|
67
107
|
"file": "fieldsets.md",
|
|
68
|
-
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Fieldsets\nGrouped form sections, from the HTML.\n\nComponent ID: `fieldsets`\n\n[Component documentation](https://www.expressivecss.com/fieldsets.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/fieldsets.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.
|
|
108
|
+
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Fieldsets\nGrouped form sections, from the HTML.\n\nComponent ID: `fieldsets`\n\n[Component documentation](https://www.expressivecss.com/fieldsets.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/fieldsets.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.13.1)\n\nContract: ExpressiveCSS 0.13.1\n\nContract SHA-256: `18ec4146d300280bbcb74eb3b23f16d1015a0a3db86a8c59fc8348dbdf9d96c2`\n\n#### Selection and adaptation\n\nRuntime ownership: `css-only`. [Related Google guidance](https://m3.material.io/components/text-fields/overview)\n\nExample: A promotional feature block needs a section or card. Use a fieldset when a shared legend explains related inputs, such as delivery preferences.\n\n#### Material mapping\n\nRelationship: related. Related guidance is not a dedicated component specification.\n\nUpstream: requirements-prose-reviewed (2026-09-13); [evidence](https://m3.material.io/components/text-fields/overview).\n\nRequirements and boundaries: design reference inputs-material-review. Full visual parity and spoken output unverified.\n\n[Specification link](https://m3.material.io/components/text-fields/specs); full specs unreviewed.\n\n[Google guidelines](https://m3.material.io/components/text-fields/guidelines).\n\nSupport (2026-09-13, `llm.md#fieldsets`): Outlined, filled, and rounded form groups.\n\nWeb adaptation: Native fieldset/legend; related text-field tokens do not make this an M3 component.\n\nKnown boundary: Named native groups and disabled descendants are checked; long legends and nested groups remain unverified.\n\nFull parity remains unassessed. [Capability evidence](../references/capability-roadmap.md#fieldsets).\n\n#### Contract\n\nA `<fieldset>` is the group. A `<legend>` is the headline. Everything else is a field — `.field`, radios, switches, a `.inline` row of radios, a trailing `<small>` as supporting text. There is no wrapper class. They are CSS only. There is no JavaScript component and nothing to AutoInit.\n\nMaterial Design 3 has no fieldset component. Tokens follow the outlined grouping container used around related form content, and the [outlined text-field](https://m3.material.io/components/text-fields/specs) shape so a group of fields matches the fields. The container is 4dp corners and a 1dp `outline-variant` stroke. The legend is `title-small` / `on-surface`. Supporting text is `body-small`. Padding is 16dp; children sit 16dp apart. Disabled is 38%.\n\n#### Syntax\n\n```html\n<fieldset>\n <legend>Name</legend>\n <div class=\"field\">\n <input id=\"first\" type=\"text\" placeholder=\" \">\n <label for=\"first\">First name</label>\n </div>\n <div class=\"field\">\n <input id=\"last\" type=\"text\" placeholder=\" \">\n <label for=\"last\">Last name</label>\n </div>\n</fieldset>\n```\n\n#### Also documented\n\nThe Fieldsets section of the [API reference](https://www.expressivecss.com/llm.md) also covers Choices, Variants, Disabled.\n\n#### Rules\n\n- `fieldset-has-legend`: A fieldset without a legend groups controls visually but names nothing.\n"
|
|
69
109
|
},
|
|
70
110
|
{
|
|
71
111
|
"file": "footer.md",
|
|
72
|
-
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Footer\nSite navigation and extra information at the end of a page.\n\nComponent ID: `footer`\n\n[Component documentation](https://www.expressivecss.com/footer.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/footer.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.
|
|
112
|
+
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Footer\nSite navigation and extra information at the end of a page.\n\nComponent ID: `footer`\n\n[Component documentation](https://www.expressivecss.com/footer.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/footer.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.13.1)\n\nContract: ExpressiveCSS 0.13.1\n\nContract SHA-256: `18ec4146d300280bbcb74eb3b23f16d1015a0a3db86a8c59fc8348dbdf9d96c2`\n\n#### Selection and adaptation\n\nRuntime ownership: `css-only`. No dedicated entry in the reviewed Google component inventory.\n\nExample: Home/Search/Profile should not require scrolling to a footer. Use a navigation bar or rail for those destinations.\n\n#### Material mapping\n\nRelationship: none. Web extension; no dedicated entry was observed in the reviewed current M3 component inventory.\n\nUpstream: web-extension-inventory-reviewed (2026-09-13); [evidence](https://m3.material.io/components).\n\nRequirements and boundaries: design reference web-extensions-review. Full visual parity and spoken output unverified.\n\nSupport (2026-09-13, `llm.md#footer`): Native footer with navigation columns and copyright content.\n\nWeb adaptation: Web page landmark styled with theme roles.\n\nKnown boundary: A document-level contentinfo landmark is checked; section-scoped footers and responsive legal links remain unverified.\n\nFull parity remains unassessed. [Capability evidence](../references/capability-roadmap.md#footer).\n\n#### Contract\n\nFooters are a good place for site navigation and extra information. This is where people look after they finish the page, or when they want more about the site.\n\nPut the page in the three HTML5 landmarks `header`, `main`, and `footer`. Anatomy is the HTML: a bare `<footer>` is the component, each `<nav>` is a column of links with an `<h2>` heading, and a trailing `<small>` is the copyright bar. There is no `page-footer` or `footer-copyright` class, and no JavaScript component.\n\nExpressive’s footer is unfilled by default. It draws a dashed top border and uses the theme tokens for paragraph and link color — not a solid primary bar.\n\n#### Syntax\n\n```html\n<footer>\n <section>\n <h2>Product</h2>\n <p>A column with no links is not navigation — it is a section with a heading.</p>\n </section>\n <nav aria-labelledby=\"footer-links\">\n <h2 id=\"footer-links\">Links</h2>\n <a href=\"/about\">About</a>\n <a href=\"/contact\">Contact</a>\n </nav>\n <small>\n <span>© 2026 Copyright Text</span>\n <a href=\"/privacy\">Privacy</a>\n </small>\n</footer>\n```\n\n#### Also documented\n\nThe Footer section of the [API reference](https://www.expressivecss.com/llm.md) also covers Sticky Footer.\n\n#### Rules\n\n- `footer-nav-has-links`: A footer column with no links is not navigation - it is a <section> with a heading. Marking it <nav> spends a landmark on prose.\n\n#### Guide checks\n\n- Label every <nav> landmark with aria-label or aria-labelledby.\n"
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
"file": "heatmap-chart.md",
|
|
116
|
+
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Heatmap chart\nA data table drawn as a grid of cells shaded by value, for patterns across two categories such as days and hours.\n\nComponent ID: `heatmap-chart`\n\n[Component documentation](https://www.expressivecss.com/heatmap-chart.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/heatmap-chart.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.13.1)\n\nContract: ExpressiveCSS 0.13.1\n\nContract SHA-256: `18ec4146d300280bbcb74eb3b23f16d1015a0a3db86a8c59fc8348dbdf9d96c2`\n\n#### Selection and adaptation\n\nRuntime ownership: `auto-init`. No dedicated entry in the reviewed Google component inventory.\n\nExample: Visits by day of the week and time of day is a heatmap chart. Visits per day over a month is a line or column chart.\n\n#### Material mapping\n\nRelationship: none. Web extension; no dedicated entry was observed in the reviewed current M3 component inventory.\n\nUpstream: web-extension-inventory-reviewed (2026-10-07); [evidence](https://m3.material.io/components).\n\nRequirements and boundaries: design reference web-extensions-review. The inventory page renders client-side, so this review relies on the 2026-09-13 inventory review. Full visual parity and spoken output unverified.\n\nSupport (2026-10-07, `llm.md#heatmap-chart`): Every body cell of a figure's data table drawn as a shaded grid cell, with row headers beside the grid, column headers below, a scale naming the lowest and highest cells, values, data-label short or hidden labels, data-min and data-max, gaps, and a pointer and keyboard tooltip that moves cell by cell.\n\nWeb adaptation: The <table> stays in the page as the text alternative and the no-JavaScript fallback; the grid, labels and scale are aria-hidden and the plot is one Tab stop with a polite live tooltip.\n\nKnown boundary: Drawing, the hidden table, cell shades and gaps, the fixed scale, values and their text color, short and running labels, the scale ends, RTL, page overflow from a wide table, the tooltip, pointer and keyboard movement round the grid and destroy are checked in Chromium, Firefox and WebKit; screen reader output, forced colors and enlarged text remain unverified. A wide grid narrows its cells rather than scrolling.\n\nFull parity remains unassessed. [Capability evidence](../references/capability-roadmap.md#heatmap-chart).\n\n#### Contract\n\nA `<figure class=\"heatmap-chart\">` takes the same `<figcaption>` and `<table>` as a column chart. `AutoInit()` draws every body cell as a cell of a grid, one grid row per table row and one grid column per value column, shaded from the lowest value to the highest. The row headers sit at the inline start, the column headers below, and a scale under the grid names the lowest and highest cells. A tooltip shows the cell under the pointer. The table stays in the page, visually hidden, as the chart's text alternative; without JavaScript it shows instead of the chart.\n\n#### Syntax\n\n```html\n<figure class=\"heatmap-chart values\">\n <figcaption>Revenue by rep, per month</figcaption>\n <table>\n <thead>\n <tr><th>Rep</th><th>Jan</th><th>Feb</th><th>Mar</th></tr>\n </thead>\n <tbody>\n <tr><th>Priya</th><td data-value=\"45000\">$45K</td><td data-value=\"58000\">$58K</td><td data-value=\"85000\">$85K</td></tr>\n <tr><th>Daniel</th><td data-value=\"88000\">$88K</td><td data-value=\"34000\">$34K</td><td data-value=\"45000\">$45K</td></tr>\n </tbody>\n </table>\n</figure>\n```\n\n#### Also documented\n\nThe Heatmap chart section of the [API reference](https://www.expressivecss.com/llm.md) also covers Tokens.\n\n#### Rules\n\n- `heatmap-chart-is-a-figure-with-a-table`: Write .heatmap-chart as a <figure> holding a <table> of the data. The drawn chart is hidden from assistive technology, so the table is all a screen reader reads, and it is what shows without JavaScript.\n- `heatmap-chart-has-a-caption`: Name a .heatmap-chart with a <figcaption>. It names the focusable plot as well as the figure; hide it with .visually-hidden when nearby text already names the chart.\n"
|
|
73
117
|
},
|
|
74
118
|
{
|
|
75
119
|
"file": "icon-buttons.md",
|
|
76
|
-
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Icon buttons\nA single icon as the whole control, in four styles and five sizes.\n\nComponent ID: `icon-buttons`\n\n[Component documentation](https://www.expressivecss.com/icon-buttons.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/icon-buttons.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.
|
|
120
|
+
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Icon buttons\nA single icon as the whole control, in four styles and five sizes.\n\nComponent ID: `icon-buttons`\n\n[Component documentation](https://www.expressivecss.com/icon-buttons.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/icon-buttons.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.13.1)\n\nContract: ExpressiveCSS 0.13.1\n\nContract SHA-256: `18ec4146d300280bbcb74eb3b23f16d1015a0a3db86a8c59fc8348dbdf9d96c2`\n\n#### Selection and adaptation\n\nRuntime ownership: `css-only`. [Google guidance](https://m3.material.io/components/icon-buttons/overview)\n\nExample: A novel approval workflow should use a labeled button. A tooltip on an ambiguous icon cannot replace a clear label or the control's accessible name.\n\n#### Material mapping\n\nRelationship: component. Google deprecates baseline filled, tonal and outlined token sets in favor of updated tokens, not those current icon-button variants. Do not rename framework tokens from Android examples.\n\nUpstream: requirements-prose-reviewed (2026-09-13); [evidence](https://m3.material.io/components/icon-buttons/overview).\n\nRequirements and boundaries: design reference layout-material-review. Full visual parity and spoken output unverified.\n\n[Specification link](https://m3.material.io/components/icon-buttons/specs); full specs unreviewed.\n\n[Google guidelines](https://m3.material.io/components/icon-buttons/guidelines).\n\nSupport (2026-09-13, `llm.md#icon-buttons`): Standard, filled, tonal, outlined; five sizes; narrow, wide, and square.\n\nWeb adaptation: Accessible name belongs to the control; the glyph is decorative.\n\nKnown boundary: Elevated and text are not icon-button variants.\n\nKnown boundary: Existing group checks cover selected icon colors and press shapes, not all standalone target sizes and disabled states.\n\nFull parity remains unassessed. [Capability evidence](../references/capability-roadmap.md#icon-buttons).\n\n#### Contract\n\nOne action as one icon. Add `icon-button` to a `<button>`, or to an `<a>` when it navigates, and put a single `<span class=\"material-symbols\" aria-hidden=\"true\">` inside. The icon is hidden, so `aria-label` is the only name the control has — an icon button without one fails the semantics suite.\n\nIt is a component, not a modifier on the common button: M3 gives it its own token families (`md.comp.icon-button.{xsmall…xlarge}` and `md.comp.icon-button-{standard,filled,tonal,outlined}`), so `.icon-button` takes none of the common-button geometry. Do not write `class=\"button icon-button\"`.\n\n#### Syntax\n\n```html\n<button type=\"button\" class=\"icon-button\" aria-label=\"Add\">\n <span class=\"material-symbols\" aria-hidden=\"true\">add</span>\n</button>\n\n<a class=\"icon-button\" href=\"/inbox\" aria-label=\"Inbox\">\n <span class=\"material-symbols\" aria-hidden=\"true\">inbox</span>\n</a>\n```\n\n#### Also documented\n\nThe Icon buttons section of the [API reference](https://www.expressivecss.com/llm.md) also covers Styles, Sizes, width and shape, Disabled and tokens.\n\n#### Rules\n\n- `icon-button-is-a-control`: An icon button is a control: <button>, or <a href> when it navigates. A <div>, or an anchor with no href, is neither focusable nor announced.\n- `icon-button-link-is-not-fake-disabled`: A link cannot be disabled by a class. `pointer-events: none` stops the pointer and nothing else, so this stays in the tab order and still navigates on Enter - it looks inert and is not. Drop the href, or the link.\n- `icon-button-icon-hidden`: The ligature is real text and is read out verbatim. The icon in an icon button is decoration - the control carries the name - so it is aria-hidden=\"true\".\n- `icon-button-is-named`: The icon is hidden from assistive technology, so an icon button has nothing left to be named by. Give it an aria-label.\n"
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
"file": "kpi.md",
|
|
124
|
+
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### KPI\nOne key figure in a card, with a trend chip, a sparkline and details.\n\nComponent ID: `kpi`\n\n[Component documentation](https://www.expressivecss.com/kpi.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/kpi.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.13.1)\n\nContract: ExpressiveCSS 0.13.1\n\nContract SHA-256: `18ec4146d300280bbcb74eb3b23f16d1015a0a3db86a8c59fc8348dbdf9d96c2`\n\n#### Selection and adaptation\n\nRuntime ownership: `css-only`. No dedicated entry in the reviewed Google component inventory.\n\nExample: Revenue, orders and churn across the top of a sales dashboard, each with its last 10 days, are KPIs. Twelve figures with no trend are a stats row.\n\n#### Material mapping\n\nRelationship: none. Web extension; no dedicated entry was observed in the reviewed current M3 component inventory.\n\nUpstream: web-extension-inventory-reviewed (2026-10-08); [evidence](https://m3.material.io/components).\n\nRequirements and boundaries: design reference web-extensions-review. The inventory page renders client-side, so this review relies on the 2026-09-13 inventory review. Full visual parity and spoken output unverified.\n\nSupport (2026-10-08, `llm.md#kpi`): A card with a labelled value in a description list, a trend chip with up, down and negative states, a tonal icon, a corner action, a sparkline, a progress bar and a footer.\n\nWeb adaptation: A <dl> pairs the value with its label inside a card <article>; the arrow is decoration, the text carries the direction and the sparkline's table is its text alternative.\n\nKnown boundary: Part placement, the value type role, arrow direction, the negative chip and chart colors and term-definition pairing are checked in a browser; enlarged text and spoken output remain unverified.\n\nFull parity remains unassessed. [Capability evidence](../references/capability-roadmap.md#kpi).\n\n#### Contract\n\nAn `<article class=\"kpi\">` holds a `<dl>` with a `<dt>` label, a `<dd class=\"kpi-value\">` and an optional `<dd class=\"kpi-trend\">` chip. The description list keeps the value paired with its label for assistive technology. The article is a card, so `filled` and `outlined` apply, and KPIs in a grid row stretch to one height with their footers aligned. CSS only; a chart inside is drawn by its own script.\n\n#### Syntax\n\n```html\n<article class=\"kpi outlined\">\n <dl>\n <dt><span class=\"kpi-icon material-symbols-outlined\" aria-hidden=\"true\">payments</span>Revenue</dt>\n <dd class=\"kpi-value\">$48,200</dd>\n <dd class=\"kpi-trend up\">+12.5%</dd>\n </dl>\n <button type=\"button\" class=\"icon-button kpi-actions\" aria-label=\"Revenue options\">\n <span class=\"material-symbols-outlined\" aria-hidden=\"true\">more_vert</span>\n </button>\n <figure class=\"line-chart sparkline area\">\n <figcaption class=\"visually-hidden\">Revenue over 10 days, in thousands</figcaption>\n <table>\n <thead><tr><th>Day</th><th>Revenue</th></tr></thead>\n <tbody><tr><th>Day 1</th><td>40.1</td></tr><tr><th>Day 2</th><td>42.3</td></tr></tbody>\n </table>\n </figure>\n <div class=\"kpi-footer\">Compared with $42,800 last month</div>\n</article>\n```\n\n#### Also documented\n\nThe KPI section of the [API reference](https://www.expressivecss.com/llm.md) also covers Tokens.\n\n#### Rules\n\n- `kpi-is-a-card-with-a-labelled-value`: Write .kpi as an <article> holding a <dl> whose <dt> label comes right before the <dd class=\"kpi-value\">. The article makes it a card, and without the pairing a screen reader reads a bare number with nothing to say what it is. Not fragmentSafe: what it detects is an omission.\n"
|
|
77
125
|
},
|
|
78
126
|
{
|
|
79
127
|
"file": "lightbox.md",
|
|
80
|
-
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Lightbox\nLightbox for enlarge-on-click images.\n\nComponent ID: `lightbox`\n\n[Component documentation](https://www.expressivecss.com/media.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/media.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.
|
|
128
|
+
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Lightbox\nLightbox for enlarge-on-click images.\n\nComponent ID: `lightbox`\n\n[Component documentation](https://www.expressivecss.com/media.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/media.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.13.1)\n\nContract: ExpressiveCSS 0.13.1\n\nContract SHA-256: `18ec4146d300280bbcb74eb3b23f16d1015a0a3db86a8c59fc8348dbdf9d96c2`\n\n#### Selection and adaptation\n\nRuntime ownership: `auto-init`. No dedicated entry in the reviewed Google component inventory.\n\nExample: A payment confirmation needs a dialog, even if its design contains an image. Lightbox is for inspecting enlarged media.\n\n#### Material mapping\n\nRelationship: none. Web extension; no dedicated entry was observed in the reviewed current M3 component inventory.\n\nUpstream: web-extension-inventory-reviewed (2026-09-13); [evidence](https://m3.material.io/components).\n\nRequirements and boundaries: design reference feedback-material-review. Full visual parity and spoken output unverified.\n\nSupport (2026-09-13, `llm.md#lightbox`): Enlarge-on-activation image and dismissal.\n\nWeb adaptation: ExpressiveCSS media behavior, not an upstream component contract.\n\nKnown boundary: Enlargement and teardown are checked; modal containment, reduced motion and destruction during closing remain unverified.\n\nFull parity remains unassessed. [Capability evidence](../references/capability-roadmap.md#lightbox).\n\n#### Contract\n\nLightbox supports Space and native button activation. Repeated open or close requests are ignored; reopening during closing cancels the old transition. Canceled transitions do not report completion. Reduced motion skips animation and finishes pending transitions when enabled while mounted. Destroy cancels pending work, retains the original image, restores owned attributes and styles, and preserves trigger focus without overriding application focus. Shared ancestor overflow remains visible until the last open Lightbox releases it. This does not supply a modal-gallery focus contract.\n\nLightbox is Expressive’s material-style enlarge-on-click image. Click an image with `lightboxed` and it centers and grows. Click it again, scroll, or press Escape to dismiss. `AutoInit()` starts every `.lightboxed` image except those marked `no-autoinit`.\n\n#### Syntax\n\n```html\n<img class=\"lightboxed\" tabindex=\"0\" role=\"button\" width=\"650\" alt=\"A mountain lake\" src=\"images/sample-1.jpg\">\n```\n\n#### Options\n\n| Name | Type | Default | Description |\n| --- | --- | --- | --- |\n| `inDuration` | Number | `275` | Open transition duration, in milliseconds. |\n| `outDuration` | Number | `200` | Close transition duration, in milliseconds. |\n| `onOpenStart` | Function | `null` | Called before the lightbox opens. |\n| `onOpenEnd` | Function | `null` | Called after the lightbox opens. |\n| `onCloseStart` | Function | `null` | Called before the lightbox closes. |\n| `onCloseEnd` | Function | `null` | Called after the lightbox closes. |\n\n#### Methods\n\n- `.open()`: Open the lightbox.\n- `.close()`: Close the lightbox.\n- `.destroy()`: Destroy the plugin instance and tear down its event handlers.\n\n#### Also documented\n\nThe Lightbox section of the [API reference](https://www.expressivecss.com/llm.md) also covers Initialization, Properties, Captions.\n\n#### Rules\n\n- `lightbox-trigger-is-operable`: A .lightboxed image opens an overlay on click, which makes it a control - and a bare <img> is not focusable and not operable from the keyboard. Wrap it in a <button>, or give it tabindex=\"0\" and a role.\n"
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
"file": "line-chart.md",
|
|
132
|
+
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Line chart\nTrends over time drawn from a data table, with a tooltip, a legend and a sparkline size.\n\nComponent ID: `line-chart`\n\n[Component documentation](https://www.expressivecss.com/line-chart.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/line-chart.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.13.1)\n\nContract: ExpressiveCSS 0.13.1\n\nContract SHA-256: `18ec4146d300280bbcb74eb3b23f16d1015a0a3db86a8c59fc8348dbdf9d96c2`\n\n#### Selection and adaptation\n\nRuntime ownership: `auto-init`. No dedicated entry in the reviewed Google component inventory.\n\nExample: Monthly revenue against last year and a target is a line chart with three series. The same figures for twenty regions are a data table.\n\n#### Material mapping\n\nRelationship: none. Web extension; no dedicated entry was observed in the reviewed current M3 component inventory.\n\nUpstream: web-extension-inventory-reviewed (2026-10-06); [evidence](https://m3.material.io/components).\n\nRequirements and boundaries: design reference web-extensions-review. The inventory page renders client-side, so this review relies on the 2026-09-13 inventory review. Full visual parity and spoken output unverified.\n\nSupport (2026-10-06, `llm.md#line-chart`): Smooth monotone lines drawn from a figure's data table, with area, stacked, fade and sparkline modifiers, dashed series, gaps, a fixed y range, x labels, a legend, and a pointer and keyboard tooltip.\n\nWeb adaptation: The <table> stays in the page as the text alternative and the no-JavaScript fallback; the SVG is aria-hidden and the plot is one Tab stop with a polite live tooltip.\n\nKnown boundary: Drawing, the hidden table, gaps, the y range, RTL, the tooltip, keyboard movement, forced-color swatches and destroy are checked in a browser; screen reader output and enlarged text remain unverified. There is no y axis labelling.\n\nFull parity remains unassessed. [Capability evidence](../references/capability-roadmap.md#line-chart).\n\n#### Contract\n\nA `<figure class=\"line-chart\">` holds a `<figcaption>` and a `<table>`. The first column labels the x axis; each further column is a series named by its header cell. `AutoInit()` draws the table as smooth SVG lines with x labels, a legend when there is more than one series, and a cursor and tooltip that follow the pointer. The table stays in the page, visually hidden, as the chart's text alternative; without JavaScript it shows instead of the chart.\n\n#### Syntax\n\n```html\n<figure class=\"line-chart fade\">\n <figcaption>Monthly revenue, 2026</figcaption>\n <table>\n <thead>\n <tr><th>Month</th><th>Revenue</th><th class=\"dashed\">Target</th></tr>\n </thead>\n <tbody>\n <tr><th>Jan</th><td>$4,400</td><td>$5,200</td></tr>\n <tr><th>Feb</th><td>$3,400</td><td>$5,400</td></tr>\n <tr><th>Mar</th><td>$3,700</td><td>$5,100</td></tr>\n </tbody>\n </table>\n</figure>\n```\n\n#### Also documented\n\nThe Line chart section of the [API reference](https://www.expressivecss.com/llm.md) also covers Tokens.\n\n#### Rules\n\n- `line-chart-is-a-figure-with-a-table`: Write .line-chart as a <figure> holding a <table> of the data. The drawn chart is hidden from assistive technology, so the table is all a screen reader reads, and it is what shows without JavaScript.\n- `line-chart-has-a-caption`: Name a .line-chart with a <figcaption>. It names the focusable plot as well as the figure; hide it with .visually-hidden when nearby text already names the chart.\n"
|
|
81
133
|
},
|
|
82
134
|
{
|
|
83
135
|
"file": "lists.md",
|
|
84
|
-
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Lists\nContinuous vertical indexes of text and images.\n\nComponent ID: `lists`\n\n[Component documentation](https://www.expressivecss.com/lists.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/lists.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.
|
|
136
|
+
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Lists\nContinuous vertical indexes of text and images.\n\nComponent ID: `lists`\n\n[Component documentation](https://www.expressivecss.com/lists.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/lists.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.13.1)\n\nContract: ExpressiveCSS 0.13.1\n\nContract SHA-256: `18ec4146d300280bbcb74eb3b23f16d1015a0a3db86a8c59fc8348dbdf9d96c2`\n\n#### Selection and adaptation\n\nRuntime ownership: `css-only`. [Google guidance](https://m3.material.io/components/lists/overview)\n\nExample: A product with a large image, description, price, and several actions may need a card. A simple product-name index remains a list.\n\n#### Material mapping\n\nRelationship: component.\n\nUpstream: requirements-prose-reviewed (2026-09-13); [evidence](https://m3.material.io/components/lists/overview).\n\nRequirements and boundaries: design reference feedback-material-review. Full visual parity and spoken output unverified.\n\n[Specification link](https://m3.material.io/components/lists/specs); full specs unreviewed.\n\n[Google guidelines](https://m3.material.io/components/lists/guidelines).\n\nSupport (2026-09-13, `llm.md#lists`): Standard and segmented lists.\n\nWeb adaptation: Native list semantics; visual selection alone does not justify aria-selected.\n\nKnown boundary: Ordinary native lists and child controls do not implement Google composite arrow navigation.\n\nFull parity remains unassessed. [Capability evidence](../references/capability-roadmap.md#lists).\n\n#### Contract\n\nContinuous, vertical indexes of text and images. Use a list so people can find an item and act on it. There is no JavaScript — the HTML is the component.\n\nThe leading visual is the first icon, `<img>`, or `<input>`. The trailing action is the last icon, `<kbd>`, `<button>`, `<time>`, or `.meta`. A following `<p>` is supporting text. Mark the current row with `active` or `selected` — **not** `aria-selected`, which is not valid on a plain `listitem` and needs a role (`option`, `tab`, `row`, `treeitem`) that brings a keyboard contract with it.\n\nTwo variants: standard (the default) and `segmented`.\n\n#### Syntax\n\n```html\n<ul class=\"list\">\n <li>\n <span class=\"material-symbols\" aria-hidden=\"true\">star</span>\n List item\n <kbd>⌘C</kbd>\n </li>\n <li class=\"selected\">\n <span class=\"material-symbols\" aria-hidden=\"true\">star</span>\n List item\n <kbd>⌘C</kbd>\n </li>\n</ul>\n```\n\n#### Also documented\n\nThe Lists section of the [API reference](https://www.expressivecss.com/llm.md) also covers Standard, Segmented, Text, Icons, Links.\n\n#### Rules\n\n- `list-item-not-selected`: aria-selected is not valid on a listitem. A plain <ul> has nowhere to put it - either give the item a role that takes it (option, tab, row, treeitem) and implement that role’s keyboard contract, or drop the attribute.\n"
|
|
85
137
|
},
|
|
86
138
|
{
|
|
87
139
|
"file": "loading-indicator.md",
|
|
88
|
-
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Loading indicator\nA shape that morphs while it spins, for waits under five seconds.\n\nComponent ID: `loading-indicator`\n\n[Component documentation](https://www.expressivecss.com/loading-indicator.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/loading-indicator.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.
|
|
140
|
+
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Loading indicator\nA shape that morphs while it spins, for waits under five seconds.\n\nComponent ID: `loading-indicator`\n\n[Component documentation](https://www.expressivecss.com/loading-indicator.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/loading-indicator.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.13.1)\n\nContract: ExpressiveCSS 0.13.1\n\nContract SHA-256: `18ec4146d300280bbcb74eb3b23f16d1015a0a3db86a8c59fc8348dbdf9d96c2`\n\n#### Selection and adaptation\n\nRuntime ownership: `css-only`. [Google guidance](https://m3.material.io/components/loading-indicator/overview)\n\nExample: A file upload reporting 42% needs determinate progress, not the morphing loading shape. A longer unmeasured job needs indeterminate progress. Do not flash an indicator for an effectively instant response or announce a fabricated value.\n\n#### Material mapping\n\nRelationship: component. Use for brief unknown waits, typically under five seconds. Do not replace progress that transitions from unknown to determinate, or circular progress within buttons.\n\nUpstream: requirements-prose-reviewed (2026-09-13); [evidence](https://m3.material.io/components/loading-indicator/overview).\n\nRequirements and boundaries: design reference feedback-material-review. Full visual parity and spoken output unverified.\n\n[Specification link](https://m3.material.io/components/loading-indicator/specs); full specs unreviewed.\n\n[Google guidelines](https://m3.material.io/components/loading-indicator/guidelines).\n\nSupport (2026-09-13, `llm.md#loading-indicator`): Uncontained and contained morphing loading indicator.\n\nWeb adaptation: CSS-only; the author supplies an accessible waiting message.\n\nKnown boundary: The framework requires a named status while Google recommends progressbar; an indeterminate progressbar validly omits aria-valuenow.\n\nFull parity remains unassessed. [Capability evidence](../references/capability-roadmap.md#loading-indicator).\n\n#### Contract\n\nM3 Expressive's indicator for a short wait — a shape that morphs while it spins.\n\nReach for it whenever the wait is under about five seconds and you cannot report a percentage. It replaces most short indeterminate circular uses. Keep a progress indicator for a process that will transition to a determinate value; Google's button guidance also retains circular progress. `.progress` supports both linear bars and determinate cases.\n\nOne element, CSS-only, no JavaScript plugin. The element is empty, so it reports nothing on its own. Supply a named waiting state and meaningful status text updates; verify spoken delivery with assistive technology. An indeterminate `progressbar` is also valid and omits `aria-valuenow`. The current framework contract requires a named status. A name alone does not prove an announcement.\n\n#### Syntax\n\n```html\n<span class=\"loading-indicator\" role=\"status\" aria-label=\"Loading\"></span>\n```\n\n#### Also documented\n\nThe Loading indicator section of the [API reference](https://www.expressivecss.com/llm.md) also covers Contained, Tokens.\n\n#### Rules\n\n- `loading-indicator-reports-itself`: The current loading-indicator contract requires role=\"status\". Provide meaningful waiting text updates and verify announcement delivery; a role alone does not prove spoken output.\n- `loading-indicator-is-named`: Name the waiting state with aria-label or aria-labelledby. Verify meaningful status updates separately from its accessible name.\n"
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
"file": "marquee.md",
|
|
144
|
+
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Marquee\nA row of logos, images or text that scrolls without end.\n\nComponent ID: `marquee`\n\n[Component documentation](https://www.expressivecss.com/marquee.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/marquee.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.13.1)\n\nContract: ExpressiveCSS 0.13.1\n\nContract SHA-256: `18ec4146d300280bbcb74eb3b23f16d1015a0a3db86a8c59fc8348dbdf9d96c2`\n\n#### Selection and adaptation\n\nRuntime ownership: `css-only`. No dedicated entry in the reviewed Google component inventory.\n\nExample: Twelve customer logos under a landing page hero are a marquee. Four featured products people should open are a carousel or a card grid.\n\n#### Material mapping\n\nRelationship: none. Web extension; no dedicated entry was observed in the reviewed current M3 component inventory.\n\nUpstream: web-extension-inventory-reviewed (2026-10-08); [evidence](https://m3.material.io/components).\n\nRequirements and boundaries: design reference web-extensions-review. The inventory page renders client-side, so this review relies on the 2026-09-13 inventory review. Full visual parity and spoken output unverified.\n\nSupport (2026-10-08, `llm.md#marquee`): Two identical groups that loop horizontally or vertically, with reverse, edge fades, a paused class, hover and focus pausing, token duration and gap, a reversed direction in RTL and a hand-scrolled strip under reduced motion.\n\nWeb adaptation: A visual copy of the item list marked aria-hidden, with its links taken out of the tab order, so assistive technology and the keyboard meet each item once.\n\nKnown boundary: The group placement, the half-cycle position, the right-to-left and vertical directions, reverse, pausing on hover, focus and .paused, the hidden copy in the accessibility tree and the reduced-motion stop are checked in a browser; spoken output and long-running smoothness remain unverified.\n\nFull parity remains unassessed. [Capability evidence](../references/capability-roadmap.md#marquee).\n\n#### Contract\n\nA `.marquee` holds two identical `<ul class=\"marquee-content\">` groups. The second is a copy that fills the gap as the first scrolls out, so mark it `aria-hidden=\"true\"` and give each link or button in it `tabindex=\"-1\"`. Each group is at least as wide as the strip and spreads short content out, so one copy is enough. Hovering over the strip or focusing a link inside pauses it. CSS only.\n\n#### Syntax\n\n```html\n<div class=\"marquee fade\">\n <ul class=\"marquee-content\" aria-label=\"Customers\">\n <li><img src=\"launchpad.svg\" alt=\"Launchpad\"></li>\n <li><a href=\"/customers/greenleaf\"><img src=\"greenleaf.svg\" alt=\"Greenleaf\"></a></li>\n </ul>\n <ul class=\"marquee-content\" aria-hidden=\"true\">\n <li><img src=\"launchpad.svg\" alt=\"Launchpad\"></li>\n <li><a href=\"/customers/greenleaf\" tabindex=\"-1\"><img src=\"greenleaf.svg\" alt=\"Greenleaf\"></a></li>\n </ul>\n</div>\n```\n\n#### Also documented\n\nThe Marquee section of the [API reference](https://www.expressivecss.com/llm.md) also covers Tokens.\n\n#### Rules\n\n- `marquee-copy-is-hidden`: The second .marquee-content repeats the first so the loop has no gap. Mark it aria-hidden=\"true\" and give each link or button in it tabindex=\"-1\", or a screen reader reads every item twice.\n"
|
|
89
145
|
},
|
|
90
146
|
{
|
|
91
147
|
"file": "menu.md",
|
|
92
|
-
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Menu\nMaterial Design 3 menus, from the HTML.\n\nComponent ID: `menu`\n\n[Component documentation](https://www.expressivecss.com/menu.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/menu.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.
|
|
148
|
+
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Menu\nMaterial Design 3 menus, from the HTML.\n\nComponent ID: `menu`\n\n[Component documentation](https://www.expressivecss.com/menu.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/menu.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.13.1)\n\nContract: ExpressiveCSS 0.13.1\n\nContract SHA-256: `18ec4146d300280bbcb74eb3b23f16d1015a0a3db86a8c59fc8348dbdf9d96c2`\n\n#### Selection and adaptation\n\nRuntime ownership: `auto-init`. [Google guidance](https://m3.material.io/components/menus/overview)\n\n#### Material mapping\n\nRelationship: component.\n\nUpstream: requirements-prose-reviewed (2026-09-13); [evidence](https://m3.material.io/components/menus/overview).\n\nRequirements and boundaries: design reference inputs-material-review. Full visual parity and spoken output unverified.\n\n[Specification link](https://m3.material.io/components/menus/specs); full specs unreviewed.\n\n[Google guidelines](https://m3.material.io/components/menus/guidelines).\n\nSupport (2026-10-06, `llm.md#menu`): Vertical menus, nested flyouts, grouping, standard/vibrant styles, and context menus opened at the pointer.\n\nWeb adaptation: Native menu/li structure; Menu owns opening and keyboard behavior.\n\nKnown boundary: Closing focus exclusion, scoped nested keys, RTL flyout placement and representative item targets have browser checks. Spoken output and full visual parity remain unverified.\n\nFull parity remains unassessed. [Capability evidence](../references/capability-roadmap.md#menu).\n\n#### Contract\n\nClosing menus are excluded from keyboard focus, and close callbacks preserve focus they move. Submenu entry/return keys mirror in RTL; typeahead stays in the active list and skips headings and disabled items. Escape closes the innermost submenu first. Fine-pointer hover opens flyouts; keyboard activation uses Enter, Space, or the submenu entry arrow.\n\nInside open or closed shadow roots, `closeOnClick` controls item clicks just as it does in the document. Submenu triggers keep the parent menu open; outside clicks dismiss it.\n\nA `<menu>` is the surface. Each `<li>` is an item. An icon leads its label by default; add `.suffix` to send it to the trailing edge, since a lone icon is indistinguishable from a leading one in CSS. A `<kbd>` or a `.badge` is always trailing content. An `<li class=\"divider\" role=\"separator\">` is a divider — `<menu>` is a list and its content model permits only `<li>`, so a bare `<hr>` between entries is invalid (it still renders); a `.gap` splits groups; a `.label` is a heading. A nested `<menu>` is a flyout. The trigger’s `data-target` must match the menu’s `id`. `.menu-trigger` is the JavaScript contract.\n\n#### Syntax\n\n```html\n<button class=\"menu-trigger\" data-target=\"menu1\">Drop me</button>\n<menu id=\"menu1\">\n <li><button type=\"button\">One</button></li>\n <li><button type=\"button\">Two</button></li>\n <li class=\"divider\" role=\"separator\"></li>\n <li><button type=\"button\">Three</button></li>\n <li>\n <button type=\"button\">\n <span class=\"material-symbols\" aria-hidden=\"true\">cloud</span>\n <span>Five</span>\n </button>\n </li>\n</menu>\n```\n\n#### Options\n\n| Name | Type | Default | Description |\n| --- | --- | --- | --- |\n| `alignment` | String | `'left'` | Edge the menu is aligned to. `'left'` or `'right'`. |\n| `autoFocus` | Boolean | `true` | If true, automatically focus the menu for keyboard navigation. This option is named `autoFocus`, not `autoTrigger`. |\n| `constrainWidth` | Boolean | `true` | If true, the menu is as wide as the trigger. M3 menus are independently sized — pass `false` for that. |\n| `container` | Element | `null` | Element that will contain the menu. When omitted, the menu is moved next to the trigger. |\n| `coverTrigger` | Boolean | `false` | If false, the menu opens below the trigger (the M3 placement). Pass `true` to cover the trigger. |\n| `closeOnClick` | Boolean | `true` | If true, close the menu when an item is clicked. |\n| `hover` | Boolean | `false` | If true, the menu opens on hover instead of click. |\n| `contextMenu` | Boolean | `false`; `true` with `data-context-menu` | If true, the trigger is a region and the menu opens at the pointer on right-click, long-press, the menu key or Shift+F10, instead of on click. |\n| `inDuration` | Number | `150` | Enter transition duration, in milliseconds. |\n| `outDuration` | Number | `250` | Exit transition duration, in milliseconds. |\n| `onOpenStart` | Function | `null` | Called when the menu starts opening. |\n| `onOpenEnd` | Function | `null` | Called when opening completes; superseded transitions and destruction cancel it. |\n| `onCloseStart` | Function | `null` | Called when the menu starts closing. |\n| `onCloseEnd` | Function | `null` | Called when closing completes; reopening and destruction cancel it. |\n| `onItemClick` | Function | `null` | Called when an item is clicked. Receives the `li`. |\n\n#### Methods\n\n- `.open()`: Open the menu.\n- `.close()`: Close the menu. With `autoFocus`, focus returns to the trigger when it remains in the menu. Focus moved to an outside control is preserved.\n- `.recalculateDimensions()`: While the menu is open, recalculate its dimensions if its contents have changed.\n- `.destroy()`: Destroy the plugin instance and tear down its event handlers.\n\n#### Also documented\n\nThe Menu section of the [API reference](https://www.expressivecss.com/llm.md) also covers Context menu, Initialization, Properties.\n\n#### Rules\n\n- `menu-children-are-list-items`: <menu> is a list: its content model permits only <li>. A bare <hr> between entries is invalid - put the separator inside an <li>, or use role=separator there.\n- `context-menu-region-is-not-a-control`: data-context-menu goes on a region of content. A button or link already has an action for Enter and click; give it its own menu trigger instead.\n"
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
"file": "message-scroller.md",
|
|
152
|
+
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Message scroller\nA chat scroll container that anchors turns, follows streamed replies and keeps its place as history loads.\n\nComponent ID: `message-scroller`\n\n[Component documentation](https://www.expressivecss.com/message-scroller.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/message-scroller.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.13.1)\n\nContract: ExpressiveCSS 0.13.1\n\nContract SHA-256: `18ec4146d300280bbcb74eb3b23f16d1015a0a3db86a8c59fc8348dbdf9d96c2`\n\n#### Selection and adaptation\n\nRuntime ownership: `auto-init`. No dedicated entry in the reviewed Google component inventory.\n\nExample: A support chat that streams replies needs a message scroller so the reply stays in view; a fixed list of three FAQ answers does not.\n\n#### Material mapping\n\nRelationship: none. Web extension; no dedicated entry was observed in the reviewed current M3 component inventory.\n\nUpstream: web-extension-inventory-reviewed (2026-10-08); [evidence](https://m3.material.io/components).\n\nRequirements and boundaries: design reference web-extensions-review. The inventory page renders client-side, so this review relies on the 2026-09-13 inventory review. Full visual parity and spoken output unverified.\n\nSupport (2026-10-08, `llm.md#message-scroller`): Following streamed output, release on scrolling up, a scroll-to-latest button, turn anchors with a peek of the previous message, prepend preservation, opening at the end, start or last turn, and jumping to a message by id.\n\nWeb adaptation: A native scroll container; the component sets scroll position, a spacer and the button's inert state. The page owns role=\"log\" and aria-busy.\n\nKnown boundary: Following, release on scrolling up, the inert button, prepend preservation, turn anchoring with the spacer, saved-transcript positions and jump to a message are checked in a browser; announcements in a role=\"log\" viewport, touch and scrollbar dragging, and spoken output remain unverified.\n\nFull parity remains unassessed. [Capability evidence](../references/capability-roadmap.md#message-scroller).\n\n#### Contract\n\nA `.message-scroller` frame holds a `.message-scroller-viewport` and an optional `.message-scroller-button`. Every child of the viewport is one item, usually a `.message` or `.message-group`. The frame fills its parent, so the parent sets the height. `AutoInit()` starts every `.message-scroller`.\n\nWhile the reader is at the end, the viewport follows new content, including text streamed into the last message. Scrolling toward the start with the wheel, touch, the scrollbar or the Up, Page Up and Home keys stops following; reaching the end again, or pressing the button, resumes it. Mark the message that starts a turn with `data-scroll-anchor`, usually each of the user's messages. When an anchor is appended, the viewport scrolls it to the top with 64px of the previous message in view, and blank space below it makes room for the reply until the reply fills the viewport. Older messages inserted at the start keep the visible message in place, and anchors among them do not scroll.\n\nName the viewport with `aria-label` and give it `tabindex=\"0\"` so the keyboard can scroll it. On a conversation that receives messages, make the viewport the log with `role=\"log\"` and `aria-relevant=\"additions\"`, and set `aria-busy=\"true\"` on it while a reply streams. The button is inert while the viewport is at the end; give it an `aria-label`.\n\n#### Syntax\n\n```html\n<div class=\"message-scroller\">\n <div class=\"message-scroller-viewport\" role=\"log\" aria-label=\"Conversation\"\n aria-relevant=\"additions\" tabindex=\"0\">\n <div id=\"m1\" class=\"message end\" data-scroll-anchor>\n <p class=\"message-bubble\">Can you check why the install fails on CI?</p>\n </div>\n <div id=\"m2\" class=\"message\">\n <p class=\"message-bubble\">Sure. Which package manager does the workspace use?</p>\n </div>\n </div>\n <button type=\"button\" class=\"icon-button tonal message-scroller-button\"\n aria-label=\"Scroll to latest message\">\n <span class=\"material-symbols\" aria-hidden=\"true\">arrow_downward</span>\n </button>\n</div>\n```\n\n#### Options\n\n| Name | Type | Default | Description |\n| --- | --- | --- | --- |\n| `autoScroll` | Boolean | `true` | If true, the viewport follows new content while the reader is at the end, and scrolls a newly appended anchor to the top. |\n| `defaultScrollPosition` | String | `'end'` | Where the transcript opens: `'end'`, `'start'`, or `'last-anchor'`, the start of the last turn. `'last-anchor'` opens at the end when that turn fits. A hidden viewport opens when it is first shown. |\n| `peek` | Number | `64` | Pixels of the previous message left in view above an anchored turn or a message reached with `scrollToMessage()`. |\n\n#### Methods\n\n- `.scrollToEnd()`: Scrolls to the latest message and resumes following.\n- `.scrollToStart()`: Scrolls to the first message.\n- `.scrollToMessage(id)`: Scrolls the element with this id to the top of the viewport, below the peek, and stops following. Returns `false` when the viewport holds no such element.\n- `.destroy()`: Removes the listeners, observers and spacer the component added.\n\n#### Also documented\n\nThe Message scroller section of the [API reference](https://www.expressivecss.com/llm.md) also covers Tokens.\n\n#### Rules\n\n- `message-scroller-viewport-is-named`: Name the viewport with aria-label. It is a focusable scroll region, and a screen reader announces the name when focus lands on it.\n"
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
"file": "message.md",
|
|
156
|
+
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Message\nOne message in a conversation, with an optional avatar, header and footer.\n\nComponent ID: `message`\n\n[Component documentation](https://www.expressivecss.com/message.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/message.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.13.1)\n\nContract: ExpressiveCSS 0.13.1\n\nContract SHA-256: `18ec4146d300280bbcb74eb3b23f16d1015a0a3db86a8c59fc8348dbdf9d96c2`\n\n#### Selection and adaptation\n\nRuntime ownership: `css-only`. No dedicated entry in the reviewed Google component inventory.\n\nExample: A \"Saved\" confirmation after an edit is app feedback, not a conversation. Use a snackbar for it, not a message bubble.\n\n#### Material mapping\n\nRelationship: none. Web extension; no dedicated entry was observed in the reviewed current M3 component inventory.\n\nUpstream: web-extension-inventory-reviewed (2026-10-06); [evidence](https://m3.material.io/components).\n\nRequirements and boundaries: design reference web-extensions-review. The inventory page renders client-side, so this review relies on the 2026-09-13 inventory review. Full visual parity and spoken output unverified.\n\nSupport (2026-10-06, `llm.md#message`): Start and end alignment, avatar, header, footer actions, message groups and outlined attachment bubbles.\n\nWeb adaptation: Plain div parts on a CSS grid; the page owns role=\"log\" on a live conversation.\n\nKnown boundary: Avatar anchoring, start and end sides in LTR and RTL, and grouped corners are checked in a browser; announcements for appended messages and spoken output remain unverified.\n\nFull parity remains unassessed. [Capability evidence](../references/capability-roadmap.md#message).\n\n#### Contract\n\nA `.message` lays out one message. It places an optional `.message-avatar`, `.message-header` and `.message-footer` around a single surface, usually a `.message-bubble`. Every other child is the surface, so wrap several elements in one `<div>` to keep them together. Messages sit on the start side; add `end` for the sender's own messages, which also turns the bubble to `primary-container`. Write the parts as `<div>`: outside sectioning content a `<header>` is a banner landmark and a `<footer>` is contentinfo.\n\nThe avatar is an `<img>`, or a `<span>` holding initials or an icon. It lines up with the bottom of the surface and stays clear of the footer. Give the image an empty `alt` when a header already names the sender. Put `role=\"log\"` on a conversation that receives new messages so screen readers announce them. CSS only; there is no JavaScript.\n\n#### Syntax\n\n```html\n<div role=\"log\" aria-label=\"Conversation with Oliver\">\n <div class=\"message end\">\n <p class=\"message-bubble\">Deploying to prod real quick.</p>\n <div class=\"message-footer\">Delivered</div>\n </div>\n <div class=\"message\">\n <img class=\"message-avatar\" src=\"oliver.jpg\" alt=\"\">\n <div class=\"message-header\">Oliver</div>\n <p class=\"message-bubble\">It's 4:55 PM. On a Friday.</p>\n </div>\n</div>\n```\n\n#### Also documented\n\nThe Message section of the [API reference](https://www.expressivecss.com/llm.md) also covers Groups, Footer actions, Attachments, Tokens.\n\n#### Rules\n\n- `message-parts-are-not-landmarks`: Write .message-header and .message-footer as <div>. Outside sectioning content a <header> is a banner landmark and a <footer> is contentinfo, so a conversation would fill the landmark menu with one of each per message.\n"
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
"file": "mixed-chart.md",
|
|
160
|
+
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Mixed chart\nColumns, lines and areas together from one data table, with a second scale for values in other units.\n\nComponent ID: `mixed-chart`\n\n[Component documentation](https://www.expressivecss.com/mixed-chart.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/mixed-chart.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.13.1)\n\nContract: ExpressiveCSS 0.13.1\n\nContract SHA-256: `18ec4146d300280bbcb74eb3b23f16d1015a0a3db86a8c59fc8348dbdf9d96c2`\n\n#### Selection and adaptation\n\nRuntime ownership: `auto-init`. No dedicated entry in the reviewed Google component inventory.\n\nExample: Monthly revenue as columns with a dashed target line, or ad spend with conversions on a second scale, is a mixed chart. Three revenue lines for three plans are a line chart.\n\n#### Material mapping\n\nRelationship: none. Web extension; no dedicated entry was observed in the reviewed current M3 component inventory.\n\nUpstream: web-extension-inventory-reviewed (2026-10-07); [evidence](https://m3.material.io/components).\n\nRequirements and boundaries: design reference web-extensions-review. The inventory page renders client-side, so this review relies on the 2026-09-13 inventory review. Full visual parity and spoken output unverified.\n\nSupport (2026-10-07, `llm.md#mixed-chart`): Columns, smooth lines and areas drawn per series from a figure's data table by header class, with dashed lines, a second scale for end series with its own fixed range, x labels, a legend, a sparkline modifier, and a pointer and keyboard tooltip with points on the lines.\n\nWeb adaptation: The <table> stays in the page as the text alternative and the no-JavaScript fallback; the columns and SVG are aria-hidden and the plot is one Tab stop with a polite live tooltip.\n\nKnown boundary: Drawing, the hidden table, both scales, gaps, the legend, the tooltip, cursor points and keyboard movement are checked in a browser; screen reader output, forced colors and enlarged text remain unverified. There is no y axis labelling for either scale, no always-on markers and no stacking.\n\nFull parity remains unassessed. [Capability evidence](../references/capability-roadmap.md#mixed-chart).\n\n#### Contract\n\nA `<figure class=\"mixed-chart\">` takes the same `<figcaption>` and `<table>` as a line chart. `AutoInit()` draws each series by its header cell's class: `column` as columns from zero, `area` as a line over a fading fill, and any other series as a line, with `dashed` for a dashed line. Columns sit behind the lines. A legend names the series and a tooltip follows the pointer from row to row. The table stays in the page, visually hidden, as the chart's text alternative; without JavaScript it shows instead of the chart.\n\n#### Syntax\n\n```html\n<figure class=\"mixed-chart\" data-end-min=\"0\" data-end-max=\"35\">\n <figcaption>Revenue ($K), profit margin and customer growth</figcaption>\n <table>\n <thead>\n <tr>\n <th>Month</th>\n <th class=\"column\">Revenue</th>\n <th class=\"area end\">Profit margin</th>\n <th class=\"dashed end\">Customer growth</th>\n </tr>\n </thead>\n <tbody>\n <tr><th>Jan</th><td>$420</td><td>18%</td><td>4%</td></tr>\n <tr><th>Feb</th><td>$460</td><td>19%</td><td>5%</td></tr>\n </tbody>\n </table>\n</figure>\n```\n\n#### Also documented\n\nThe Mixed chart section of the [API reference](https://www.expressivecss.com/llm.md) also covers Tokens.\n\n#### Rules\n\n- `mixed-chart-is-a-figure-with-a-table`: Write .mixed-chart as a <figure> holding a <table> of the data. The drawn chart is hidden from assistive technology, so the table is all a screen reader reads, and it is what shows without JavaScript.\n- `mixed-chart-has-a-caption`: Name a .mixed-chart with a <figcaption>. It names the focusable plot as well as the figure; hide it with .visually-hidden when nearby text already names the chart.\n"
|
|
93
161
|
},
|
|
94
162
|
{
|
|
95
163
|
"file": "navigation-bar.md",
|
|
96
|
-
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Navigation bar\nSwitch between UI views on compact and medium screens.\n\nComponent ID: `navigation-bar`\n\n[Component documentation](https://www.expressivecss.com/navigation-bar.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/navigation-bar.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.
|
|
164
|
+
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Navigation bar\nSwitch between UI views on compact and medium screens.\n\nComponent ID: `navigation-bar`\n\n[Component documentation](https://www.expressivecss.com/navigation-bar.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/navigation-bar.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.13.1)\n\nContract: ExpressiveCSS 0.13.1\n\nContract SHA-256: `18ec4146d300280bbcb74eb3b23f16d1015a0a3db86a8c59fc8348dbdf9d96c2`\n\n#### Selection and adaptation\n\nRuntime ownership: `css-only`. [Google guidance](https://m3.material.io/components/navigation-bar/overview)\n\nExample: Overview/Activity within an account page should not displace Home/Search/Profile in app navigation. Use tabs for those local panels and preserve the app destinations across widths.\n\n- expanded window: replace-with [navigation-rail](./navigation-rail.md). Use persistent peer navigation at wider widths.\n\n#### Material mapping\n\nRelationship: component. Google replaces the baseline bar with the flexible bar. The documented stacked/horizontal navigation-bar layouts remain current; do not exclude the family by its name.\n\nUpstream: overview-specs-guidelines-accessibility-prose-reviewed (2026-09-13); [evidence](https://m3.material.io/components/navigation-bar/overview).\n\nRendered prose reviewed; collapsed token tables, image-only measurements, runtime and full parity unverified. See the design priority component review for requirement-level findings.\n\nSupport (2026-09-07, `llm.md#navigation-bar`): Stacked and horizontal destination layouts.\n\nWeb adaptation: Links navigate; the application marks the current destination.\n\nFull parity remains unassessed. [Capability evidence](../references/capability-roadmap.md#navigation-bar).\n\n#### Contract\n\nSwitch between UI views on compact and medium screens. A `nav.navigation-bar` holds 3–5 destinations of equal importance. Destinations do not change from screen to screen. There is no JavaScript — mark the current view with `aria-current=\"page\"` (or `active`).\n\nThis is not the app bar. The app bar names the current page and holds 1–2 actions. Use a navigation bar in compact windows, a bar or rail in medium windows, and an expanded navigation rail in wider windows.\n\nDefault. Icon above the label. The selected destination puts a pill behind the icon and fills the glyph.\n\n#### Syntax\n\n```html\n<nav class=\"navigation-bar\" aria-label=\"Main\">\n <a href=\"/\" aria-current=\"page\">\n <span class=\"material-symbols\" aria-hidden=\"true\">home</span>\n Home\n </a>\n <a href=\"/browse\">\n <span class=\"material-symbols\" aria-hidden=\"true\">explore</span>\n Browse\n </a>\n <a href=\"/radio\">\n <span class=\"material-symbols\" aria-hidden=\"true\">radio</span>\n Radio\n </a>\n <a href=\"/library\">\n <span class=\"material-symbols\" aria-hidden=\"true\">library_music</span>\n Library\n </a>\n</nav>\n```\n\n#### Also documented\n\nThe Navigation bar section of the [API reference](https://www.expressivecss.com/llm.md) also covers Stacked, Horizontal, Fixed.\n\n#### Rules\n\n- `navigation-bar-marks-current`: The active destination needs aria-current=\"page\".\n\n#### Guide checks\n\n- Label every <nav> landmark with aria-label or aria-labelledby.\n"
|
|
97
165
|
},
|
|
98
166
|
{
|
|
99
167
|
"file": "navigation-rail.md",
|
|
100
|
-
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Navigation rail\nSwitch between UI views on mid-sized devices.\n\nComponent ID: `navigation-rail`\n\n[Component documentation](https://www.expressivecss.com/navigation-rail.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/navigation-rail.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.
|
|
168
|
+
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Navigation rail\nSwitch between UI views on mid-sized devices.\n\nComponent ID: `navigation-rail`\n\n[Component documentation](https://www.expressivecss.com/navigation-rail.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/navigation-rail.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.13.1)\n\nContract: ExpressiveCSS 0.13.1\n\nContract SHA-256: `18ec4146d300280bbcb74eb3b23f16d1015a0a3db86a8c59fc8348dbdf9d96c2`\n\n#### Selection and adaptation\n\nRuntime ownership: `auto-init`. [Google guidance](https://m3.material.io/components/navigation-rail/overview)\n\n- compact window: replace-with [navigation-bar](./navigation-bar.md). Compact peer destinations fit the bottom edge.\n- expanded window: variant. Use the expanded rail when labels should remain visible.\n\n#### Material mapping\n\nRelationship: component. Google replaces the baseline rail with the collapsed rail and the drawer with the expanded rail. The documented collapsed, expanded and modal variants remain current. Retain a drawer only for compatibility, an explicit request or a verified target-version nesting gap.\n\nUpstream: overview-specs-guidelines-accessibility-prose-reviewed (2026-09-13); [evidence](https://m3.material.io/components/navigation-rail/overview).\n\nRendered prose reviewed; collapsed token tables, image-only measurements, runtime and full parity unverified. See the design priority component review for requirement-level findings.\n\n[Specification link](https://m3.material.io/components/navigation-rail/specs); full specs unreviewed.\n\nSupport (2026-09-07, `llm.md#navigation-rail`): Collapsed, expanded, and modal rails.\n\nWeb adaptation: The compact expanded rail uses a scrim and dismissal behavior.\n\nFull parity remains unassessed. [Capability evidence](../references/capability-roadmap.md#navigation-rail).\n\n#### Contract\n\nSwitch between UI views on mid-sized devices. A `nav.navigation-rail` holds 3–7 destinations plus an optional FAB. Put it in the same place on every screen.\n\nCollapsed is 96dp with the icon above the label. Add `expanded` for 220–360dp, icon and label on one row, and an extended FAB. The menu button toggles that class (`AutoInit()` starts it). On compact windows an expanded rail is modal — a scrim, and Escape or a scrim tap collapses it. Add `modal` to keep that overlay at every breakpoint.\n\nEscape closes an open nested menu first. A second Escape collapses the modal rail and returns focus from inside it to the rail toggle, unless a close callback moves focus elsewhere.\n\n#### Syntax\n\n```html\n<nav class=\"navigation-rail\" aria-label=\"Main\">\n <button type=\"button\" aria-label=\"Menu\">\n <span class=\"material-symbols\" aria-hidden=\"true\">menu</span>\n </button>\n <a class=\"button extra\" href=\"/compose\">\n <span class=\"material-symbols\" aria-hidden=\"true\">edit</span>\n <span>Compose</span>\n </a>\n <a href=\"/\" aria-current=\"page\">\n <span class=\"material-symbols\" aria-hidden=\"true\">star</span>\n Label\n </a>\n <a href=\"/two\" aria-label=\"Starred, 3 unread\">\n <span class=\"material-symbols\" aria-hidden=\"true\">star<span class=\"badge\">3</span></span>\n Label\n </a>\n</nav>\n```\n\n#### Rules\n\n- `navigation-rail-marks-current`: The active destination needs aria-current=\"page\".\n\n#### Guide checks\n\n- Label every <nav> landmark with aria-label or aria-labelledby.\n"
|
|
101
169
|
},
|
|
102
170
|
{
|
|
103
171
|
"file": "pagination.md",
|
|
104
|
-
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Pagination\nA list of pages. The HTML is the component.\n\nComponent ID: `pagination`\n\n[Component documentation](https://www.expressivecss.com/pagination.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/pagination.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.
|
|
172
|
+
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Pagination\nA list of pages. The HTML is the component.\n\nComponent ID: `pagination`\n\n[Component documentation](https://www.expressivecss.com/pagination.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/pagination.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.13.1)\n\nContract: ExpressiveCSS 0.13.1\n\nContract SHA-256: `18ec4146d300280bbcb74eb3b23f16d1015a0a3db86a8c59fc8348dbdf9d96c2`\n\n#### Selection and adaptation\n\nRuntime ownership: `css-only`. No dedicated entry in the reviewed Google component inventory.\n\nExample: A long article with Introduction/Methods/Results needs section links, optionally with scrollspy, not numbered result pages.\n\n#### Material mapping\n\nRelationship: none. Web extension; no dedicated entry was observed in the reviewed current M3 component inventory.\n\nUpstream: web-extension-inventory-reviewed (2026-09-13); [evidence](https://m3.material.io/components).\n\nRequirements and boundaries: design reference web-extensions-review. Full visual parity and spoken output unverified.\n\nSupport (2026-09-13, `llm.md#pagination`): Page links, active page, and unavailable previous/next items.\n\nWeb adaptation: CSS-only navigation; the application supplies destinations.\n\nKnown boundary: aria-disabled and pointer-events alone cannot disable a link for keyboard users.\n\nFull parity remains unassessed. [Capability evidence](../references/capability-roadmap.md#pagination).\n\n#### Contract\n\nSplit long content into shorter, easier blocks.\n\nAdd pagination links to split long content into shorter blocks. The component is CSS-only: a `pagination` list of links. There is no JavaScript plugin.\n\nThe list lives in a `<nav aria-label=\"Pagination\">` — a page's links are navigation, and the label distinguishes it from every other `<nav>` on the page.\n\n#### Syntax\n\n```html\n<nav class=\"pagination\" aria-label=\"Pagination\">\n <ol>\n <li class=\"disabled\">\n <span aria-hidden=\"true\"><span class=\"material-symbols\" aria-hidden=\"true\">chevron_left</span></span>\n </li>\n <li class=\"active\"><a href=\"?page=1\" aria-current=\"page\">1</a></li>\n <li><a href=\"?page=2\">2</a></li>\n <li><a href=\"?page=3\">3</a></li>\n <li><a href=\"?page=4\">4</a></li>\n <li><a href=\"?page=5\">5</a></li>\n <li>\n <a href=\"?page=2\" aria-label=\"Next page\"><span class=\"material-symbols\" aria-hidden=\"true\">chevron_right</span></a>\n </li>\n </ol>\n</nav>\n```\n\n#### Also documented\n\nThe Pagination section of the [API reference](https://www.expressivecss.com/llm.md) also covers Responsive.\n\n#### Rules\n\n- `pagination-in-nav`: Pagination is navigation and belongs in a labelled <nav>, or it is just a list of numbers.\n- `pagination-marks-current`: The current page needs aria-current=\"page\". A class only colours it.\n- `pagination-disabled-not-a-link`: A disabled control that is still an <a href> stays in the tab order and still navigates. Use a <span>, or drop the href.\n\n#### Guide checks\n\n- Label every <nav> landmark with aria-label or aria-labelledby.\n"
|
|
105
173
|
},
|
|
106
174
|
{
|
|
107
175
|
"file": "panes.md",
|
|
108
|
-
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Panes\nMaterial 3 canonical layouts, from the HTML.\n\nComponent ID: `panes`\n\n[Component documentation](https://www.expressivecss.com/panes.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/panes.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.
|
|
176
|
+
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Panes\nMaterial 3 canonical layouts, from the HTML.\n\nComponent ID: `panes`\n\n[Component documentation](https://www.expressivecss.com/panes.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/panes.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.13.1)\n\nContract: ExpressiveCSS 0.13.1\n\nContract SHA-256: `18ec4146d300280bbcb74eb3b23f16d1015a0a3db86a8c59fc8348dbdf9d96c2`\n\n#### Selection and adaptation\n\nRuntime ownership: `css-only`. [Google guidance](https://m3.material.io/foundations/layout/canonical-examples/overview)\n\nExample: A contact directory with no detail view needs a list. Use list-detail panes when selecting a contact reveals a separate detail region.\n\n- medium container: variant. Stack or collapse panes when their container cannot preserve useful pane widths.\n\n#### Material mapping\n\nRelationship: pattern. Canonical layout pattern, not a standalone component.\n\nUpstream: requirements-prose-reviewed (2026-09-13); [evidence](https://m3.material.io/foundations/layout/canonical-examples/overview).\n\nRequirements and boundaries: design reference layout-material-review. Full visual parity and spoken output unverified.\n\nSupport (2026-09-17, `llm.md#panes`): List-detail, supporting pane, and equal panes.\n\nWeb adaptation: CSS viewport and container queries; the application chooses the active compact pane.\n\nKnown boundary: Compact supporting layouts retain one-active-pane selection rather than automatic stacking. Native zoom, assistive technology and physical-device review remain pending.\n\nFull parity remains unassessed. [Capability evidence](../references/capability-roadmap.md#panes).\n\n#### Contract\n\nMaterial 3 canonical layouts — list-detail, supporting pane, and equal panes.\n\nPanes are CSS-only. A container (`panes`, or one of the named aliases `list-detail`, `supporting-pane-layout`, `pane-layout`) holds two or three `pane` children. Layout decisions use the nearest ancestor with inline-size or size containment, falling back to viewport media rules when none exists. Set `container-type: inline-size` on the containing column for nested layouts. The pane layout cannot query its own size. Compact widths (`< 600px`) use 16px inline margins; wider layouts use 24px margins and spacers. Below 840px only one pane shows at a time; at 840px and up the panes sit side by side. Google recommends stacking supporting content below primary content on compact and medium layouts; the current one-active-pane behavior is a framework adaptation. Footer actions wrap to keep translated labels reachable.\n\nAny of `pane`, `list-pane`, `primary-pane`, `detail-pane`, and `supporting-pane` counts as a pane child — the specific names are for readability.\n\n#### Syntax\n\n```html\n<div class=\"panes list-detail\">\n <div class=\"list-pane\">\n <header>\n <h2>Inbox</h2>\n </header>\n <ul class=\"list\">\n <li><a href=\"/inbox/brunch\">Brunch this weekend?</a></li>\n <li><a href=\"/inbox/design-review\">Design review</a></li>\n </ul>\n </div>\n <div class=\"detail-pane\">\n <header>\n <button aria-label=\"Back\"><span class=\"material-symbols\" aria-hidden=\"true\">arrow_back</span></button>\n <h2>Brunch this weekend?</h2>\n </header>\n <section>\n <p>Detail content.</p>\n </section>\n </div>\n</div>\n```\n\n#### Also documented\n\nThe Panes section of the [API reference](https://www.expressivecss.com/llm.md) also covers List-detail, Compact, Supporting pane, Equal, Three-pane, Appearance, Pane anatomy, Tokens.\n\n#### Rules\n\n- `pane-is-not-main`: A document has one <main>, and a pane is a region inside it, not another one. Use <section>.\n"
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
"file": "pie-chart.md",
|
|
180
|
+
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Pie chart\nParts of a whole drawn as slices from a data table, as a pie or a donut with its total.\n\nComponent ID: `pie-chart`\n\n[Component documentation](https://www.expressivecss.com/pie-chart.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/pie-chart.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.13.1)\n\nContract: ExpressiveCSS 0.13.1\n\nContract SHA-256: `18ec4146d300280bbcb74eb3b23f16d1015a0a3db86a8c59fc8348dbdf9d96c2`\n\n#### Selection and adaptation\n\nRuntime ownership: `auto-init`. No dedicated entry in the reviewed Google component inventory.\n\nExample: Revenue split across three plans, with the total in the middle, is a donut pie chart. Revenue from twelve sources is a bar chart.\n\n#### Material mapping\n\nRelationship: none. Web extension; no dedicated entry was observed in the reviewed current M3 component inventory.\n\nUpstream: web-extension-inventory-reviewed (2026-10-07); [evidence](https://m3.material.io/components).\n\nRequirements and boundaries: design reference web-extensions-review. The inventory page renders client-side, so this review relies on the 2026-09-13 inventory review. Full visual parity and spoken output unverified.\n\nSupport (2026-10-07, `llm.md#pie-chart`): Slices drawn clockwise from the top from a figure's data table, with a legend of each row's cell and share, donut, values and sparkline modifiers, a <tfoot> total in the donut hole, further columns in the tooltip, and a pointer and keyboard tooltip that highlights the slice.\n\nWeb adaptation: The <table> stays in the page as the text alternative and the no-JavaScript fallback; the slices, values, total and legend are aria-hidden and the plot is one Tab stop with a polite live tooltip.\n\nKnown boundary: Drawing, the hidden table, slice geometry and colors, the legend and shares, the donut hole and total, values, zero and missing values, RTL, the tooltip, pointer angle hit testing, keyboard movement and destroy are checked in a browser; screen reader output, forced colors and enlarged text remain unverified. Zero and negative values have no slice; there are no slice labels outside the pie.\n\nFull parity remains unassessed. [Capability evidence](../references/capability-roadmap.md#pie-chart).\n\n#### Contract\n\nA `<figure class=\"pie-chart\">` takes the same `<figcaption>` and `<table>` as a column chart. `AutoInit()` draws the first value column as slices of the total, clockwise from the top in table order, with a legend of every row, its cell and its share, and a tooltip that follows the pointer round the pie. The table stays in the page, visually hidden, as the chart's text alternative; without JavaScript it shows instead of the chart.\n\n#### Syntax\n\n```html\n<figure class=\"pie-chart donut\">\n <figcaption>Lead sources, last six months</figcaption>\n <table>\n <thead>\n <tr><th>Source</th><th>Leads</th></tr>\n </thead>\n <tbody>\n <tr><th>Referrals</th><td>558</td></tr>\n <tr><th>Organic search</th><td>434</td></tr>\n <tr><th>Paid campaigns</th><td>248</td></tr>\n </tbody>\n <tfoot>\n <tr><th>Total leads</th><td>1,240</td></tr>\n </tfoot>\n </table>\n</figure>\n```\n\n#### Also documented\n\nThe Pie chart section of the [API reference](https://www.expressivecss.com/llm.md) also covers Tokens.\n\n#### Rules\n\n- `pie-chart-is-a-figure-with-a-table`: Write .pie-chart as a <figure> holding a <table> of the data. The drawn chart is hidden from assistive technology, so the table is all a screen reader reads, and it is what shows without JavaScript.\n- `pie-chart-has-a-caption`: Name a .pie-chart with a <figcaption>. It names the focusable plot as well as the figure; hide it with .visually-hidden when nearby text already names the chart.\n"
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
"file": "popover.md",
|
|
184
|
+
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Popover\nA panel that opens from a button and stays until dismissed, on the native popover attribute.\n\nComponent ID: `popover`\n\n[Component documentation](https://www.expressivecss.com/popover.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/popover.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.13.1)\n\nContract: ExpressiveCSS 0.13.1\n\nContract SHA-256: `18ec4146d300280bbcb74eb3b23f16d1015a0a3db86a8c59fc8348dbdf9d96c2`\n\n#### Selection and adaptation\n\nRuntime ownership: `native`. No dedicated entry in the reviewed Google component inventory.\n\nExample: A label for an icon button that shows on hover is a tooltip. A popover would make the user press the button just to learn what it does.\n\n#### Material mapping\n\nRelationship: none. Web extension; no dedicated entry was observed in the reviewed current M3 component inventory.\n\nUpstream: web-extension-inventory-reviewed (2026-10-06); [evidence](https://m3.material.io/components).\n\nRequirements and boundaries: design reference web-extensions-review. The inventory page renders client-side, so this review relies on the 2026-09-13 inventory review. Full visual parity and spoken output unverified.\n\nSupport (2026-10-06, `llm.md#popover`): Native popover panels anchored below, above or beside their button with automatic flipping, rich-tooltip styling, an actions row and frosted glass.\n\nWeb adaptation: The browser owns opening, light dismiss, Escape, focus return and the button's expanded state; CSS anchor positioning places the panel.\n\nKnown boundary: Anchoring and flipping in LTR and RTL, light dismiss, Escape and focus return are checked in a browser, and the expanded state in Chromium; spoken output and engines without anchor positioning remain unverified.\n\nFull parity remains unassessed. [Capability evidence](../references/capability-roadmap.md#popover).\n\n#### Contract\n\nA `.popover` is a `[popover]` element, and the button that opens it names it with `popovertarget` (or `commandfor` with `command=\"toggle-popover\"`). The browser owns the behavior: pressing the button toggles the panel, Escape or a click outside closes it, focus returns to the button, and the button reports the panel as expanded. CSS anchor positioning places the panel 4px below the button with their start edges aligned, and flips it to the other side when it would not fit. CSS only; there is no JavaScript.\n\nIt looks like a rich tooltip but behaves as a disclosure: it opens on press, stays open while the user reads or acts, and can hold links and buttons. A small one behind an info icon button is a toggletip. Add `top` to open above, or `start` or `end` to open beside the button, centered on it.\n\n#### Syntax\n\n```html\n<button class=\"icon-button\" type=\"button\" popovertarget=\"storage-help\" aria-label=\"About storage\">\n <span class=\"material-symbols\" aria-hidden=\"true\">info</span>\n</button>\n<div id=\"storage-help\" class=\"popover\" popover>\n <h3>Storage</h3>\n <p>Files in the trash count toward your quota until you empty it.</p>\n <div class=\"popover-actions\">\n <button type=\"button\" class=\"text\">Empty trash</button>\n </div>\n</div>\n```\n\n#### Also documented\n\nThe Popover section of the [API reference](https://www.expressivecss.com/llm.md) also covers Accessibility, Tokens.\n\n#### Rules\n\n- `popover-is-native`: Give a .popover the popover attribute. Without it the panel is never in the top layer, never light-dismisses and its button reports no expanded state.\n- `popover-target-exists`: popovertarget must name the id of a [popover] element in the page. A dangling id leaves a button that does nothing.\n- `popover-is-not-a-tooltip`: A popover opens on press and can hold links and buttons, which a tooltip never can. Leave the role off; the browser relates the panel to its button.\n"
|
|
109
185
|
},
|
|
110
186
|
{
|
|
111
187
|
"file": "progress.md",
|
|
112
|
-
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Progress indicators\nActivity and progress. The HTML is the indicator.\n\nComponent ID: `progress`\n\n[Component documentation](https://www.expressivecss.com/preloader.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/preloader.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.
|
|
188
|
+
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Progress indicators\nActivity and progress. The HTML is the indicator.\n\nComponent ID: `progress`\n\n[Component documentation](https://www.expressivecss.com/preloader.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/preloader.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.13.1)\n\nContract: ExpressiveCSS 0.13.1\n\nContract SHA-256: `18ec4146d300280bbcb74eb3b23f16d1015a0a3db86a8c59fc8348dbdf9d96c2`\n\n#### Selection and adaptation\n\nRuntime ownership: `css-only`. [Google guidance](https://m3.material.io/components/progress-indicators/overview)\n\nExample: A brief unknown refresh fits a loading indicator. An upload needs accurate determinate progress, even if short. Keep circular progress when an unknown process becomes determinate or runs inside a button; indeterminate linear bars remain supported. Never invent a percentage.\n\n#### Material mapping\n\nRelationship: component. Google replaces most short indeterminate circular waits with a loading indicator. Keep circular progress in buttons and for processes that become determinate; keep determinate and indeterminate linear progress. Deprecated baseline token sets do not deprecate these families.\n\nUpstream: requirements-prose-reviewed (2026-09-13); [evidence](https://m3.material.io/components/progress-indicators/overview).\n\nRequirements and boundaries: design reference feedback-material-review. Full visual parity and spoken output unverified.\n\n[Specification link](https://m3.material.io/components/progress-indicators/specs); full specs unreviewed.\n\n[Google guidelines](https://m3.material.io/components/progress-indicators/guidelines).\n\nSupport (2026-09-13, `llm.md#progress-indicators`): Linear and circular progress, determinate and indeterminate.\n\nWeb adaptation: Prefer native progress where applicable; supply accessible state for custom hosts.\n\nKnown boundary: Forced-colors rendering is checked in browser emulation; Windows high-contrast and spoken output still require manual review.\n\nFull parity remains unassessed. [Capability evidence](../references/capability-roadmap.md#progress).\n\n#### Contract\n\nNative and custom progress use system colors in forced-colors mode, outline empty linear tracks, and preserve circular value segments. Reduced motion retains static feedback, including the legacy preloader. Browser emulation checks do not replace Windows high-contrast or screen-reader review.\n\nActivity and progress indicators for content that takes time to load.\n\nIf content will take a while to load, give the user feedback. Expressive ships linear progress bars and circular spinners. Both are CSS-only — there is no JavaScript plugin.\n\n#### Syntax\n\n```html\n<progress class=\"progress\" value=\"70\" max=\"100\"></progress>\n\n<div class=\"progress\" role=\"progressbar\" aria-valuenow=\"70\" aria-valuemin=\"0\" aria-valuemax=\"100\">\n <div class=\"determinate\" style=\"width: 70%\"></div>\n</div>\n\n<div class=\"progress\" role=\"progressbar\" aria-valuenow=\"70\" aria-valuemin=\"0\" aria-valuemax=\"100\" style=\"--md-comp-progress-value: 70%\"></div>\n```\n\n#### Also documented\n\nThe Progress indicators section of the [API reference](https://www.expressivecss.com/llm.md) also covers Linear, Circular.\n\n#### Rules\n\n- `div-progress-reports-progress`: <progress> reports itself. A <div class=\"progress\"> is a bar drawn with CSS and reports nothing - it needs role=\"progressbar\", plus aria-valuenow/min/max when it is determinate.\n- `determinate-progress-reports-its-value`: role=\"progressbar\" with no aria-valuenow is an *indeterminate* bar. A determinate one draws a width the user can see and must report the same number: aria-valuenow, plus valuemin/valuemax when they are not 0 and 100.\n"
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
"file": "questionnaire.md",
|
|
192
|
+
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Questionnaire\nA form that asks one question at a time, with single and multiple choice, free text and skippable questions.\n\nComponent ID: `questionnaire`\n\n[Component documentation](https://www.expressivecss.com/questionnaire.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/questionnaire.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.13.1)\n\nContract: ExpressiveCSS 0.13.1\n\nContract SHA-256: `18ec4146d300280bbcb74eb3b23f16d1015a0a3db86a8c59fc8348dbdf9d96c2`\n\n#### Selection and adaptation\n\nRuntime ownership: `auto-init`. No dedicated entry in the reviewed Google component inventory.\n\nExample: A five-question product survey with an optional comment at the end is a questionnaire. A shipping, payment and review checkout is a stepper.\n\n#### Material mapping\n\nRelationship: none. Web extension; no dedicated entry was observed in the reviewed current M3 component inventory.\n\nUpstream: web-extension-inventory-reviewed (2026-10-08); [evidence](https://m3.material.io/components).\n\nRequirements and boundaries: design reference web-extensions-review. The inventory page renders client-side, so this review relies on the 2026-09-13 inventory review. Full visual parity and spoken output unverified.\n\nSupport (2026-10-08, `llm.md#questionnaire`): A native form of fieldsets shown one at a time, with single choice, multiple choice and text answers, choice descriptions, required and data-required questions, Skip for optional ones, authored or generated errors, a progress row, Enter to advance, disabled questions for branching and normal form submission.\n\nWeb adaptation: Each question is a native fieldset named by its legend; the browser's constraint validation checks answers, hidden questions use hidden, and without JavaScript the form shows every question and still submits.\n\nKnown boundary: Showing one question, required radios, data-required checkboxes that ignore disabled ones, generated and authored errors, aria-invalid and aria-describedby, Enter, Skip clearing an answer, Back focus, a disabled question left out, submit and requestSubmit() opening the first unanswered question, FormData and destroy are checked in a browser; screen reader output, forced colors and enlarged text remain unverified. There are no answer keyboard shortcuts, no summary step and no saved progress; the page owns persistence and branching.\n\nFull parity remains unassessed. [Capability evidence](../references/capability-roadmap.md#questionnaire).\n\n#### Contract\n\nA `<form class=\"questionnaire\">` holds one `<fieldset>` per question and a `<footer>` of buttons. The `<legend>` is the prompt and a `<p>` under it describes the question. Answers are ordinary radio buttons, checkboxes or text fields; a `<small>` inside a choice's label describes that choice, and choices draw as outlined rows that fill with `secondary-container` when selected. A `<p class=\"questionnaire-error\">` in a question holds its error text. The footer buttons are marked by `value`: `previous`, `skip` and `next`, each `type=\"button\"`, plus a submit button. `AutoInit()` starts every `form.questionnaire`.\n\nThe component adds a progress row (\"Question 1 of 3\" and a `<progress>` named by that text), hides every question but the current one with `hidden`, and shows only the buttons that apply: Back after the first question, Skip on an optional question, Next before the last one and Submit on the last. A question is required when one of its controls is `required`; `data-required` on a fieldset of checkboxes requires at least one. Next checks the shown question with the browser's validation. When it fails, the fieldset gets `invalid`, its controls get `aria-invalid=\"true\"`, the error shows and joins the description in the fieldset's `aria-describedby`, and focus moves to the control to fix. Answering clears it. A question without its own error shows the browser's validation message, or `requiredMessage`. Skip clears the shown answer, firing `change`, and moves on. Enter in a field moves to the next question, and on the last one submits. Submitting, with the button, Enter or `requestSubmit()`, checks every question and opens the first that still needs an answer. Only enabled checkboxes count toward `data-required`, since a disabled one is not sent. After Next, Back or Skip, focus moves to the selected answer or the first control.\n\nA disabled fieldset is skipped and left out of the count, so a page branches by enabling or disabling questions from earlier answers. The form submits normally: read the answers with `FormData`, using `getAll()` for checkboxes. Without JavaScript every question shows at once, the navigation buttons stay hidden and the submit button sends the form; a `data-required` group is not checked then. Name the form with `aria-label` or `aria-labelledby`.\n\n#### Syntax\n\n```html\n<form class=\"questionnaire\" aria-label=\"Product survey\">\n <fieldset>\n <legend>What should we prototype next?</legend>\n <p>Pick the direction you want to explore first.</p>\n <label><input type=\"radio\" name=\"direction\" value=\"search\" required>Search<small>Find any page in two keystrokes</small></label>\n <label><input type=\"radio\" name=\"direction\" value=\"sharing\" required>Sharing</label>\n <p class=\"questionnaire-error\">Choose an answer to continue.</p>\n </fieldset>\n <fieldset data-required>\n <legend>Where do you use the app?</legend>\n <label><input type=\"checkbox\" name=\"platforms\" value=\"web\">Web</label>\n <label><input type=\"checkbox\" name=\"platforms\" value=\"mobile\">Mobile</label>\n <p class=\"questionnaire-error\">Choose at least one platform.</p>\n </fieldset>\n <fieldset>\n <legend>Anything else we should know?</legend>\n <div class=\"field\">\n <textarea id=\"notes\" name=\"notes\" class=\"expressive-textarea\" placeholder=\" \"></textarea>\n <label for=\"notes\">Notes</label>\n </div>\n </fieldset>\n <footer>\n <button type=\"button\" class=\"text\" value=\"previous\">Back</button>\n <button type=\"button\" class=\"text\" value=\"skip\">Skip</button>\n <button type=\"button\" value=\"next\">Next</button>\n <button type=\"submit\">Submit</button>\n </footer>\n</form>\n```\n\n#### Options\n\n| Name | Type | Default | Description |\n| --- | --- | --- | --- |\n| `progressLabel` | String | `'Question {current} of {total}'` | Text of the progress row. `{current}` and `{total}` become the question number and count. `data-progress-label` on the form sets it. |\n| `requiredMessage` | String | `'Choose an answer to continue.'` | Error for a question with no `.questionnaire-error` when the browser has no message, as for a `data-required` group. |\n\n#### Methods\n\n- `.next()`: Checks the shown question and moves to the next one if it is answered.\n- `.previous()`: Moves to the previous question.\n- `.skip()`: Clears the answer to the shown question and moves to the next one.\n- `.goTo(index)`: Shows the question at `index` without checking the current one, for example to resume a saved questionnaire.\n- `.destroy()`: Shows every question and removes the progress row, ids, states and listeners the component added.\n\n#### Also documented\n\nThe Questionnaire section of the [API reference](https://www.expressivecss.com/llm.md) also covers Properties, Tokens.\n\n#### Rules\n\n- `questionnaire-is-a-form`: Write the questionnaire as <form class=\"questionnaire\">. The form sends the answers, runs the browser's validation and still works without JavaScript.\n- `questionnaire-is-named`: Name the questionnaire with aria-label or aria-labelledby so it is announced as a form.\n- `questionnaire-navigation-does-not-submit`: Give the Back, Skip and Next buttons type=\"button\". A button without a type submits the form, so pressing one before the component starts would send it early.\n"
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
"file": "radar-chart.md",
|
|
196
|
+
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Radar chart\nSeveral measures on one scale drawn as shapes on spokes from a data table, one shape per series.\n\nComponent ID: `radar-chart`\n\n[Component documentation](https://www.expressivecss.com/radar-chart.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/radar-chart.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.13.1)\n\nContract: ExpressiveCSS 0.13.1\n\nContract SHA-256: `18ec4146d300280bbcb74eb3b23f16d1015a0a3db86a8c59fc8348dbdf9d96c2`\n\n#### Selection and adaptation\n\nRuntime ownership: `auto-init`. No dedicated entry in the reviewed Google component inventory.\n\nExample: Two quarters of client satisfaction scored out of 100 on five measures is a radar chart with area. Monthly revenue for a year is a line chart.\n\n#### Material mapping\n\nRelationship: none. Web extension; no dedicated entry was observed in the reviewed current M3 component inventory.\n\nUpstream: web-extension-inventory-reviewed (2026-10-07); [evidence](https://m3.material.io/components).\n\nRequirements and boundaries: design reference web-extensions-review. The inventory page renders client-side, so this review relies on the 2026-09-13 inventory review. Full visual parity and spoken output unverified.\n\nSupport (2026-10-07, `llm.md#radar-chart`): Spokes drawn clockwise from the top from a figure's data table, one shape per series from zero in the middle to the scale's top at the rim, row labels past each spoke, a legend for several series, area, points, dashed and sparkline modifiers, data-min and data-max, and a pointer and keyboard tooltip with a cursor spoke.\n\nWeb adaptation: The <table> stays in the page as the text alternative and the no-JavaScript fallback; the grid, shapes, labels and legend are aria-hidden and the plot is one Tab stop with a polite live tooltip.\n\nKnown boundary: Drawing, the hidden table, the grid, shape geometry and colors, the scale with data-max and clamping, gaps, labels, the legend, area and points, the sparkline, RTL, the tooltip and cursor spoke, nearest-spoke pointer hit testing, keyboard movement and destroy are checked in a browser; screen reader output, forced colors and enlarged text remain unverified. A gap joins the spokes either side of it; there are no ring value labels.\n\nFull parity remains unassessed. [Capability evidence](../references/capability-roadmap.md#radar-chart).\n\n#### Contract\n\nA `<figure class=\"radar-chart\">` takes the same `<figcaption>` and `<table>` as a line chart. `AutoInit()` draws each row as a spoke, clockwise from the top in table order, with its label past the tip, and each value column as a shape joining its values, from zero in the middle to the scale's top at the rim. A legend names the series when there is more than one, and a tooltip follows the pointer round the chart. The table stays in the page, visually hidden, as the chart's text alternative; without JavaScript it shows instead of the chart.\n\n#### Syntax\n\n```html\n<figure class=\"radar-chart area\" data-max=\"100\">\n <figcaption>Client satisfaction</figcaption>\n <table>\n <thead>\n <tr><th>Measure</th><th>This quarter</th><th class=\"dashed\">Last quarter</th></tr>\n </thead>\n <tbody>\n <tr><th>Communication</th><td>92</td><td>80</td></tr>\n <tr><th>Quality</th><td>85</td><td>76</td></tr>\n <tr><th>Timeliness</th><td>78</td><td>82</td></tr>\n <tr><th>Value</th><td>88</td><td>74</td></tr>\n <tr><th>Support</th><td>81</td><td>70</td></tr>\n </tbody>\n </table>\n</figure>\n```\n\n#### Also documented\n\nThe Radar chart section of the [API reference](https://www.expressivecss.com/llm.md) also covers Tokens.\n\n#### Rules\n\n- `radar-chart-is-a-figure-with-a-table`: Write .radar-chart as a <figure> holding a <table> of the data. The drawn chart is hidden from assistive technology, so the table is all a screen reader reads, and it is what shows without JavaScript.\n- `radar-chart-has-a-caption`: Name a .radar-chart with a <figcaption>. It names the focusable plot as well as the figure; hide it with .visually-hidden when nearby text already names the chart.\n"
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
"file": "radial-chart.md",
|
|
200
|
+
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Radial chart\nProgress toward a goal drawn as rings from a data table, as concentric rings or a half-circle gauge.\n\nComponent ID: `radial-chart`\n\n[Component documentation](https://www.expressivecss.com/radial-chart.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/radial-chart.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.13.1)\n\nContract: ExpressiveCSS 0.13.1\n\nContract SHA-256: `18ec4146d300280bbcb74eb3b23f16d1015a0a3db86a8c59fc8348dbdf9d96c2`\n\n#### Selection and adaptation\n\nRuntime ownership: `auto-init`. No dedicated entry in the reviewed Google component inventory.\n\nExample: Four plan targets as a share of their goals, or seats filled out of 25 as a gauge, are a radial chart. Revenue split across plans is a pie chart.\n\n#### Material mapping\n\nRelationship: none. Web extension; no dedicated entry was observed in the reviewed current M3 component inventory.\n\nUpstream: web-extension-inventory-reviewed (2026-10-07); [evidence](https://m3.material.io/components).\n\nRequirements and boundaries: design reference web-extensions-review. The inventory page renders client-side, so this review relies on the 2026-09-13 inventory review. Full visual parity and spoken output unverified.\n\nSupport (2026-10-07, `llm.md#radial-chart`): Rings drawn clockwise from the top from a figure's data table as a share of data-max, with tracks, a legend of each row's cell for more than one row, gauge and sparkline modifiers, a <tfoot> total in the middle, further columns in the tooltip, and a pointer and keyboard tooltip that highlights the ring.\n\nWeb adaptation: The <table> stays in the page as the text alternative and the no-JavaScript fallback; the rings, total and legend are aria-hidden and the plot is one Tab stop with a polite live tooltip.\n\nKnown boundary: Drawing, the hidden table, ring geometry and colors, tracks, the legend, the <tfoot> total, the gauge's half circles, values past the maximum and missing values, thinning crowded rings, RTL, the tooltip, pointer distance hit testing, keyboard movement and destroy are checked in a browser; screen reader output, forced colors and enlarged text remain unverified. Zero and negative values leave only the track; there are no labels on the rings.\n\nFull parity remains unassessed. [Capability evidence](../references/capability-roadmap.md#radial-chart).\n\n#### Contract\n\nA `<figure class=\"radial-chart\">` takes the same `<figcaption>` and `<table>` as a column chart. `AutoInit()` draws each row of the first value column as a ring, outermost first, filled clockwise from the top as a share of `data-max` (100 by default), with a legend of the rows and their cells when there is more than one, and a tooltip that follows the pointer across the rings. The table stays in the page, visually hidden, as the chart's text alternative; without JavaScript it shows instead of the chart.\n\n#### Syntax\n\n```html\n<figure class=\"radial-chart gauge\" data-max=\"25\">\n <figcaption>Team capacity</figcaption>\n <table>\n <thead>\n <tr><th>Seats</th><th>Count</th></tr>\n </thead>\n <tbody>\n <tr><th>Filled</th><td>18</td></tr>\n <tr><th>Offered</th><td>21</td></tr>\n </tbody>\n <tfoot>\n <tr><th>Total seats</th><td>25</td></tr>\n </tfoot>\n </table>\n</figure>\n```\n\n#### Also documented\n\nThe Radial chart section of the [API reference](https://www.expressivecss.com/llm.md) also covers Tokens.\n\n#### Rules\n\n- `radial-chart-is-a-figure-with-a-table`: Write .radial-chart as a <figure> holding a <table> of the data. The drawn chart is hidden from assistive technology, so the table is all a screen reader reads, and it is what shows without JavaScript.\n- `radial-chart-has-a-caption`: Name a .radial-chart with a <figcaption>. It names the focusable plot as well as the figure; hide it with .visually-hidden when nearby text already names the chart.\n"
|
|
113
201
|
},
|
|
114
202
|
{
|
|
115
203
|
"file": "radio-buttons.md",
|
|
116
|
-
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Radio buttons\nMaterial Design 3 radios, from the HTML.\n\nComponent ID: `radio-buttons`\n\n[Component documentation](https://www.expressivecss.com/radio-buttons.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/radio-buttons.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.
|
|
204
|
+
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Radio buttons\nMaterial Design 3 radios, from the HTML.\n\nComponent ID: `radio-buttons`\n\n[Component documentation](https://www.expressivecss.com/radio-buttons.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/radio-buttons.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.13.1)\n\nContract: ExpressiveCSS 0.13.1\n\nContract SHA-256: `18ec4146d300280bbcb74eb3b23f16d1015a0a3db86a8c59fc8348dbdf9d96c2`\n\n#### Selection and adaptation\n\nRuntime ownership: `css-only`. [Google guidance](https://m3.material.io/components/radio-button/overview)\n\n#### Material mapping\n\nRelationship: component.\n\nUpstream: requirements-prose-reviewed (2026-09-13); [evidence](https://m3.material.io/components/radio-button/overview).\n\nRequirements and boundaries: design reference inputs-material-review. Full visual parity and spoken output unverified.\n\n[Specification link](https://m3.material.io/components/radio-button/specs); full specs unreviewed.\n\n[Google guidelines](https://m3.material.io/components/radio-button/guidelines).\n\nSupport (2026-09-13, `llm.md#radio-buttons`): Native radio groups and disabled controls.\n\nWeb adaptation: Shared name provides exclusivity; fieldset/legend names the group.\n\nKnown boundary: Native group selection is checked; initially-empty and disabled-option paths remain unmeasured.\n\nFull parity remains unassessed. [Capability evidence](../references/capability-roadmap.md#radio-buttons).\n\n#### Contract\n\nA `<label>` wrapping `<input type=\"radio\">` is the control. The label text is a sibling of the input — no extra class, no required `<span>`. They are CSS only. There is no JavaScript component and nothing to AutoInit.\n\nA radio only means anything as one of a set, so the set is a `<fieldset>` and the `<legend>` names the question the radios answer. Without it the options are announced one by one with nothing saying what is being chosen.\n\nTokens follow the [M3 radio spec](https://m3.material.io/components/radio-button/specs). The icon is a 20dp ring with a 2dp stroke. Selected is `primary` with a 10dp inner disc (the M3 gap). The state layer is 40dp at 8% hover and 10% focus. The touch target is 48dp. The label is `body-large` / `on-surface`. Disabled is `on-surface` at 38%.\n\n#### Syntax\n\n```html\n<fieldset>\n <legend>Colour</legend>\n <label>\n <input name=\"group1\" type=\"radio\" checked>\n Red\n </label>\n <label>\n <input name=\"group1\" type=\"radio\">\n Yellow\n </label>\n <label>\n <input name=\"group1\" type=\"radio\" disabled>\n Brown\n </label>\n</fieldset>\n```\n\n#### Also documented\n\nThe Radio Buttons section of the [API reference](https://www.expressivecss.com/llm.md) also covers In a row.\n\n#### Rules\n\n- `radio-in-fieldset`: A radio only means something as one of a group, and the group needs a <fieldset> with a <legend> to name the question being answered.\n- `radio-labelled`: A radio must be inside its <label> or carry an id a label points at.\n- `fieldset-has-legend`: A fieldset without a legend groups controls visually but names nothing.\n"
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
"file": "rating.md",
|
|
208
|
+
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Rating\nStars for choosing or showing a score, on native radio buttons.\n\nComponent ID: `rating`\n\n[Component documentation](https://www.expressivecss.com/rating.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/rating.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.13.1)\n\nContract: ExpressiveCSS 0.13.1\n\nContract SHA-256: `18ec4146d300280bbcb74eb3b23f16d1015a0a3db86a8c59fc8348dbdf9d96c2`\n\n#### Selection and adaptation\n\nRuntime ownership: `css-only`. No dedicated entry in the reviewed Google component inventory.\n\nExample: Choosing a volume from 0 to 100 is a slider. Stars suit a short fixed scale where each step has a meaning.\n\n#### Material mapping\n\nRelationship: none. Web extension; no dedicated entry was observed in the reviewed current M3 component inventory.\n\nUpstream: web-extension-inventory-reviewed (2026-10-06); [evidence](https://m3.material.io/components).\n\nRequirements and boundaries: design reference web-extensions-review. The inventory page renders client-side, so this review relies on the 2026-09-13 inventory review. Full visual parity and spoken output unverified.\n\nSupport (2026-10-06, `llm.md#rating`): A fieldset of radios drawn as stars with fill, hover preview and focus ring, and a read-only display that fills to a fractional value in LTR and RTL.\n\nWeb adaptation: Native radios keep keyboard, form and naming behavior; the display is a named role=\"img\".\n\nKnown boundary: Star fill, hover preview, native arrow keys, form value, the display fill direction and names are checked in a browser; the focus ring after arrow keys is not drawn in Playwright WebKit, and spoken output remains unverified.\n\nFull parity remains unassessed. [Capability evidence](../references/capability-roadmap.md#rating).\n\n#### Contract\n\nFor input, a `<fieldset class=\"rating\">` holds a `<legend>` and one radio per star, each in a `<label>` whose visually hidden text (\"3 stars\") comes before the radio. The radio is drawn as the star, so Tab, the arrow keys, `required` and form submission stay native. A star fills when it or a later star is checked, and hovering previews a choice. Put the text before the radio: a span after it is the older `input + span` radio, which hides the input. CSS only; there is no JavaScript.\n\n#### Syntax\n\n```html\n<fieldset class=\"rating\">\n <legend>Rate this recipe</legend>\n <label><span class=\"visually-hidden\">1 star</span><input type=\"radio\" name=\"score\" value=\"1\"></label>\n <label><span class=\"visually-hidden\">2 stars</span><input type=\"radio\" name=\"score\" value=\"2\"></label>\n <label><span class=\"visually-hidden\">3 stars</span><input type=\"radio\" name=\"score\" value=\"3\" checked></label>\n <label><span class=\"visually-hidden\">4 stars</span><input type=\"radio\" name=\"score\" value=\"4\"></label>\n <label><span class=\"visually-hidden\">5 stars</span><input type=\"radio\" name=\"score\" value=\"5\"></label>\n</fieldset>\n```\n\n#### Also documented\n\nThe Rating section of the [API reference](https://www.expressivecss.com/llm.md) also covers Display, Tokens.\n\n#### Rules\n\n- `rating-is-a-fieldset-or-an-image`: A .rating is either a <fieldset> of radios, for input, or an element with role=\"img\" and a name, for display. Anything else draws stars that say nothing.\n- `rating-display-is-named`: Name a rating display with the value, such as aria-label=\"Rated 4.5 out of 5\".\n- `rating-label-text-precedes-the-radio`: Put a star's visually hidden text before its radio. A span after the radio is the older input + span pattern, which hides the radio and with it the star.\n"
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
"file": "rich-text-editor.md",
|
|
212
|
+
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Rich text editor\nA text field with a formatting toolbar for writing headings, lists and styled text, built on Tiptap.\n\nComponent ID: `rich-text-editor`\n\n[Component documentation](https://www.expressivecss.com/rich-text-editor.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/rich-text-editor.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.13.1)\n\nContract: ExpressiveCSS 0.13.1\n\nContract SHA-256: `18ec4146d300280bbcb74eb3b23f16d1015a0a3db86a8c59fc8348dbdf9d96c2`\n\n#### Selection and adaptation\n\nRuntime ownership: `manual`. No dedicated entry in the reviewed Google component inventory.\n\nExample: A blog post body with headings and lists needs a rich text editor; a one-line title needs a text field.\n\n#### Material mapping\n\nRelationship: none. Web extension; no dedicated entry was observed in the reviewed current M3 component inventory.\n\nUpstream: web-extension-inventory-reviewed (2026-10-08); [evidence](https://m3.material.io/components).\n\nRequirements and boundaries: design reference web-extensions-review. The inventory page renders client-side, so this review relies on the 2026-09-13 inventory review. Full visual parity and spoken output unverified.\n\nSupport (2026-10-08, `llm.md#rich-text-editor`): A Tiptap editor mounted from a textarea, toolbar buttons that run Tiptap commands with JSON arguments, pressed and disabled button state, form submission through the textarea, label naming and read-only textareas.\n\nWeb adaptation: The page supplies Tiptap's Editor class and extensions; the component mounts it, names the contenteditable textbox from the textarea and keeps the textarea in sync. The toolbar is the docked toolbar.\n\nKnown boundary: Formatting a selection, pressed and disabled toolbar state, textarea sync with input events, label naming and focus, a disabled textarea and destroy are checked in a browser; spoken output, IME composition, paste from other applications and touch selection remain unverified.\n\nFull parity remains unassessed. [Capability evidence](../references/capability-roadmap.md#rich-text-editor).\n\n#### Contract\n\nA `.rich-text-editor` holds a docked `.toolbar` and a `<textarea>` whose value is the starting HTML. The component hides the textarea, mounts the editor in a `.rich-text-editor-content` after it, and writes the editor's HTML back on every change, so the textarea submits with its form and fires `input`. An empty editor submits an empty value. Without JavaScript the textarea stays visible and the toolbar is hidden.\n\nExpressiveCSS does not bundle Tiptap. Install `@tiptap/core`, `@tiptap/pm` and `@tiptap/starter-kit`, and pass the `Editor` class and extensions to `RichTextEditor.init()`. `AutoInit()` does not start the editor.\n\nA button's `data-command` names a Tiptap command and `data-attributes` holds its argument as JSON. A click runs the command and returns focus to the editor. A button is disabled while its command cannot run. Buttons for `toggle` commands set `aria-pressed` and `.active` while the selection has that format: `toggleBold` while it is bold, `toggleHeading` with `{\"level\":2}` while it is in a second-level heading.\n\n#### Syntax\n\n```html\n<label for=\"body\">Release notes</label>\n<div class=\"rich-text-editor\">\n <div class=\"toolbar docked\">\n <button type=\"button\" class=\"circle\" data-command=\"toggleBold\" aria-label=\"Bold\">\n <span class=\"material-symbols\" aria-hidden=\"true\">format_bold</span>\n </button>\n <button type=\"button\" class=\"circle\" data-command=\"toggleHeading\"\n data-attributes='{\"level\":2}' aria-label=\"Heading\">\n <span class=\"material-symbols\" aria-hidden=\"true\">format_h2</span>\n </button>\n <span class=\"max\"></span>\n <button type=\"button\" class=\"circle\" data-command=\"undo\" aria-label=\"Undo\">\n <span class=\"material-symbols\" aria-hidden=\"true\">undo</span>\n </button>\n </div>\n <textarea id=\"body\" name=\"body\"><p>Starting content</p></textarea>\n</div>\n```\n\n#### Options\n\n| Name | Type | Default | Description |\n| --- | --- | --- | --- |\n| `Editor` | Class | `null` | Tiptap's `Editor` class. Required. |\n| `extensions` | Array | `[]` | Tiptap extensions, usually `[StarterKit]`. |\n\n#### Methods\n\n- `.editor`: The Tiptap editor, for anything the toolbar does not cover.\n- `.destroy()`: Destroys the editor, removes its element and the button state, and shows the textarea again.\n\n#### Also documented\n\nThe Rich text editor section of the [API reference](https://www.expressivecss.com/llm.md) also covers Tokens.\n\n#### Rules\n\n- `rich-text-editor-textarea-is-named`: Name the textarea with a <label for>, aria-label or aria-labelledby. The editor copies that name to its editable area, which has none of its own. Its content is the starting HTML, not a name.\n"
|
|
117
213
|
},
|
|
118
214
|
{
|
|
119
215
|
"file": "scrollspy.md",
|
|
120
|
-
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Scrollspy\nHighlight the table of contents as the page scrolls.\n\nComponent ID: `scrollspy`\n\n[Component documentation](https://www.expressivecss.com/scrollspy.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/scrollspy.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.
|
|
216
|
+
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Scrollspy\nHighlight the table of contents as the page scrolls.\n\nComponent ID: `scrollspy`\n\n[Component documentation](https://www.expressivecss.com/scrollspy.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/scrollspy.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.13.1)\n\nContract: ExpressiveCSS 0.13.1\n\nContract SHA-256: `18ec4146d300280bbcb74eb3b23f16d1015a0a3db86a8c59fc8348dbdf9d96c2`\n\n#### Selection and adaptation\n\nRuntime ownership: `auto-init`. No dedicated entry in the reviewed Google component inventory.\n\nExample: A settings screen with mutually exclusive panels needs tabs. Scrollspy only tracks sections already present in the scrolling document.\n\n#### Material mapping\n\nRelationship: none. Web extension; no dedicated entry was observed in the reviewed current M3 component inventory.\n\nUpstream: web-extension-inventory-reviewed (2026-09-13); [evidence](https://m3.material.io/components).\n\nRequirements and boundaries: design reference web-extensions-review. Full visual parity and spoken output unverified.\n\nSupport (2026-09-17, `llm.md#scrollspy`): Section tracking with per-section offsets and independent tables of contents grouped by getActiveElement callback identity.\n\nWeb adaptation: Web document navigation helper.\n\nKnown boundary: Independent TOCs must reuse one getActiveElement function per group. Screen-reader announcements, native zoom and physical-device checks remain pending.\n\nFull parity remains unassessed. [Capability evidence](../references/capability-roadmap.md#scrollspy).\n\n#### Contract\n\nScrollspy watches a set of sections and which one is currently in view. The table of contents on the right of every documentation page is the live demo: the matching link gets `active`, and clicking a link scrolls to that section.\n\nPut `scrollspy` and an `id` on each section. The table of contents is a set of destinations within the page, so it lives in a labelled `<nav>`; use `table-of-contents` on its list and point each link at `#that-id`. `AutoInit()` starts every `.scrollspy` except those marked `no-autoinit`.\n\n#### Syntax\n\n```html\n<div class=\"row\">\n <div class=\"s12 m9\">\n <div id=\"introduction\" class=\"section scrollspy\">\n <p>Content</p>\n </div>\n <div id=\"structure\" class=\"section scrollspy\">\n <p>Content</p>\n </div>\n <div id=\"initialization\" class=\"section scrollspy\">\n <p>Content</p>\n </div>\n </div>\n <div class=\"hide-on-compact-only m3\">\n <nav aria-label=\"On this page\">\n <ul class=\"section table-of-contents\">\n <li><a href=\"#introduction\">Introduction</a></li>\n <li><a href=\"#structure\">Structure</a></li>\n <li><a href=\"#initialization\">Initialization</a></li>\n </ul>\n </nav>\n </div>\n</div>\n```\n\n#### Options\n\n| Name | Type | Default | Description |\n| --- | --- | --- | --- |\n| `scrollOffset` | Number | `200` | Per-section observer top inset and `--md-comp-scrollspy-offset` for native anchor scroll margins. |\n| `activeClass` | String | `'active'` | Class applied to the active table-of-contents link. |\n| `getActiveElement` | Function | see below | Returns a CSS selector for the element that should receive `activeClass`, given the section’s id. |\n| `keepTopElementActive` | Boolean | `false` | If true, keep the last section above the viewport active when the scrollbar is outside all spy sections. If there is no such section, the first one stays active. |\n\n#### Methods\n\n- `.destroy()`: Disconnect the section's observer, restore its original offset style, and update the current link within its group. Repeated destruction is safe.\n\n#### Also documented\n\nThe Scrollspy section of the [API reference](https://www.expressivecss.com/llm.md) also covers Initialization, Properties.\n\n#### Rules\n\n- `toc-in-nav`: A table of contents is a set of destinations within the page. It belongs in a labelled <nav>.\n\n#### Guide checks\n\n- Label every <nav> landmark with aria-label or aria-labelledby.\n"
|
|
121
217
|
},
|
|
122
218
|
{
|
|
123
219
|
"file": "search.md",
|
|
124
|
-
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Search\nA search bar, and the view it expands into.\n\nComponent ID: `search`\n\n[Component documentation](https://www.expressivecss.com/search.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/search.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.
|
|
220
|
+
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Search\nA search bar, and the view it expands into.\n\nComponent ID: `search`\n\n[Component documentation](https://www.expressivecss.com/search.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/search.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.13.1)\n\nContract: ExpressiveCSS 0.13.1\n\nContract SHA-256: `18ec4146d300280bbcb74eb3b23f16d1015a0a3db86a8c59fc8348dbdf9d96c2`\n\n#### Selection and adaptation\n\nRuntime ownership: `css-only`. [Google guidance](https://m3.material.io/components/search/overview)\n\n#### Material mapping\n\nRelationship: component.\n\nUpstream: requirements-prose-reviewed (2026-09-13); [evidence](https://m3.material.io/components/search/overview).\n\nRequirements and boundaries: design reference inputs-material-review. Full visual parity and spoken output unverified.\n\n[Specification link](https://m3.material.io/components/search/specs); full specs unreviewed.\n\n[Google guidelines](https://m3.material.io/components/search/guidelines).\n\nSupport (2026-09-13, `llm.md#search`): Search bar and expanded search view.\n\nWeb adaptation: Application toggles hidden for docked views or opens a native dialog for full-screen views; optional Autocomplete owns suggestions.\n\nKnown boundary: CSS does not supply search results announcements or modal focus ownership.\n\nFull parity remains unassessed. [Capability evidence](../references/capability-roadmap.md#search).\n\n#### Contract\n\nA `<search>` with `search-bar` on it is the bar: a leading icon or icon button, an `<input type=\"search\">`, and whatever trailing actions the query needs. The element is the landmark, so there is no `role=\"search\"` to add. The input goes in bare — the bar is the container, so none of the text-field chrome (`.field`, the underline, the floating label) applies.\n\nSpacing follows what is at each end: a bare glyph sits 16dp from the edge, an icon button 4dp, because the button already insets its own glyph by 12dp. An `<img>` in the bar is the account avatar — 30dp and circular; put it inside the button when it is tappable, so the 48dp target comes from the button. `.searchbar`, the pre-1.0 name, reaches the same rules.\n\n#### Syntax\n\n```html\n<search class=\"search-bar\" aria-label=\"Search\">\n <span class=\"material-symbols\" aria-hidden=\"true\">search</span>\n <input type=\"search\" aria-label=\"Search recipes\" placeholder=\"Search recipes\">\n <button type=\"button\" class=\"icon-button\" aria-label=\"Filters\">\n <span class=\"material-symbols\" aria-hidden=\"true\">tune</span>\n </button>\n</search>\n```\n\n#### Also documented\n\nThe Search section of the [API reference](https://www.expressivecss.com/llm.md) also covers Docked view, Full-screen view, Suggestions, Tokens.\n\n#### Rules\n\n- `search-view-not-a-composite-widget`: The search view takes no composite role. Its contents are links and buttons reached with Tab; the combobox and listbox belong to the Autocomplete on the bar's input.\n- `search-bar-holds-a-search-input`: The bar's control is a search input. `type=\"search\"` is what tells assistive technology, the UA and the on-screen keyboard what the field is for, and it is static semantics, so the author states it. The selector names `.search-bar` and deliberately not the `.searchbar` alias: pre-1.0 markup of that shape predates the requirement, and holding old markup to a rule it could not have known is how a promise of an additive rename gets broken.\n"
|
|
125
221
|
},
|
|
126
222
|
{
|
|
127
223
|
"file": "select.md",
|
|
128
|
-
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Select\nChoose one option, or several, from a styled menu.\n\nComponent ID: `select`\n\n[Component documentation](https://www.expressivecss.com/select.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/select.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.
|
|
224
|
+
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Select\nChoose one option, or several, from a styled menu.\n\nComponent ID: `select`\n\n[Component documentation](https://www.expressivecss.com/select.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/select.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.13.1)\n\nContract: ExpressiveCSS 0.13.1\n\nContract SHA-256: `18ec4146d300280bbcb74eb3b23f16d1015a0a3db86a8c59fc8348dbdf9d96c2`\n\n#### Selection and adaptation\n\nRuntime ownership: `auto-init`. [Related Google guidance](https://m3.material.io/components/menus/overview)\n\n#### Material mapping\n\nRelationship: related. Related guidance is not a dedicated component specification.\n\nUpstream: requirements-prose-reviewed (2026-09-13); [evidence](https://m3.material.io/components/menus/overview).\n\nRequirements and boundaries: design reference inputs-material-review. Full visual parity and spoken output unverified.\n\n[Specification link](https://m3.material.io/components/menus/specs); full specs unreviewed.\n\n[Google guidelines](https://m3.material.io/components/menus/guidelines).\n\nSupport (2026-09-17, `llm.md#select`): Single/multiple selection, optgroups, and browser-default fallback.\n\nWeb adaptation: Native select enhanced by runtime; this is related to menu/text-field patterns.\n\nKnown boundary: Native zoom, screen-reader announcements, Windows contrast themes and physical-device form interaction remain unverified.\n\nFull parity remains unassessed. [Capability evidence](../references/capability-roadmap.md#select).\n\n#### Contract\n\nFilled enhanced selects reserve a label row that grows with wrapping and enlarged text. Leading icons retain their label spacing; supporting text stays outside the filled surface. Native and outlined variants retain their existing layouts.\n\nSelect turns a native `<select>` into a menu. Wrap it in a `.field` and give the label a matching `for`. `AutoInit()` starts every `select` except those marked `no-autoinit`. Add `browser-default` to keep the native control.\n\nAdd `multiple` to select several options. Chosen values appear as a comma-separated list. Distinct options with the same value can be selected and deselected independently.\n\n#### Syntax\n\n```html\n<div class=\"field\">\n <select id=\"form-select-1\">\n <option value=\"\" disabled selected>Choose your option</option>\n <option value=\"1\">Option 1</option>\n <option value=\"2\">Option 2</option>\n <option value=\"3\">Option 3</option>\n </select>\n <label for=\"form-select-1\">Expressive Select</label>\n</div>\n\n<div class=\"field\">\n <select id=\"form-select-2\" multiple>\n <option value=\"\" disabled selected>Choose your option</option>\n <option value=\"1\">Option 1</option>\n <option value=\"2\">Option 2</option>\n <option value=\"3\">Option 3</option>\n </select>\n <label for=\"form-select-2\">Multiple Select</label>\n</div>\n\n<div class=\"field\">\n <select id=\"form-select-3\">\n <optgroup label=\"team 1\">\n <option value=\"1\">Option 1</option>\n <option value=\"2\">Option 2</option>\n </optgroup>\n <optgroup label=\"team 2\">\n <option value=\"3\">Option 3</option>\n <option value=\"4\">Option 4</option>\n </optgroup>\n </select>\n <label for=\"form-select-3\">Optgroups</label>\n</div>\n\n<div class=\"field\">\n <select id=\"form-select-4\">\n <option value=\"\" disabled selected>Choose your option</option>\n <option value=\"1\" data-icon=\"photo.jpg\">example 1</option>\n <option value=\"2\" data-icon=\"photo.jpg\" class=\"left\">example 2</option>\n </select>\n <label for=\"form-select-4\">Images in select</label>\n</div>\n\n<label for=\"form-select-6\">Browser Select</label>\n<select id=\"form-select-6\" class=\"browser-default\">\n <option value=\"\" disabled selected>Choose your option</option>\n <option value=\"1\">Option 1</option>\n</select>\n```\n\n#### Options\n\n| Name | Type | Default | Description |\n| --- | --- | --- | --- |\n| `classes` | String | `''` | Space-separated classes added to the `.field` wrapper, either the parent `.field` or one the plugin creates. |\n| `menuOptions` | Object | `{}` | Options passed to `Menu`. See Menu. `coverTrigger` is forced to `false` and `closeOnClick` is forced to `false`. |\n\n#### Methods\n\n- `.getSelectedValues()`: Selected values as an array of strings.\n- `.refresh()`: Re-read native values, option labels, options and disabled states. The generated rows are rebuilt while the field and an existing Menu instance remain. Refresh preserves focus on a surviving option in an open menu; it does not emit a user `change` event. Call it after programmatic changes, including moving the select to another form or changing its `form` attribute, to rebind reset handling. There is no mutation observer.\n- `.destroy()`: Destroy the plugin instance, remove the menu, and restore the native select. Associated labels keep their original nodes and event listeners; teardown restores their attributes and authored positions.\n\n#### Also documented\n\nThe Select section of the [API reference](https://www.expressivecss.com/llm.md) also covers Initialization, Properties, Disabled Styles.\n\n#### Rules\n\n- `option-has-selected-state`: role=option promises a selection state. Without aria-selected the listbox never says which entry is chosen.\n- `field-container-class`: The field container is `.field`. `.input-field` matches nothing in the sheet - the only `.input-field` rule is `.chips.input-field`.\n- `field-icon-not-i`: <i> means idiomatic text, not icon. Use <span class=\"material-symbols\">.\n- `field-icon-hidden`: The ligature is read aloud. A field icon is decoration; the label names the control.\n- `field-icon-side`: A field icon must name its side with .prefix or .suffix. The positional fallback counts elements of the same *type*, which only ever worked for <i>.\n- `field-label-for`: A label that does not wrap its control has to name it. The floating label sits *after* the control and cannot wrap it, so it always needs `for`; a label that does wrap its control (the file input trigger) does not.\n- `field-control-id`: Without an id the label has nothing to point at.\n- `field-supporting-text-id`: Supporting and error text has to be referenceable to be announced with the control.\n- `field-supporting-text-linked`: A field with supporting text must point at it with aria-describedby, or the text is never read out with the control.\n- `password-toggle-reports-its-state`: Give a .password-toggle aria-pressed=\"false\". The behavior flips it, and its name stays \"Show password\", so the pressed state is how a screen reader user hears that the password is visible.\n- `otp-autocompletes`: Set autocomplete=\"one-time-code\" on a one-time code input so the browser can offer the code from a message.\n- `otp-is-not-a-number`: A code is not a quantity: type=\"number\" drops leading zeros and adds a spinner. Use type=\"text\" with inputmode=\"numeric\".\n"
|
|
129
225
|
},
|
|
130
226
|
{
|
|
131
227
|
"file": "side-sheet.md",
|
|
132
|
-
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Side sheet\nOptional content and actions, without interrupting the main view.\n\nComponent ID: `side-sheet`\n\n[Component documentation](https://www.expressivecss.com/side-sheet.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/side-sheet.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.
|
|
228
|
+
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Side sheet\nOptional content and actions, without interrupting the main view.\n\nComponent ID: `side-sheet`\n\n[Component documentation](https://www.expressivecss.com/side-sheet.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/side-sheet.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.13.1)\n\nContract: ExpressiveCSS 0.13.1\n\nContract SHA-256: `18ec4146d300280bbcb74eb3b23f16d1015a0a3db86a8c59fc8348dbdf9d96c2`\n\n#### Selection and adaptation\n\nRuntime ownership: `shared-runtime`. [Google guidance](https://m3.material.io/components/side-sheets/overview)\n\n- compact window: replace-with [bottom-sheet](./bottom-sheet.md). Secondary content must fit a compact window.\n- compact window: replace-with [dialogs](./dialogs.md). Use a dialog when a compact task must block progress.\n\n#### Material mapping\n\nRelationship: component.\n\nUpstream: requirements-prose-reviewed (2026-09-13); [evidence](https://m3.material.io/components/side-sheets/overview).\n\nRequirements and boundaries: design reference layout-material-review. Full visual parity and spoken output unverified.\n\n[Specification link](https://m3.material.io/components/side-sheets/specs); full specs unreviewed.\n\n[Google guidelines](https://m3.material.io/components/side-sheets/guidelines).\n\nSupport (2026-09-17, `llm.md#side-sheet`): Standard and modal side sheets.\n\nWeb adaptation: Native dialog and shared drag/dismiss behavior.\n\nKnown boundary: Rendered RTL docking, dragging and enlarged translated text are checked; native zoom, assistive technology and physical-device review remain pending.\n\nFull parity remains unassessed. [Capability evidence](../references/capability-roadmap.md#side-sheet).\n\n#### Contract\n\nA recent flick toward the docked edge dismisses the sheet. For short drags, pausing for at least 100ms or reversing direction lets it snap back.\n\nA `dialog.side-sheet` (or `.right` / `.left`) is optional content anchored to the side. `show()` is standard (1dp inner divider, no scrim). `showModal()` is modal (28dp inner corners, scrim). A `<header>` holds an optional back button, a `title-large` headline, and a close control. A last-child `form[method=dialog]` is the action row. Drag the header or the inner 24dp edge toward the docked side to dismiss.\n\nDocking is logical: `.start`, `.left` and `.left-sheet` use the start edge; the default, `.right` and `.right-sheet` use the end edge. Drag direction, inner-edge hit testing, modal corners and opening motion follow computed RTL direction. Headings and actions wrap; the body scrolls independently. Shared sheet dragging ignores secondary-pointer cancellation and resets when a sheet closes, is removed, or a primary press starts elsewhere. Dismissal thresholds are unchanged. Keep an explicit close control; native zoom, assistive technology and physical-device review remain pending.\n\n#### Syntax\n\n```html\n<dialog class=\"side-sheet\" aria-labelledby=\"headline-title\">\n <header>\n <h2 id=\"headline-title\">Headline</h2>\n <form method=\"dialog\">\n <button type=\"submit\" aria-label=\"Close\">\n <span class=\"material-symbols\" aria-hidden=\"true\">close</span>\n </button>\n </form>\n </header>\n <div>…</div>\n</dialog>\n```\n\n#### Rules\n\n- `dialog-is-named`: A <dialog> takes no name from its heading. Point aria-labelledby at that heading, or give it an aria-label - otherwise it opens announced as just \"dialog\".\n"
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
"file": "skeleton.md",
|
|
232
|
+
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Skeleton\nPlaceholder shapes that hold the layout while content loads.\n\nComponent ID: `skeleton`\n\n[Component documentation](https://www.expressivecss.com/skeleton.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/skeleton.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.13.1)\n\nContract: ExpressiveCSS 0.13.1\n\nContract SHA-256: `18ec4146d300280bbcb74eb3b23f16d1015a0a3db86a8c59fc8348dbdf9d96c2`\n\n#### Selection and adaptation\n\nRuntime ownership: `css-only`. No dedicated entry in the reviewed Google component inventory.\n\nExample: A one-second spinner while a form saves is a loading indicator. Skeletons there would draw content shapes for something that is not content.\n\n#### Material mapping\n\nRelationship: none. Web extension; no dedicated entry was observed in the reviewed current M3 component inventory.\n\nUpstream: web-extension-inventory-reviewed (2026-10-06); [evidence](https://m3.material.io/components).\n\nRequirements and boundaries: design reference web-extensions-review. The inventory page renders client-side, so this review relies on the 2026-09-13 inventory review. Full visual parity and spoken output unverified.\n\nSupport (2026-10-06, `llm.md#skeleton`): Box, text line and circle shapes with a shimmer that reverses in RTL, stops under reduced motion and keeps an outline in forced colors.\n\nWeb adaptation: Empty decorative blocks; the page marks the loading region aria-busy and reports the wait through a status.\n\nKnown boundary: The shimmer, its reduced-motion stop and the absence of skeletons from the accessibility tree are checked in a browser; status announcements and spoken output remain unverified.\n\nFull parity remains unassessed. [Capability evidence](../references/capability-roadmap.md#skeleton).\n\n#### Contract\n\nA `.skeleton` is an empty block in the shape of the content it stands in for, with a shimmer passing across it. Size the default box with `width` and `height`. `text` is one line of body text, and the last of several lines runs shorter. `circle` is a 40px circle for an avatar. Reduced motion stops the shimmer and keeps the shapes. CSS only; there is no JavaScript.\n\nSkeletons say nothing to assistive technology. Mark the region being filled with `aria-busy=\"true\"` and report the wait in a status, such as a loading indicator or visually hidden `role=\"status\"` text. Swap in the content and remove `aria-busy` in the same update.\n\n#### Syntax\n\n```html\n<p class=\"visually-hidden\" role=\"status\">Loading messages</p>\n<div class=\"flex gap-4\" aria-busy=\"true\">\n <div class=\"skeleton circle\"></div>\n <div class=\"flex-1\">\n <div class=\"skeleton text\"></div>\n <div class=\"skeleton text\"></div>\n </div>\n</div>\n```\n\n#### Also documented\n\nThe Skeleton section of the [API reference](https://www.expressivecss.com/llm.md) also covers Tokens.\n\n#### Rules\n\n- `skeleton-is-empty`: Keep a .skeleton empty. Text inside it is read out as if it were content; report the wait through a status such as a loading indicator, and mark the loading region aria-busy=\"true\".\n"
|
|
133
233
|
},
|
|
134
234
|
{
|
|
135
235
|
"file": "slider.md",
|
|
136
|
-
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Slider\nSelections from a range of values.\n\nComponent ID: `slider`\n\n[Component documentation](https://www.expressivecss.com/slider.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/slider.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.
|
|
236
|
+
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Slider\nSelections from a range of values.\n\nComponent ID: `slider`\n\n[Component documentation](https://www.expressivecss.com/slider.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/slider.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.13.1)\n\nContract: ExpressiveCSS 0.13.1\n\nContract SHA-256: `18ec4146d300280bbcb74eb3b23f16d1015a0a3db86a8c59fc8348dbdf9d96c2`\n\n#### Selection and adaptation\n\nRuntime ownership: `shared-runtime`. [Google guidance](https://m3.material.io/components/sliders/overview)\n\nExample: An invoice amount needs a labeled number field with appropriate precision. A slider fits approximate volume or brightness adjustment.\n\n#### Material mapping\n\nRelationship: component.\n\nUpstream: requirements-prose-reviewed (2026-09-13); [evidence](https://m3.material.io/components/sliders/overview).\n\nRequirements and boundaries: design reference inputs-material-review. Full visual parity and spoken output unverified.\n\n[Specification link](https://m3.material.io/components/sliders/specs); full specs unreviewed.\n\n[Google guidelines](https://m3.material.io/components/sliders/guidelines).\n\nSupport (2026-09-17, `llm.md#sliders`): Standard, centered, range, horizontal/vertical, and five sizes.\n\nWeb adaptation: Native range inputs with runtime value-label and range coordination.\n\nKnown boundary: Native-handle alignment, paired interaction and emulated forced colors have browser coverage. Screen readers, physical touch devices and actual Windows contrast themes remain pending.\n\nFull parity remains unassessed. [Capability evidence](../references/capability-roadmap.md#slider).\n\n#### Contract\n\nSliders handle a maximum of zero and update value-label placement when the input resizes, including horizontal RTL placement.\n\nMaterial Design 3 sliders, from the HTML.\n\nAn `<input type=\"range\">` is the control. A wrapping `.slider` (or a `<label>`) is the host for the value label; `.range` and `.range-field` are the older names and still work.\n\n#### Syntax\n\n```html\n<label>\n Volume\n <input type=\"range\" min=\"0\" max=\"100\" value=\"40\">\n</label>\n```\n\n#### Methods\n\n- `.destroy()`: Destroy the plugin instance, remove the value label, tear down its event handlers, and restore the inline custom properties it changed.\n\n#### Also documented\n\nThe Sliders section of the [API reference](https://www.expressivecss.com/llm.md) also covers Variants, Sizes, Inset icon, stops, value, Vertical, Disabled, Initialization, Properties.\n\n#### Rules\n\n- `range-labelled`: A range announces only its value unless something names it.\n"
|
|
137
237
|
},
|
|
138
238
|
{
|
|
139
239
|
"file": "snackbar.md",
|
|
140
|
-
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Snackbar\nMaterial Design 3 snackbars, from the HTML.\n\nComponent ID: `snackbar`\n\n[Component documentation](https://www.expressivecss.com/snackbar.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/snackbar.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.
|
|
240
|
+
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Snackbar\nMaterial Design 3 snackbars, from the HTML.\n\nComponent ID: `snackbar`\n\n[Component documentation](https://www.expressivecss.com/snackbar.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/snackbar.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.13.1)\n\nContract: ExpressiveCSS 0.13.1\n\nContract SHA-256: `18ec4146d300280bbcb74eb3b23f16d1015a0a3db86a8c59fc8348dbdf9d96c2`\n\n#### Selection and adaptation\n\nRuntime ownership: `shared-runtime`. [Google guidance](https://m3.material.io/components/snackbar/overview)\n\nExample: A successful save can use a snackbar. Offline status that still allows editing needs a persistent inline message; a required irreversible-delete confirmation needs a dialog before the action.\n\n#### Material mapping\n\nRelationship: component.\n\nUpstream: requirements-prose-reviewed (2026-09-13); [evidence](https://m3.material.io/components/snackbar/overview).\n\nRequirements and boundaries: design reference feedback-material-review. Full visual parity and spoken output unverified.\n\n[Specification link](https://m3.material.io/components/snackbar/specs); full specs unreviewed.\n\n[Google guidelines](https://m3.material.io/components/snackbar/guidelines).\n\nSupport (2026-09-13, `llm.md#snackbar`): Timed text feedback or persistent actionable messages, with optional close control and focused Escape dismissal.\n\nWeb adaptation: Use the documented announcement and timeout behavior.\n\nKnown boundary: Custom template actions require explicit persistence and a close control. Application keyboard reach controls and spoken live-region delivery need verification.\n\nFull parity remains unassessed. [Capability evidence](../references/capability-roadmap.md#snackbar).\n\n#### Contract\n\nSnackbars with an action are persistent and dismissible by default. Escape dismisses a focused snackbar; closing restores focus only when it remains inside the bar. Explicit finite timers still pause during focus or hover.\n\nSnackbars show short updates about app processes at the bottom of the screen. They should not interrupt browsing. A `.snackbar` is the bar. A `<p>` is the supporting text. A trailing `<button>` is the optional action; a `.circle` button is the optional close.\n\nTokens follow the [M3 snackbar spec](https://m3.material.io/components/snackbar/specs). The container is `inverse-surface`, 4dp corners, elevation 3, 48dp minimum. Supporting text is `body-medium` / `inverse-on-surface`, two lines max. The action is a `label-large` / `inverse-primary` text button. Close is a 24dp `inverse-on-surface` icon. On Compact viewports the bar is inset 8dp from the edges; from the Medium breakpoint up it hugs content (344–672dp) and sits centered 24dp from the bottom.\n\n#### Syntax\n\n```js\nnew Expressive.Snackbar({ text: 'Photo saved to album' });\n\nnew Expressive.Snackbar({\n text: 'Item archived',\n action: 'Undo',\n onAction: function() { /* restore */ }\n});\n\nnew Expressive.Snackbar({\n text: \"Can't send photo. Retry in 5 seconds.\",\n action: 'Retry',\n dismissible: true\n});\n```\n\n#### Options\n\n| Name | Type | Default | Description |\n| --- | --- | --- | --- |\n| `text` | String | `''` | Plain-text supporting text, wrapped in a `<p>`. If set, it replaces any HTML from `snackbarId`. |\n| `action` | String | `''` | Optional action label. Rendered as a trailing text button. |\n| `onAction` | Function | `null` | Called when the action button is pressed. The snackbar still dismisses. |\n| `dismissible` | Boolean | `false`, or `true` with an action | Show a trailing close icon button. An explicit false is preserved. |\n| `snackbarId` | String | — | Id of a `<template>` (or another element) used as the snackbar body. Templates are cloned. Other elements are moved into the snackbar and restored on dismissal or replacement, so the same ID can be reused. |\n| `displayLength` | Number | `4000` | Default 4000ms without an action, or Infinity with an action. Explicit finite timers need equivalent persistent feedback in the application. |\n| `inDuration` | Number | `300` | Enter transition duration, in milliseconds. |\n| `outDuration` | Number | `375` | Exit transition duration, in milliseconds. |\n| `classes` | String | `''` | Space-separated classes added to the snackbar. `rounded` is a stadium. `top` moves the bar off the bottom. |\n| `completeCallback` | Function | `null` | Called when the snackbar is dismissed. |\n| `activationPercent` | Number | `0.8` | Fraction of the snackbar’s width a drag must travel to dismiss it. |\n| `root` | Element | `null` | Any element in the tree the snackbar should render into. Pass one when the page lives in a shadow root, so the container is appended there rather than to `document.body`. |\n\n#### Methods\n\n- `.dismiss()`: Dismiss this snackbar with its exit animation. Runs `completeCallback` when the animation finishes.\n- `Snackbar.dismissAll()`: Dismiss every snackbar that is currently showing.\n\n#### Also documented\n\nThe Snackbar section of the [API reference](https://www.expressivecss.com/llm.md) also covers Initialization, Markup, Properties, Custom HTML, Callback, Styling, Dismiss a Snackbar Programmatically.\n\n#### Rules\n\nNo component-specific `semantics.json` rules apply to this guide.\n"
|
|
141
241
|
},
|
|
142
242
|
{
|
|
143
243
|
"file": "split-button.md",
|
|
144
|
-
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Split button\nA lead action and a trailing half that opens a menu of related ones, in five sizes.\n\nComponent ID: `split-button`\n\n[Component documentation](https://www.expressivecss.com/split-button.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/split-button.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.
|
|
244
|
+
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Split button\nA lead action and a trailing half that opens a menu of related ones, in five sizes.\n\nComponent ID: `split-button`\n\n[Component documentation](https://www.expressivecss.com/split-button.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/split-button.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.13.1)\n\nContract: ExpressiveCSS 0.13.1\n\nContract SHA-256: `18ec4146d300280bbcb74eb3b23f16d1015a0a3db86a8c59fc8348dbdf9d96c2`\n\n#### Selection and adaptation\n\nRuntime ownership: `auto-init`. [Google guidance](https://m3.material.io/components/split-button)\n\nExample: PDF/CSV format selection for a later Export action belongs in a select or native inputs. A split button fits Export now plus alternate export commands.\n\n#### Material mapping\n\nRelationship: component.\n\nUpstream: requirements-prose-reviewed (2026-09-13); [evidence](https://m3.material.io/components/split-button/overview).\n\nRequirements and boundaries: design reference layout-material-review. Full visual parity and spoken output unverified.\n\n[Specification link](https://m3.material.io/components/split-button/specs); full specs unreviewed.\n\n[Google guidelines](https://m3.material.io/components/split-button/guidelines).\n\nSupport (2026-09-18, `llm.md#split-button`): Leading action and trailing menu trigger with matching styles.\n\nWeb adaptation: Reuses Menu; no separate split-button runtime.\n\nKnown boundary: Separate 48dp effective targets, native zoom, spoken output and physical-device behavior remain unverified.\n\nFull parity remains unassessed. [Capability evidence](../references/capability-roadmap.md#split-button).\n\n#### Contract\n\nOne action the user will take most of the time, next to a menu of the ones they might take instead — Save and \"save as\", Reply and \"reply all\", Export and every other format. The root is a `<div class=\"split-button\">` holding two buttons: a leading one that does the work, and a trailing `.menu-trigger` that opens a `<menu>`. The pair sits 2dp apart, the outside is round, and the seam between them is square-ish.\n\nThe trailing half is an ordinary Menu trigger, so everything Menu does it does here — `Expressive.AutoInit()` starts it, `data-target` names the surface, and the menu's keyboard model, alignment and options are unchanged. This component adds no script of its own. The one thing Menu does differently is width: a trigger narrower than the surface's own 112dp minimum is not a constraint it can honour, and this trigger is a chevron in a 48dp box, so the menu is sized to its own content instead.\n\n#### Syntax\n\n```html\n<div class=\"split-button\">\n <button class=\"button\">Save</button>\n <button class=\"button menu-trigger\" data-target=\"save-menu\" aria-label=\"More save options\">\n <span class=\"material-symbols\" aria-hidden=\"true\">arrow_drop_down</span>\n </button>\n <menu id=\"save-menu\">\n <li><button type=\"button\">Save a copy</button></li>\n <li><button type=\"button\">Save as template</button></li>\n </menu>\n</div>\n```\n\n#### Rules\n\n- `split-button-is-not-an-authored-composite-widget`: A split button takes no composite role; its two halves are reached with Tab, not arrow keys. The <menu> the trailing half opens is the composite widget here, and it carries its own role.\n- `split-button-halves-are-controls`: A split button is exactly two controls and the menu one of them opens: a <button>, or an <a class=\"button\" href> when it navigates. A wrapper element is refused too - the seam, the insets and the larger trailing icon are all written against direct children, so a nested <div> loses every one of them.\n- `split-button-half-is-not-an-icon-button`: An icon button is its own component: its height, insets, colours, icon sizing and transitions are all set on the element itself, so it reaches none of the split button's geometry and fights the seam it is handed. The trailing half is <button class=\"button menu-trigger\">, which the container sizes like the leading one.\n- `split-button-trigger-is-the-trailing-half`: The menu trigger is the *trailing* half, and only the <menu> may follow it. Order is the whole of the geometry here: the halves are told apart by role, not by position, so a trigger written first still takes the trailing side's round outer corners while flex puts it on the leading side - a split button with its seam and its round ends swapped.\n- `split-button-is-two-controls`: A split button is exactly two controls: one lead action and one menu trigger. A third gets the leading half's rules as well, so two buttons draw round start corners and a seam against each other. Several peer actions are a button group; several related ones belong in the menu.\n- `split-button-has-a-leading-action`: A split button is a lead action *and* a menu of related ones; without the lead it is a menu trigger wearing half a pill. The trailing half's shape is written for a seam, so the only control ends up squared off on the edge it should be joined along, with nothing to join to. One control that opens a menu is a menu trigger - drop the container. Not fragmentSafe: what it detects is an omission, and a fragment omits by nature.\n- `split-button-has-a-trailing-menu-trigger`: The trailing half is the whole point: it opens a menu of actions related to the leading one. Without `.menu-trigger` on it there is no menu, and the pair is a lone button with a chevron glued to it - write two buttons, or one. Not fragmentSafe: what it detects is an omission, and a fragment omits by nature.\n- `split-button-trigger-names-its-menu`: Menu finds its surface through `data-target` on the trigger, falling back to an `href` fragment. A `.menu-trigger` with neither initializes against no element: it gets aria-haspopup and aria-expanded it will never change, which is a promise of a menu that does not exist.\n- `split-button-trailing-icon-hidden`: The ligature is real text and is read out verbatim, so an unhidden chevron names the trailing half \"arrow_drop_down\". It is decoration - the control carries the name - so it is aria-hidden=\"true\", and `icon-only-control-is-named` then requires the aria-label.\n- `split-button-expanded-is-not-authored`: Expanded is dynamic state, so the framework owns it. Menu's constructor stamps aria-expanded on the trigger and every open() and close() rewrites it; authoring it states a value that is about to be overwritten - and the expanded shape is drawn from that attribute, so an authored \"true\" draws an open split button over a closed menu.\n- `menu-children-are-list-items`: <menu> is a list: its content model permits only <li>. A bare <hr> between entries is invalid - put the separator inside an <li>, or use role=separator there.\n- `context-menu-region-is-not-a-control`: data-context-menu goes on a region of content. A button or link already has an action for Enter and click; give it its own menu trigger instead.\n"
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
"file": "stat.md",
|
|
248
|
+
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Stat\nKey figures in tiles, each with a label, a value and an optional change.\n\nComponent ID: `stat`\n\n[Component documentation](https://www.expressivecss.com/stat.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/stat.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.13.1)\n\nContract: ExpressiveCSS 0.13.1\n\nContract SHA-256: `18ec4146d300280bbcb74eb3b23f16d1015a0a3db86a8c59fc8348dbdf9d96c2`\n\n#### Selection and adaptation\n\nRuntime ownership: `css-only`. No dedicated entry in the reviewed Google component inventory.\n\nExample: Monthly revenue for every region and product is a data table. Stat tiles would turn a comparison into a wall of numbers without columns.\n\n#### Material mapping\n\nRelationship: none. Web extension; no dedicated entry was observed in the reviewed current M3 component inventory.\n\nUpstream: web-extension-inventory-reviewed (2026-10-06); [evidence](https://m3.material.io/components).\n\nRequirements and boundaries: design reference web-extensions-review. The inventory page renders client-side, so this review relies on the 2026-09-13 inventory review. Full visual parity and spoken output unverified.\n\nSupport (2026-10-06, `llm.md#stat`): Wrapping tiles in a description list with headline values, and a change line with up, down and negative states.\n\nWeb adaptation: A <dl> pairs each value with its label; the arrow is decoration and the text carries the direction.\n\nKnown boundary: Tiling, wrapping, the value type role, arrow direction, the negative color and term-definition pairing are checked in a browser; enlarged text and spoken output remain unverified.\n\nFull parity remains unassessed. [Capability evidence](../references/capability-roadmap.md#stat).\n\n#### Contract\n\nA `<dl class=\"stats\">` holds one `<div>` tile per figure: a `<dt>` label, a `<dd>` value and an optional `<dd class=\"stat-change\">` comparison. The description list keeps each value paired with its label for assistive technology. Tiles share a row and wrap below 160px each. CSS only; there is no JavaScript.\n\nAdd `up` or `down` to `.stat-change` for an arrow, and `negative` when the change is bad news, which turns it to the error color. The arrow is decoration, so say the direction in the text.\n\n#### Syntax\n\n```html\n<dl class=\"stats\">\n <div>\n <dt>Revenue</dt>\n <dd>$48.2k</dd>\n <dd class=\"stat-change up\">+12% from last month</dd>\n </div>\n <div>\n <dt>Churn</dt>\n <dd>3.1%</dd>\n <dd class=\"stat-change up negative\">+0.4 points from last month</dd>\n </div>\n</dl>\n```\n\n#### Also documented\n\nThe Stat section of the [API reference](https://www.expressivecss.com/llm.md) also covers Tokens.\n\n#### Rules\n\n- `stats-are-a-description-list`: Write .stats as a <dl> with a <dt> label and <dd> value per tile. Without the pairing a screen reader reads a run of numbers with nothing to say what each one is.\n"
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
"file": "stepper.md",
|
|
252
|
+
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Stepper\nProgress through a fixed sequence of steps, such as a checkout, with each step's state.\n\nComponent ID: `stepper`\n\n[Component documentation](https://www.expressivecss.com/stepper.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/stepper.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.13.1)\n\nContract: ExpressiveCSS 0.13.1\n\nContract SHA-256: `18ec4146d300280bbcb74eb3b23f16d1015a0a3db86a8c59fc8348dbdf9d96c2`\n\n#### Selection and adaptation\n\nRuntime ownership: `css-only`. No dedicated entry in the reviewed Google component inventory.\n\nExample: Account, Notifications and Privacy settings that people open in any order are tabs. A stepper would number them as if they had to be done in sequence.\n\n#### Material mapping\n\nRelationship: none. Web extension; no dedicated entry was observed in the reviewed current M3 component inventory.\n\nUpstream: web-extension-inventory-reviewed (2026-10-06); [evidence](https://m3.material.io/components).\n\nRequirements and boundaries: design reference web-extensions-review. The inventory page renders client-side, so this review relies on the 2026-09-13 inventory review. Full visual parity and spoken output unverified.\n\nSupport (2026-10-06, `llm.md#stepper`): Horizontal and vertical ordered lists with current, complete and invalid steps, supporting text, links back to finished steps and a content panel in vertical steps.\n\nWeb adaptation: An <ol> with aria-current=\"step\"; CSS alt text speaks the complete and invalid states from translatable tokens.\n\nKnown boundary: Layout in LTR and RTL, indicator fills and glyphs, and the spoken state text are checked in a browser; screen reader output and enlarged text remain unverified.\n\nFull parity remains unassessed. [Capability evidence](../references/capability-roadmap.md#stepper).\n\n#### Contract\n\nA `.stepper` is an `<ol>`, and each `<li>` is a step whose text is its label. The list numbers the steps. Mark the step the user is on with `aria-current=\"step\"`, finished steps with `complete`, and a step that needs attention with `invalid`; the page keeps these current as the user moves. A complete step shows a check and an invalid one shows \"!\", and both states are spoken before the label through the indicator's alt text. CSS only; there is no JavaScript.\n\nA `<small>` after the label is supporting text, such as \"Optional\". A finished step can link back with an `<a>`. Steps never shrink below their label: a row too narrow for all of them wraps. Add `vertical` to stack the steps, which suits compact windows, and put a `.stepper-content` block in a vertical step to show its form or summary under the label.\n\n#### Syntax\n\n```html\n<ol class=\"stepper\">\n <li class=\"complete\"><a href=\"/checkout/cart\">Cart</a></li>\n <li aria-current=\"step\">Shipping<small>Address and speed</small></li>\n <li>Payment</li>\n <li>Review</li>\n</ol>\n```\n\n#### Also documented\n\nThe Stepper section of the [API reference](https://www.expressivecss.com/llm.md) also covers Vertical, Translation, Tokens.\n\n#### Rules\n\n- `stepper-is-an-ordered-list`: Write a stepper as <ol class=\"stepper\">. The order is the content: an ordered list announces how many steps there are and which one each is.\n- `stepper-current-is-a-step`: Mark the current step with aria-current=\"step\". \"page\" or \"true\" tells assistive technology something else.\n- `stepper-has-one-current-step`: Only one step is current. Mark finished steps with .complete instead.\n- `stepper-step-is-not-error-colored`: Mark a step that needs attention with .invalid. .error is the error color utility: it paints the whole step red and is not spoken.\n"
|
|
145
253
|
},
|
|
146
254
|
{
|
|
147
255
|
"file": "switches.md",
|
|
148
|
-
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Switches\nMaterial Design 3 switches, from the HTML.\n\nComponent ID: `switches`\n\n[Component documentation](https://www.expressivecss.com/switches.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/switches.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.
|
|
256
|
+
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Switches\nMaterial Design 3 switches, from the HTML.\n\nComponent ID: `switches`\n\n[Component documentation](https://www.expressivecss.com/switches.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/switches.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.13.1)\n\nContract: ExpressiveCSS 0.13.1\n\nContract SHA-256: `18ec4146d300280bbcb74eb3b23f16d1015a0a3db86a8c59fc8348dbdf9d96c2`\n\n#### Selection and adaptation\n\nRuntime ownership: `css-only`. [Google guidance](https://m3.material.io/components/switch/overview)\n\n#### Material mapping\n\nRelationship: component.\n\nUpstream: requirements-prose-reviewed (2026-09-13); [evidence](https://m3.material.io/components/switch/overview).\n\nRequirements and boundaries: design reference inputs-material-review. Full visual parity and spoken output unverified.\n\n[Specification link](https://m3.material.io/components/switch/specs); full specs unreviewed.\n\n[Google guidelines](https://m3.material.io/components/switch/guidelines).\n\nSupport (2026-09-13, `llm.md#switches`): Native checkbox switches and disabled controls.\n\nWeb adaptation: Input state owns selection and form value.\n\nKnown boundary: The styled native checkbox does not perform or announce a persisted setting change.\n\nFull parity remains unassessed. [Capability evidence](../references/capability-roadmap.md#switches).\n\n#### Contract\n\nA `<label class=\"switch\">` wrapping `<input type=\"checkbox\">` is the control. The label text is a sibling of the input — no `.lever` required. They are CSS only. There is no JavaScript component and nothing to AutoInit.\n\nTokens follow the [M3 switch spec](https://m3.material.io/components/switch/specs). The track is 52×32dp. Unselected is `surface-variant` with a 2dp `outline` and a 16dp handle. Selected is a `primary` track and a 24dp `on-primary` handle. The state layer is 40dp at 8% hover and 10% focus. The touch target is 48dp. The label is `body-large` / `on-surface`. Disabled is 38%.\n\n#### Syntax\n\n```html\n<label class=\"switch\">\n <input type=\"checkbox\">\n Wi-Fi\n</label>\n\n<label class=\"switch\">\n <input type=\"checkbox\" checked>\n Bluetooth\n</label>\n\n<label class=\"switch\">\n <input type=\"checkbox\" disabled>\n Airplane mode\n</label>\n```\n\n#### Rules\n\n- `switch-is-label`: A switch is a <label> wrapping its checkbox - that is what makes the text its accessible name.\n- `switch-decorative-text-hidden`: On/off captions inside the label are folded into the accessible name, which then reads \"Off On\". Hide them and let the label text name the switch.\n"
|
|
149
257
|
},
|
|
150
258
|
{
|
|
151
259
|
"file": "tabs.md",
|
|
152
|
-
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Tabs\nMaterial Design 3 tabs, from the HTML.\n\nComponent ID: `tabs`\n\n[Component documentation](https://www.expressivecss.com/tabs.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/tabs.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.
|
|
260
|
+
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Tabs\nMaterial Design 3 tabs, from the HTML.\n\nComponent ID: `tabs`\n\n[Component documentation](https://www.expressivecss.com/tabs.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/tabs.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.13.1)\n\nContract: ExpressiveCSS 0.13.1\n\nContract SHA-256: `18ec4146d300280bbcb74eb3b23f16d1015a0a3db86a8c59fc8348dbdf9d96c2`\n\n#### Selection and adaptation\n\nRuntime ownership: `auto-init`. [Google guidance](https://m3.material.io/components/tabs/overview)\n\nExample: Home/Search/Profile are app destinations, so use a navigation bar or rail. Overview/Activity panels within one account page can use tabs; Day/Week/Month as a form value can use native radio inputs.\n\n#### Material mapping\n\nRelationship: component.\n\nUpstream: requirements-prose-reviewed (2026-09-13); [evidence](https://m3.material.io/components/tabs/overview).\n\nRequirements and boundaries: design reference inputs-material-review. Full visual parity and spoken output unverified.\n\n[Specification link](https://m3.material.io/components/tabs/specs); full specs unreviewed.\n\n[Google guidelines](https://m3.material.io/components/tabs/guidelines).\n\nSupport (2026-09-13, `llm.md#tabs`): Primary and secondary tabs; stacked and horizontal icon layouts.\n\nWeb adaptation: Links target panels; use the shipped runtime keyboard contract.\n\nKnown boundary: Native links use Tab and aria-current; runtime does not implement composite tab arrow keys.\n\nFull parity remains unassessed. [Capability evidence](../references/capability-roadmap.md#tabs).\n\n#### Contract\n\nA `<nav class=\"tabs\" aria-label=\"Sections\">` of `<a href=\"#panel\">` is the bar. A `<span>` (or the link text) is the label; a leading `<span class=\"material-symbols\">` is the icon. Nest a `<span class=\"badge\">` in the icon for a count, and put that count in the tab's `aria-label` so a hidden icon does not swallow it. `.active` is the selected tab. There is no `li.tab` required — `ul.tabs > li.tab > a` stays as an alias. `AutoInit()` starts every `.tabs` except those marked `no-autoinit`.\n\nTokens follow the [M3 tabs spec](https://m3.material.io/components/tabs/specs). Primary tabs sit on `surface`, 48dp (64dp with a stacked icon). The label is `title-small` / `on-surface-variant`; selected is `primary`. The indicator is 3dp `primary` with 3dp top corners. The icon is 24dp. A 1dp `outline-variant` divider runs under the bar. Hover is 8%; focus is 10%. Disabled is 38%.\n\n#### Syntax\n\n```html\n<nav class=\"tabs\" aria-label=\"Travel\">\n <a href=\"#flight\">\n <span class=\"material-symbols\" aria-hidden=\"true\">flight</span>\n <span>Flight</span>\n </a>\n <a class=\"active\" aria-current=\"page\" href=\"#luggage\" aria-label=\"Luggage, 3 unread\">\n <span class=\"material-symbols\" aria-hidden=\"true\">luggage<span class=\"badge\">3</span></span>\n <span>Luggage</span>\n </a>\n <a href=\"#explore\">\n <span class=\"material-symbols\" aria-hidden=\"true\">explore</span>\n <span>Explore</span>\n </a>\n</nav>\n<div id=\"flight\">Flight</div>\n<div id=\"luggage\">Luggage</div>\n<div id=\"explore\">Explore</div>\n```\n\n#### Options\n\n| Name | Type | Default | Description |\n| --- | --- | --- | --- |\n| `duration` | Number | `300` | Indicator transition duration, in milliseconds. |\n| `onShow` | Function | `null` | Called when a new tab panel is shown. Receives the panel element. |\n| `swipeable` | Boolean | `false` | Enable swipeable tabs. Uses `responsiveThreshold`. Wraps the panels in a carousel. |\n| `responsiveThreshold` | Number | `Infinity` | Maximum viewport width, in pixels, at which swipeable mode starts. Wider viewports stay non-swipeable. |\n\n#### Methods\n\n- `.select()`: Show the panel that belongs to the tab with this id. Panel IDs are matched literally, including numeric IDs and IDs containing dots, colons or brackets, in normal and swipeable modes.\n- `.updateTabIndicator()`: Recalculate the indicator position. Useful if the bar was hidden or resized.\n- `.destroy()`: Destroy the plugin instance and tear down its event handlers.\n\n#### Also documented\n\nThe Tabs section of the [API reference](https://www.expressivecss.com/llm.md) also covers Secondary, Initialization, Properties, Preselecting a tab, Linking to an External Page, Swipeable Tabs, Fixed width, Inline icons.\n\n#### Rules\n\n- `tabs-not-a-tablist`: A tab strip takes no ARIA role here; these are links to in-page sections.\n- `tabs-marks-current`: The active tab is the section you are on; aria-current says so where a class cannot.\n\n#### Guide checks\n\n- Label every <nav> landmark with aria-label or aria-labelledby.\n"
|
|
153
261
|
},
|
|
154
262
|
{
|
|
155
263
|
"file": "text-fields.md",
|
|
156
|
-
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Text fields\nMaterial Design 3 text fields, from the HTML.\n\nComponent ID: `text-fields`\n\n[Component documentation](https://www.expressivecss.com/text-inputs.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/text-inputs.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.
|
|
264
|
+
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Text fields\nMaterial Design 3 text fields, from the HTML.\n\nComponent ID: `text-fields`\n\n[Component documentation](https://www.expressivecss.com/text-inputs.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/text-inputs.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.13.1)\n\nContract: ExpressiveCSS 0.13.1\n\nContract SHA-256: `18ec4146d300280bbcb74eb3b23f16d1015a0a3db86a8c59fc8348dbdf9d96c2`\n\n#### Selection and adaptation\n\nRuntime ownership: `css-only`. [Google guidance](https://m3.material.io/components/text-fields/overview)\n\n#### Material mapping\n\nRelationship: component.\n\nUpstream: overview-specs-guidelines-accessibility-prose-reviewed (2026-09-13); [evidence](https://m3.material.io/components/text-fields/overview).\n\nRendered prose reviewed; collapsed token tables, image-only measurements, runtime and full parity unverified. See the design priority component review for requirement-level findings.\n\n[Specification link](https://m3.material.io/components/text-fields/specs); full specs unreviewed.\n\nSupport (2026-10-06, `llm.md#text-inputs`): Filled and outlined fields, supporting text, error states, trailing icon buttons, a password visibility toggle and one-time code cells.\n\nWeb adaptation: Native input/textarea with explicit label and description relationships.\n\nFull parity remains unassessed. [Capability evidence](../references/capability-roadmap.md#text-fields).\n\n#### Contract\n\nA `.field` is the container. The `<label>` after the control is the floating label, so it must carry `for` — it sits after the control and cannot wrap it. An icon names its side with `prefix` or `suffix` and is `aria-hidden`, because the ligature is real text. `<small>` is supporting text: give it an `id` and point the control at it with `aria-describedby`, or it is never read out with the field. The default is the M3 filled field; add `outlined` (or `border`) for the outlined variant.\n\n`.input-field` is **not** the container and never was — the only rule matching it in the sheet is `.chips.input-field`.\n\nTokens follow the [M3 text field spec](https://m3.material.io/components/text-fields/specs). Height is 56dp. Filled is a `surface-variant` well with 4dp top corners and a 1dp / 2dp bottom indicator. Outlined is a 1dp / 2dp `outline` at 4dp. The label is `body-large` at rest and `body-small` floated. Input text is `body-large` / `on-surface`. Icons are 24dp, 12dp from the edge.\n\n#### Syntax\n\n```html\n<div class=\"field\">\n <input id=\"first_name\" type=\"text\" placeholder=\" \" aria-describedby=\"first_name_help\">\n <label for=\"first_name\">First name</label>\n <small id=\"first_name_help\">Supporting text</small>\n</div>\n\n<div class=\"field outlined\">\n <input id=\"last_name\" type=\"text\" placeholder=\" \">\n <label for=\"last_name\">Last name</label>\n</div>\n\n<div class=\"field\">\n <span class=\"material-symbols prefix\" aria-hidden=\"true\">place</span>\n <span class=\"material-symbols suffix\" aria-hidden=\"true\">gps_fixed</span>\n <input id=\"loc\" type=\"text\" placeholder=\" \">\n <label for=\"loc\">Location</label>\n</div>\n```\n\n#### Also documented\n\nThe Text Inputs section of the [API reference](https://www.expressivecss.com/llm.md) also covers Input types, Inline, Textarea, Trailing button, Password visibility, One-time code, File input, Character counter.\n\n#### Rules\n\n- `field-container-class`: The field container is `.field`. `.input-field` matches nothing in the sheet - the only `.input-field` rule is `.chips.input-field`.\n- `field-icon-not-i`: <i> means idiomatic text, not icon. Use <span class=\"material-symbols\">.\n- `field-icon-hidden`: The ligature is read aloud. A field icon is decoration; the label names the control.\n- `field-icon-side`: A field icon must name its side with .prefix or .suffix. The positional fallback counts elements of the same *type*, which only ever worked for <i>.\n- `field-label-for`: A label that does not wrap its control has to name it. The floating label sits *after* the control and cannot wrap it, so it always needs `for`; a label that does wrap its control (the file input trigger) does not.\n- `field-control-id`: Without an id the label has nothing to point at.\n- `field-supporting-text-id`: Supporting and error text has to be referenceable to be announced with the control.\n- `field-supporting-text-linked`: A field with supporting text must point at it with aria-describedby, or the text is never read out with the control.\n- `password-toggle-reports-its-state`: Give a .password-toggle aria-pressed=\"false\". The behavior flips it, and its name stays \"Show password\", so the pressed state is how a screen reader user hears that the password is visible.\n- `otp-autocompletes`: Set autocomplete=\"one-time-code\" on a one-time code input so the browser can offer the code from a message.\n- `otp-is-not-a-number`: A code is not a quantity: type=\"number\" drops leading zeros and adds a spinner. Use type=\"text\" with inputmode=\"numeric\".\n- `file-input-not-nested`: A control inside a control is not valid HTML and gives two overlapping hit targets. Use a <label> styled as a button.\n- `file-path-readonly`: The path field only ever displays what the file input holds; leaving it writable offers an edit that is discarded.\n- `counter-is-announced`: The count changes as the user types, so it has to be a live region or the remaining characters are never announced.\n"
|
|
157
265
|
},
|
|
158
266
|
{
|
|
159
267
|
"file": "time-picker.md",
|
|
160
|
-
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Time picker\nPick a time from a clock face, in 12-hour or 24-hour form.\n\nComponent ID: `time-picker`\n\n[Component documentation](https://www.expressivecss.com/timepicker.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/timepicker.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.
|
|
268
|
+
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Time picker\nPick a time from a clock face, in 12-hour or 24-hour form.\n\nComponent ID: `time-picker`\n\n[Component documentation](https://www.expressivecss.com/timepicker.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/timepicker.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.13.1)\n\nContract: ExpressiveCSS 0.13.1\n\nContract SHA-256: `18ec4146d300280bbcb74eb3b23f16d1015a0a3db86a8c59fc8348dbdf9d96c2`\n\n#### Selection and adaptation\n\nRuntime ownership: `auto-init`. [Google guidance](https://m3.material.io/components/time-pickers/overview)\n\nExample: An appointment time can use a labeled native time input when platform entry is enough. Do not choose this picker expecting a popup: its clock stays inline.\n\n#### Material mapping\n\nRelationship: component.\n\nUpstream: requirements-prose-reviewed (2026-09-13); [evidence](https://m3.material.io/components/time-pickers/overview).\n\nRequirements and boundaries: design reference inputs-material-review. Full visual parity and spoken output unverified.\n\n[Specification link](https://m3.material.io/components/time-pickers/specs); full specs unreviewed.\n\n[Google guidelines](https://m3.material.io/components/time-pickers/guidelines).\n\nSupport (2026-09-18, `llm.md#time-picker`): Inline clock and digital input; 12-hour and 24-hour formats.\n\nWeb adaptation: Default inline presentation with optional docked display plugin; no open()/close() methods or modal implementation.\n\nKnown boundary: Modal picker parity is not provided by the documented inline API.\n\nKnown boundary: Named digital inputs, AM/PM state and input-event synchronization have browser coverage. Modal and clock-dial keyboard behavior remain incomplete.\n\nFull parity remains unassessed. [Capability evidence](../references/capability-roadmap.md#time-picker).\n\n#### Contract\n\nAdd `time-picker` to a text input. `AutoInit()` starts every `.time-picker` except those marked `no-autoinit`.\n\nThe clock is inline, not a modal. It is appended to the input’s parent and stays visible. There is no `openByDefault` flag. `open()` and `close()` are not provided.\n\n#### Syntax\n\n```html\n<div class=\"field\">\n <input type=\"text\" class=\"time-picker\" id=\"lunchtime\">\n <label for=\"lunchtime\">Lunchtime</label>\n</div>\n```\n\n#### Options\n\n| Name | Type | Default | Description |\n| --- | --- | --- | --- |\n| `duration` | Number | `350` | Transition between the hours and minutes views, in milliseconds. |\n| `container` | Element or String | `null` | Element or selector to render the clock into. When omitted, it is appended to the input’s parent. |\n| `showClearBtn` | Boolean | `false` | Show a Clear button in the footer. |\n| `autoSubmit` | Boolean | `true` | If true, choosing a minute writes the input. If false, Ok and Cancel buttons are added. |\n| `defaultTime` | String | `'now'` | Initial time. `'now'` or a `'HH:MM'` string (optionally with `AM`/`PM`). |\n| `fromNow` | Number | `0` | Millisecond offset added to `'now'`. |\n| `i18n` | Object | See below | Labels for Cancel, Clear, Ok, Hours, and Minutes. |\n| `twelveHour` | Boolean | `true` | If true, use a 12-hour clock with AM/PM. If false, use 24-hour hours on two rings. |\n| `vibrate` | Boolean | `true` | Vibrate the device when the clock hand changes value. |\n| `onSelect` | Function | `null` | Called when a time is chosen on the dial. Receives `(hour, minute)`. |\n| `onInputInteraction` | Function | `null` | Called when the input is clicked or confirmed with Enter. |\n| `onDone` | Function | `null` | Called once when Ok is activated by click, Enter or Space. The button is created when `autoSubmit` is false. |\n| `onCancel` | Function | `null` | Called once when Cancel is activated by click, Enter or Space. The button is created when `autoSubmit` is false. |\n| `displayPlugin` | String | `null` | Set to `'docked'` for a click-to-open popover. |\n| `displayPluginOptions` | Object | `null` | Options for the docked plugin: `margin`, `transition`, `duration`, `align`. |\n\n#### Methods\n\n- `.showView()`: Show the hours or minutes face.\n- `.done()`: Write the current hours and minutes to the input. Pass a truthy argument to clear the input instead.\n- `.clear()`: Clear the input. Same as `done(true)`.\n- `.destroy()`: Destroy the plugin instance, remove the clock, and tear down its event handlers. Pending automatic submission and clock updates are cancelled, so the destroyed instance cannot write to the input.\n- `.open(); / .close()`: These methods are not provided. The clock defaults to visible inline presentation; `displayPlugin: 'docked'` enables docking. No modal implementation is available.\n\n#### Also documented\n\nThe Time Picker section of the [API reference](https://www.expressivecss.com/llm.md) also covers Initialization, Display, Internationalization, 12-hour and 24-hour, Properties.\n\n#### Rules\n\nNo component-specific `semantics.json` rules apply to this guide.\n"
|
|
269
|
+
},
|
|
270
|
+
{
|
|
271
|
+
"file": "timeline.md",
|
|
272
|
+
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Timeline\nEvents in order, each with a time, a title and details, joined by a line.\n\nComponent ID: `timeline`\n\n[Component documentation](https://www.expressivecss.com/timeline.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/timeline.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.13.1)\n\nContract: ExpressiveCSS 0.13.1\n\nContract SHA-256: `18ec4146d300280bbcb74eb3b23f16d1015a0a3db86a8c59fc8348dbdf9d96c2`\n\n#### Selection and adaptation\n\nRuntime ownership: `css-only`. No dedicated entry in the reviewed Google component inventory.\n\nExample: Cart, Shipping and Payment in a checkout are steps the user still has to finish, so they belong in a stepper. A timeline would present them as things that already happened.\n\n#### Material mapping\n\nRelationship: none. Web extension; no dedicated entry was observed in the reviewed current M3 component inventory.\n\nUpstream: web-extension-inventory-reviewed (2026-10-06); [evidence](https://m3.material.io/components).\n\nRequirements and boundaries: design reference web-extensions-review. The inventory page renders client-side, so this review relies on the 2026-09-13 inventory review. Full visual parity and spoken output unverified.\n\nSupport (2026-10-06, `llm.md#timeline`): Ordered events with times, headings and text, dots joined by a line and a leading icon variant.\n\nWeb adaptation: An <ol> of events with <time datetime>; the dots and line are decoration.\n\nKnown boundary: Marker and line geometry in LTR and RTL and the icon variant are checked in a browser; long feeds, enlarged text and spoken output remain unverified.\n\nFull parity remains unassessed. [Capability evidence](../references/capability-roadmap.md#timeline).\n\n#### Contract\n\nAn `<ol class=\"timeline\">` lists events in the order the page chooses, newest or oldest first. Each `<li>` is an event: an optional `<time>` with a `datetime`, a heading and supporting `<p>` text. A dot marks each event and a line joins it to the next. A leading icon as the first child replaces the dot with a tonal circle; hide it from assistive technology. CSS only; there is no JavaScript.\n\n#### Syntax\n\n```html\n<ol class=\"timeline\">\n <li>\n <time datetime=\"2026-10-06T09:12\">9:12 AM</time>\n <h3>Order placed</h3>\n <p>Two items, paid by card.</p>\n </li>\n <li>\n <time datetime=\"2026-10-06T11:40\">11:40 AM</time>\n <h3>Shipped</h3>\n <p>Tracking number sent by email.</p>\n </li>\n</ol>\n```\n\n#### Also documented\n\nThe Timeline section of the [API reference](https://www.expressivecss.com/llm.md) also covers Leading icon, Tokens.\n\n#### Rules\n\n- `timeline-is-an-ordered-list`: Write a timeline as <ol class=\"timeline\">. The events have an order, and an ordered list says so to assistive technology.\n- `timeline-time-is-machine-readable`: Give each <time> a datetime value. \"9:12 AM\" or \"Yesterday\" has no date a script, a translation tool or a reader in another time zone can use.\n"
|
|
161
273
|
},
|
|
162
274
|
{
|
|
163
275
|
"file": "toolbars.md",
|
|
164
|
-
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Toolbars\nFrequently used actions for the current page.\n\nComponent ID: `toolbars`\n\n[Component documentation](https://www.expressivecss.com/toolbars.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/toolbars.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.
|
|
276
|
+
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Toolbars\nFrequently used actions for the current page.\n\nComponent ID: `toolbars`\n\n[Component documentation](https://www.expressivecss.com/toolbars.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/toolbars.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.13.1)\n\nContract: ExpressiveCSS 0.13.1\n\nContract SHA-256: `18ec4146d300280bbcb74eb3b23f16d1015a0a3db86a8c59fc8348dbdf9d96c2`\n\n#### Selection and adaptation\n\nRuntime ownership: `css-only`. [Google guidance](https://m3.material.io/components/toolbars/overview)\n\nExample: Home/Search/Profile belong in navigation. A toolbar near a document can hold edit, share, and formatting actions.\n\n#### Material mapping\n\nRelationship: component.\n\nUpstream: overview-specs-guidelines-accessibility-prose-reviewed (2026-09-13); [evidence](https://m3.material.io/components/toolbars/overview).\n\nRendered prose reviewed; collapsed token tables, image-only measurements, runtime and full parity unverified. See the design priority component review for requirement-level findings.\n\n[Specification link](https://m3.material.io/components/toolbars/specs); full specs unreviewed.\n\nSupport (2026-09-07, `llm.md#toolbars`): Floating/docked, standard/vibrant, vertical floating, and companion FAB.\n\nWeb adaptation: CSS-only commands; no automatic composite toolbar keyboard model.\n\nFull parity remains unassessed. [Capability evidence](../references/capability-roadmap.md#toolbars).\n\n#### Contract\n\nMaterial Design 3 toolbars, from the HTML.\n\nA `<div class=\"toolbar\">` is the bar — not a `<nav>`, because a toolbar holds commands rather than destinations. Direct `<button>` or `<a>` children are the actions. A `<span class=\"material-symbols\">` is the icon; wrap a label in its own `<span>`. An action with an icon and no label needs an `aria-label`. `.active` marks the selected action. They are CSS only. There is no JavaScript component and nothing to AutoInit.\n\nTokens follow the [M3 toolbar spec](https://m3.material.io/components/toolbars/specs). Actions are 48dp targets with a 24dp icon, transparent at rest, `on-surface-variant`, 4dp apart.\n\n#### Syntax\n\n```html\n<div class=\"toolbar\">\n <button type=\"button\" class=\"circle\" aria-label=\"Undo\">\n <span class=\"material-symbols\" aria-hidden=\"true\">undo</span>\n </button>\n <button type=\"button\" class=\"circle active\" aria-label=\"Bold\">\n <span class=\"material-symbols\" aria-hidden=\"true\">format_bold</span>\n </button>\n <button type=\"button\" class=\"circle\" aria-label=\"Italic\">\n <span class=\"material-symbols\" aria-hidden=\"true\">format_italic</span>\n </button>\n</div>\n```\n\n#### Also documented\n\nThe Toolbars section of the [API reference](https://www.expressivecss.com/llm.md) also covers Variants, Docked, Pairing with a FAB, Fixed.\n\n#### Rules\n\n- `toolbar-not-nav`: A toolbar holds commands (Bold, Italic), not navigation, so it is not a <nav> landmark.\n- `toolbar-not-a-composite-widget`: A toolbar takes no composite role; its buttons are reached with Tab, not arrow keys.\n"
|
|
165
277
|
},
|
|
166
278
|
{
|
|
167
279
|
"file": "tooltips.md",
|
|
168
|
-
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Tooltips\nMaterial Design 3 tooltips, from the HTML.\n\nComponent ID: `tooltips`\n\n[Component documentation](https://www.expressivecss.com/tooltips.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/tooltips.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.
|
|
280
|
+
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Tooltips\nMaterial Design 3 tooltips, from the HTML.\n\nComponent ID: `tooltips`\n\n[Component documentation](https://www.expressivecss.com/tooltips.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/tooltips.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.13.1)\n\nContract: ExpressiveCSS 0.13.1\n\nContract SHA-256: `18ec4146d300280bbcb74eb3b23f16d1015a0a3db86a8c59fc8348dbdf9d96c2`\n\n#### Selection and adaptation\n\nRuntime ownership: `auto-init`. [Google guidance](https://m3.material.io/components/tooltips/overview)\n\n#### Material mapping\n\nRelationship: component.\n\nUpstream: overview-specs-guidelines-accessibility-prose-reviewed (2026-09-13); [evidence](https://m3.material.io/components/tooltips/overview).\n\nRendered prose reviewed; collapsed token tables, image-only measurements, runtime and full parity unverified. See the design priority component review for requirement-level findings.\n\n[Specification link](https://m3.material.io/components/tooltips/specs); full specs unreviewed.\n\nSupport (2026-09-07, `llm.md#tooltips`): Plain and rich tooltips; CSS child and runtime-generated forms.\n\nWeb adaptation: The selected form determines ownership; CSS-only hover/focus is not runtime initialization.\n\nKnown boundary: For hover/focus tooltips, verify dismissibility, hoverability, and persistence under WCAG SC 1.4.13, including its exceptions. Plain CSS bubbles disable pointer events and have no own Escape handler; aria-describedby alone does not verify these behaviors. Read the [focused web checks](../expressivecss-accessibility/references/web-checks.md#content-on-hover-or-focus).\n\nFull parity remains unassessed. [Capability evidence](../references/capability-roadmap.md#tooltips).\n\n#### Contract\n\nA child `.tooltip` is the bubble. No `tooltipped` class is required. They are CSS only on hover and keyboard focus. Helpers are only for placement and the rich variant: `top` (the default), `bottom`, `left`, `right`, `rich` / `max`.\n\nTokens follow the [M3 tooltip spec](https://m3.material.io/components/tooltips/specs). A plain tooltip is `inverse-surface` / `inverse-on-surface`, 4dp corners, `body-small`, 24dp minimum, 4/8dp padding, 200dp max, no elevation and no caret. It sits 4dp from the activator. M3 prefers above.\n\nInside a `<button>` the bubble has to be a `<span>` — a `<div>` is not phrasing content and the parser will hoist it. Icon-only buttons still need `.circle` so the span is not treated as a label.\n\n#### Syntax\n\n```html\n<button type=\"button\" class=\"circle\" aria-label=\"Add\" aria-describedby=\"tip-add-to-album\">\n <span class=\"material-symbols\" aria-hidden=\"true\">add</span>\n <span class=\"tooltip\" id=\"tip-add-to-album\">Add to album</span>\n</button>\n\n<button type=\"button\" class=\"circle\" aria-label=\"Below\" aria-describedby=\"tip-below\">\n <span class=\"material-symbols\" aria-hidden=\"true\">arrow_downward</span>\n <span class=\"tooltip bottom\" id=\"tip-below\">Below</span>\n</button>\n```\n\n#### Options\n\n| Name | Type | Default | Description |\n| --- | --- | --- | --- |\n| `exitDelay` | Number | `200` | Delay before the tooltip disappears, in milliseconds. |\n| `enterDelay` | Number | `0` | Delay before the tooltip appears, in milliseconds. |\n| `tooltipId` | String | — | Id of an element used as the tooltip body. Set by `data-tooltip-id`. Marks the bubble `rich`. |\n| `text` | String | `''` | Plain-text content. Set by `data-tooltip`. Ignored when `tooltipId` is set. |\n| `margin` | Number | `4` | Distance from the activator, in pixels, not counting `transitionMovement`. M3 is 4dp. |\n| `inDuration` | Number | `250` | Enter transition duration, in milliseconds. |\n| `opacity` | Number | `1` | Opacity of the tooltip when shown. |\n| `outDuration` | Number | `200` | Exit transition duration, in milliseconds. |\n| `position` | String | `'bottom'` | Direction: `'top'`, `'right'`, `'bottom'`, or `'left'`. Set by `data-position`. The CSS-only default is above. |\n| `transitionMovement` | Number | `10` | How far the tooltip moves during its transition, in pixels. |\n\n#### Methods\n\n- `.open()`: `open()` and `open(true)` show the tooltip after its enter delay without requiring hover or focus. `open(false)` uses the automatic behavior and requires hover or focus when the delay ends.\n- `.close()`: Hide the tooltip.\n- `.destroy()`: Destroy the plugin instance and remove its generated tooltip and event handlers. Rich content returns to its authored position and presentation state. Existing description associations are preserved, so the same content can be initialized again.\n\n#### Also documented\n\nThe Tooltips section of the [API reference](https://www.expressivecss.com/llm.md) also covers Rich, JavaScript, Initialization, Properties.\n\n#### Rules\n\n- `css-tooltip-is-described-by`: A .tooltip inside its control is swallowed: with an aria-label present the label wins and the tooltip is never announced, and without one the two run together. Give the tooltip an id and point aria-describedby at it.\n"
|
|
281
|
+
},
|
|
282
|
+
{
|
|
283
|
+
"file": "tree.md",
|
|
284
|
+
"content": "<!-- Generated by scripts/gen-expressivecss-skill.mjs. Do not edit. -->\n\n### Tree\nNested lists of folders and links that open in place, for file browsers and deep navigation.\n\nComponent ID: `tree`\n\n[Component documentation](https://www.expressivecss.com/tree.html.md) · [Repository source](https://github.com/BaezFJ/ExpressiveCSS/blob/master/docs/src/pages/tree.astro) · [Matching tag](https://github.com/BaezFJ/ExpressiveCSS/tree/v0.13.1)\n\nContract: ExpressiveCSS 0.13.1\n\nContract SHA-256: `18ec4146d300280bbcb74eb3b23f16d1015a0a3db86a8c59fc8348dbdf9d96c2`\n\n#### Selection and adaptation\n\nRuntime ownership: `native`. No dedicated entry in the reviewed Google component inventory.\n\nExample: Three settings pages under one heading are a list or a navigation rail. A tree would add chevrons and indentation for a hierarchy one level deep.\n\n#### Material mapping\n\nRelationship: none. Web extension; no dedicated entry was observed in the reviewed current M3 component inventory.\n\nUpstream: web-extension-inventory-reviewed (2026-10-06); [evidence](https://m3.material.io/components).\n\nRequirements and boundaries: design reference web-extensions-review. The inventory page renders client-side, so this review relies on the 2026-09-13 inventory review. Full visual parity and spoken output unverified.\n\nSupport (2026-10-06, `llm.md#tree`): Nested lists of native disclosures and links with guide lines, turning chevrons in LTR and RTL, leading icons and an aria-current row.\n\nWeb adaptation: Native <details> branches and links; every row is a Tab stop and no tree role is promised.\n\nKnown boundary: Indentation, chevrons in LTR and RTL, opening a branch and the current row are checked in a browser; long trees for keyboard users, enlarged text and spoken output remain unverified.\n\nFull parity remains unassessed. [Capability evidence](../references/capability-roadmap.md#tree).\n\n#### Contract\n\nA `<ul class=\"tree\">` nests lists. A branch is an `<li>` holding a `<details>`: its `<summary>` is the row and its nested `<ul>` the children. A leaf is an `<li>` holding a link, or a `<span>` when it is not one. Mark the current row with `aria-current`. Each level draws a guide line from its parent's chevron. Wrap a tree of links in a named `<nav>`. CSS only; there is no JavaScript.\n\nIt is lists of disclosures and links rather than `role=\"tree\"`: every row is a Tab stop and the browser opens a branch with Enter or Space. A tree role would promise arrow-key navigation that native disclosures do not provide.\n\n#### Syntax\n\n```html\n<nav aria-label=\"Files\">\n <ul class=\"tree\">\n <li>\n <details open>\n <summary>src</summary>\n <ul>\n <li><a href=\"/src/index.ts\" aria-current=\"page\">index.ts</a></li>\n <li>\n <details>\n <summary>components</summary>\n <ul>\n <li><a href=\"/src/components/menu.ts\">menu.ts</a></li>\n </ul>\n </details>\n </li>\n </ul>\n </details>\n </li>\n <li><a href=\"/README.md\">README.md</a></li>\n </ul>\n</nav>\n```\n\n#### Also documented\n\nThe Tree section of the [API reference](https://www.expressivecss.com/llm.md) also covers Tokens.\n\n#### Rules\n\n- `tree-is-not-a-tree-widget`: A .tree takes no ARIA tree roles. It is lists of disclosures and links, which keep their own roles and keyboard behavior.\n- `tree-marks-current-not-selected`: Mark the open file or page with aria-current. aria-selected is not valid on a link or a list item.\n\n#### Guide checks\n\n- Label every <nav> landmark with aria-label or aria-labelledby.\n"
|
|
169
285
|
}
|
|
170
286
|
]
|
|
171
287
|
}
|