@uxelle/skills 0.2.2 → 0.2.3

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/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  // src/manifest.generated.ts
2
2
  var manifest = {
3
3
  "schemaVersion": 1,
4
- "version": "0.2.2",
4
+ "version": "0.2.3",
5
5
  "skills": [
6
6
  {
7
7
  "name": "uxelle-components",
@@ -154,8 +154,8 @@ var manifest = {
154
154
  },
155
155
  {
156
156
  "path": "getting-started.md",
157
- "size": 2276,
158
- "hash": "sha256:c40148458f76df3bdd9a0cf85732cd09bbe621c5625d20c4251aa7f4bd808a90"
157
+ "size": 3387,
158
+ "hash": "sha256:5aa1009aaf71d2d45d8f57198ee52942d4220d031948357c646b799b19ffd34f"
159
159
  },
160
160
  {
161
161
  "path": "Hero.md",
@@ -164,8 +164,8 @@ var manifest = {
164
164
  },
165
165
  {
166
166
  "path": "Icon.md",
167
- "size": 2218,
168
- "hash": "sha256:bfde859f04937c40b1ce2f3b386e9b059e3781878b1bb91471e688de605ccf1b"
167
+ "size": 2948,
168
+ "hash": "sha256:c467d672c13c5d52120618d5f932454e5f3a8dbd5f8bdd2e1498b26f676d523d"
169
169
  },
170
170
  {
171
171
  "path": "IconButton.md",
@@ -254,8 +254,8 @@ var manifest = {
254
254
  },
255
255
  {
256
256
  "path": "NavigationSide.md",
257
- "size": 5005,
258
- "hash": "sha256:32386fd82517e9d74561b4c05b8a7b0951532fcf1ab80b038fe7bde376b96752"
257
+ "size": 5474,
258
+ "hash": "sha256:ffe9e9a88ade7c6cef99c1953e4e265afce446d616af366fd4b63206517396bf"
259
259
  },
260
260
  {
261
261
  "path": "NavigationSideGroup.md",
@@ -264,13 +264,13 @@ var manifest = {
264
264
  },
265
265
  {
266
266
  "path": "NavigationSideItem.md",
267
- "size": 5839,
268
- "hash": "sha256:a57473d0c207155e48e34e495a9a8b195357442871132c04e3a51b0130044cc9"
267
+ "size": 6595,
268
+ "hash": "sha256:3e7f51a9eabb5bf144022064b14416cebbc4d7d9eb87ee9c82299c5ecbb50e34"
269
269
  },
270
270
  {
271
271
  "path": "NavigationSideSubItem.md",
272
- "size": 2350,
273
- "hash": "sha256:f3da7ff70aac2a0568ff6831efd18bd2849d459f88ba41a76006432a33245b02"
272
+ "size": 2649,
273
+ "hash": "sha256:3edf70cb4a3cb43abd112cbae73d83385481f004d8e48513ab455596388d221e"
274
274
  },
275
275
  {
276
276
  "path": "NavLink.md",
@@ -344,8 +344,8 @@ var manifest = {
344
344
  },
345
345
  {
346
346
  "path": "SKILL.md",
347
- "size": 14408,
348
- "hash": "sha256:de312111538d710c822a0212af2188584591d594d4f99ba7f3efb7a4435e9021"
347
+ "size": 14816,
348
+ "hash": "sha256:a15ed6eb6c149096d9cb6376ee47e17700cbf36ed498edd176232ff87f78d94b"
349
349
  },
350
350
  {
351
351
  "path": "Spinner.md",
@@ -354,8 +354,8 @@ var manifest = {
354
354
  },
355
355
  {
356
356
  "path": "StatTile.md",
357
- "size": 7820,
358
- "hash": "sha256:e9330341336d46ab38724a17e76548d8cd4081b06b1a483bfea6be642718ea9e"
357
+ "size": 8042,
358
+ "hash": "sha256:76a99c8ea47082659e625c59030d0cef527bd19d8d6cabd635e71016be13754d"
359
359
  },
360
360
  {
361
361
  "path": "Stepper.md",
@@ -415,8 +415,8 @@ var manifest = {
415
415
  "files": [
416
416
  {
417
417
  "path": "a2ui.md",
418
- "size": 4779,
419
- "hash": "sha256:e557e7caba1471f250a651f6449aa4b8e73b40e05c331dcbda25892b958ecd99"
418
+ "size": 5279,
419
+ "hash": "sha256:9ebad576e8878251c6ce2210a7cab70066b30d04f0b38e1963b5eeb9f5c820e3"
420
420
  },
421
421
  {
422
422
  "path": "density.md",
@@ -425,8 +425,8 @@ var manifest = {
425
425
  },
426
426
  {
427
427
  "path": "how-to-accessibility.md",
428
- "size": 3554,
429
- "hash": "sha256:c19043086e302c1681ae462efa9f7282a9e227aeb78dfe93bf66f6c4f4710bf7"
428
+ "size": 3732,
429
+ "hash": "sha256:c66c97e67182cb444fb9ff6f1b02db8da663f928a79bf31517008b9dc3f7a82e"
430
430
  },
431
431
  {
432
432
  "path": "how-to-color.md",
@@ -435,8 +435,8 @@ var manifest = {
435
435
  },
436
436
  {
437
437
  "path": "how-to-host.md",
438
- "size": 2647,
439
- "hash": "sha256:bdab3b08e77adaabcd582dff4b64838e0723ade66d5c9416be9111f9dc9b49cd"
438
+ "size": 2856,
439
+ "hash": "sha256:05187f27fb791bc18c613354635d5d68a044454c166dfce0925116adca3a6cba"
440
440
  },
441
441
  {
442
442
  "path": "how-to-page-layout.md",
@@ -465,8 +465,8 @@ var manifest = {
465
465
  },
466
466
  {
467
467
  "path": "recipe-dashboard-overview.md",
468
- "size": 4708,
469
- "hash": "sha256:a6a581ca179595535d9d3df0469d362d8e7175e5595bea3222a32733c66c4615"
468
+ "size": 4857,
469
+ "hash": "sha256:4fe4573d6eb09730c1737875c835ca9b939cd3d8e8587ecae09ba7331b2ff8d7"
470
470
  },
471
471
  {
472
472
  "path": "recipe-data-table-page.md",
@@ -540,8 +540,8 @@ var manifest = {
540
540
  },
541
541
  {
542
542
  "path": "recipe-stat-callouts.md",
543
- "size": 4017,
544
- "hash": "sha256:fb3857880f691fd016cb1414ee788e3a9fdfc49f7e61376442965aa018896f6e"
543
+ "size": 4207,
544
+ "hash": "sha256:ef84f56f302f623b990dff4e5051256642a41cf2dabab0e102ddf030bff4f61c"
545
545
  },
546
546
  {
547
547
  "path": "recipe-states.md",
package/index.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "skills": [
5
5
  {
6
6
  "name": "uxelle-components",
@@ -153,8 +153,8 @@
153
153
  },
154
154
  {
155
155
  "path": "getting-started.md",
156
- "size": 2276,
157
- "hash": "sha256:c40148458f76df3bdd9a0cf85732cd09bbe621c5625d20c4251aa7f4bd808a90"
156
+ "size": 3387,
157
+ "hash": "sha256:5aa1009aaf71d2d45d8f57198ee52942d4220d031948357c646b799b19ffd34f"
158
158
  },
159
159
  {
160
160
  "path": "Hero.md",
@@ -163,8 +163,8 @@
163
163
  },
164
164
  {
165
165
  "path": "Icon.md",
166
- "size": 2218,
167
- "hash": "sha256:bfde859f04937c40b1ce2f3b386e9b059e3781878b1bb91471e688de605ccf1b"
166
+ "size": 2948,
167
+ "hash": "sha256:c467d672c13c5d52120618d5f932454e5f3a8dbd5f8bdd2e1498b26f676d523d"
168
168
  },
169
169
  {
170
170
  "path": "IconButton.md",
@@ -253,8 +253,8 @@
253
253
  },
254
254
  {
255
255
  "path": "NavigationSide.md",
256
- "size": 5005,
257
- "hash": "sha256:32386fd82517e9d74561b4c05b8a7b0951532fcf1ab80b038fe7bde376b96752"
256
+ "size": 5474,
257
+ "hash": "sha256:ffe9e9a88ade7c6cef99c1953e4e265afce446d616af366fd4b63206517396bf"
258
258
  },
259
259
  {
260
260
  "path": "NavigationSideGroup.md",
@@ -263,13 +263,13 @@
263
263
  },
264
264
  {
265
265
  "path": "NavigationSideItem.md",
266
- "size": 5839,
267
- "hash": "sha256:a57473d0c207155e48e34e495a9a8b195357442871132c04e3a51b0130044cc9"
266
+ "size": 6595,
267
+ "hash": "sha256:3e7f51a9eabb5bf144022064b14416cebbc4d7d9eb87ee9c82299c5ecbb50e34"
268
268
  },
269
269
  {
270
270
  "path": "NavigationSideSubItem.md",
271
- "size": 2350,
272
- "hash": "sha256:f3da7ff70aac2a0568ff6831efd18bd2849d459f88ba41a76006432a33245b02"
271
+ "size": 2649,
272
+ "hash": "sha256:3edf70cb4a3cb43abd112cbae73d83385481f004d8e48513ab455596388d221e"
273
273
  },
274
274
  {
275
275
  "path": "NavLink.md",
@@ -343,8 +343,8 @@
343
343
  },
344
344
  {
345
345
  "path": "SKILL.md",
346
- "size": 14408,
347
- "hash": "sha256:de312111538d710c822a0212af2188584591d594d4f99ba7f3efb7a4435e9021"
346
+ "size": 14816,
347
+ "hash": "sha256:a15ed6eb6c149096d9cb6376ee47e17700cbf36ed498edd176232ff87f78d94b"
348
348
  },
349
349
  {
350
350
  "path": "Spinner.md",
@@ -353,8 +353,8 @@
353
353
  },
354
354
  {
355
355
  "path": "StatTile.md",
356
- "size": 7820,
357
- "hash": "sha256:e9330341336d46ab38724a17e76548d8cd4081b06b1a483bfea6be642718ea9e"
356
+ "size": 8042,
357
+ "hash": "sha256:76a99c8ea47082659e625c59030d0cef527bd19d8d6cabd635e71016be13754d"
358
358
  },
359
359
  {
360
360
  "path": "Stepper.md",
@@ -414,8 +414,8 @@
414
414
  "files": [
415
415
  {
416
416
  "path": "a2ui.md",
417
- "size": 4779,
418
- "hash": "sha256:e557e7caba1471f250a651f6449aa4b8e73b40e05c331dcbda25892b958ecd99"
417
+ "size": 5279,
418
+ "hash": "sha256:9ebad576e8878251c6ce2210a7cab70066b30d04f0b38e1963b5eeb9f5c820e3"
419
419
  },
420
420
  {
421
421
  "path": "density.md",
@@ -424,8 +424,8 @@
424
424
  },
425
425
  {
426
426
  "path": "how-to-accessibility.md",
427
- "size": 3554,
428
- "hash": "sha256:c19043086e302c1681ae462efa9f7282a9e227aeb78dfe93bf66f6c4f4710bf7"
427
+ "size": 3732,
428
+ "hash": "sha256:c66c97e67182cb444fb9ff6f1b02db8da663f928a79bf31517008b9dc3f7a82e"
429
429
  },
430
430
  {
431
431
  "path": "how-to-color.md",
@@ -434,8 +434,8 @@
434
434
  },
435
435
  {
436
436
  "path": "how-to-host.md",
437
- "size": 2647,
438
- "hash": "sha256:bdab3b08e77adaabcd582dff4b64838e0723ade66d5c9416be9111f9dc9b49cd"
437
+ "size": 2856,
438
+ "hash": "sha256:05187f27fb791bc18c613354635d5d68a044454c166dfce0925116adca3a6cba"
439
439
  },
440
440
  {
441
441
  "path": "how-to-page-layout.md",
@@ -464,8 +464,8 @@
464
464
  },
465
465
  {
466
466
  "path": "recipe-dashboard-overview.md",
467
- "size": 4708,
468
- "hash": "sha256:a6a581ca179595535d9d3df0469d362d8e7175e5595bea3222a32733c66c4615"
467
+ "size": 4857,
468
+ "hash": "sha256:4fe4573d6eb09730c1737875c835ca9b939cd3d8e8587ecae09ba7331b2ff8d7"
469
469
  },
470
470
  {
471
471
  "path": "recipe-data-table-page.md",
@@ -539,8 +539,8 @@
539
539
  },
540
540
  {
541
541
  "path": "recipe-stat-callouts.md",
542
- "size": 4017,
543
- "hash": "sha256:fb3857880f691fd016cb1414ee788e3a9fdfc49f7e61376442965aa018896f6e"
542
+ "size": 4207,
543
+ "hash": "sha256:ef84f56f302f623b990dff4e5051256642a41cf2dabab0e102ddf030bff4f61c"
544
544
  },
545
545
  {
546
546
  "path": "recipe-states.md",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uxelle/skills",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "description": "uxElle generate-UI agent skills: component API reference and design harness, as versioned markdown with a machine-readable manifest",
5
5
  "type": "module",
6
6
  "repository": {
@@ -2,7 +2,7 @@
2
2
 
3
3
  Import: `import { Icon } from "@uxelle/components";`
4
4
 
5
- Renders Material Symbols icons.
5
+ Inline Material Symbols Sharp pictogram.
6
6
 
7
7
  ## Props
8
8
 
@@ -10,13 +10,13 @@ Renders Material Symbols icons.
10
10
 
11
11
  | Prop | Type | Default | Description |
12
12
  |------|------|---------|-------------|
13
- | iconName | `string` | `"add"` | Material Symbols ligature name to render, e.g. `home`, `close`, or `settings`. Names outside that set render as literal text. |
13
+ | iconName | `string` | `"add"` | Material Symbols Sharp name to render, e.g. `home`, `close`, or `settings`. GPF chrome names always resolve. Any other name needs `import "@uxelle/icons/register"` (or `registerIcons`) before render. Unknown names render no glyph (an empty span — a visual-only miss that a11y tests will not catch). Type names with `UxelleIconName` from `@uxelle/icons` to catch typos at compile time. |
14
14
  | size | `number` | `24` | Glyph size in pixels, applied as an inline `font-size`. Valid range is 8–128 and out-of-range values warn in development; leaving it at the default emits no inline size, so CSS or a parent token can drive it. |
15
15
  | style | `CSSProperties` | `—` | Inline styles merged onto the icon `span`, applied after the `size`-derived `font-size` — a `fontSize` here therefore overrides `size`. |
16
16
  | "aria-label" | `string` | `""` | Accessible name for an icon that carries meaning. Setting it also suppresses the automatic `aria-hidden`, so the icon becomes visible to assistive technology. |
17
17
  | "aria-hidden" | `"auto" \| "true" \| "false"` | `undefined` | Explicitly includes or excludes the icon from the accessibility tree. Leave it unset for the default: hidden when there is no `aria-label`, exposed once there is one. |
18
18
  | role | `string` | `""` | Role placed on the icon `span`. Pair `img` with `aria-label` when the glyph is meaningful content; leave it unset for decorative icons. |
19
- | variant | `"sharpUnfilled" \| "sharpFilled"` | `"sharpUnfilled"` | Glyph weight: `sharpFilled` renders the solid form through font-variation settings, `sharpUnfilled` the outlined form. |
19
+ | variant | `"sharpUnfilled" \| "sharpFilled"` | `"sharpUnfilled"` | Glyph weight: `sharpFilled` renders the solid SVG, `sharpUnfilled` the outlined form. |
20
20
 
21
21
  <!-- prettier-ignore-end -->
22
22
 
@@ -40,4 +40,4 @@ Renders Material Symbols icons.
40
40
 
41
41
  ## Notes
42
42
 
43
- - Uses the material-symbols sharp font; updates instantly when `iconName` changes. By default, icons are hidden from screen readers (`aria-hidden="true"`) as they are typically decorative. Provide an `aria-label` when the icon conveys meaningful information.
43
+ - Looks up a Sharp SVG from `iconName` and `variant`. `@uxelle/components` ships the GPF chrome set; register the full catalog with `import "@uxelle/icons/register"` or **registerIcons** before the first render. Unknown names render no glyph and warn in development — treat that as an authoring error, not UI copy. Production is silent: an icon-only control with `aria-label` still has an accessible name, so this is a visual-only failure that a11y tests will not catch. Import `@uxelle/icons/register` in the host, or type names with `UxelleIconName` from `@uxelle/icons`. By default, icons are hidden from screen readers (`aria-hidden="true"`) as they are typically decorative. Provide an `aria-label` when the icon conveys meaningful information.
@@ -10,9 +10,9 @@ App chrome for a vertical side rail, or a compact top bar on small layouts.
10
10
 
11
11
  | Prop | Type | Default | Description |
12
12
  |------|------|---------|-------------|
13
- | expanded | `boolean` | `—` | Controlled rail width: wide labeled rail when true, icon-only rail when false. Pass with `onExpandedChange`; omit to let `defaultExpanded` and the built-in toggle own the width. Ignored when the mobile layout is shown. |
13
+ | expanded | `boolean` | `—` | Controlled rail width: wide labeled rail when true, icon-only rail when false. Pass with `onExpandedChange`; omit to let `defaultExpanded` and the built-in toggle own the width. Ignored when the mobile layout is shown. Collapsing hides open nested destination lists; accordion parents of the current nested page show the selected appearance. |
14
14
  | defaultExpanded | `boolean` | `true` | Whether the rail starts wide and labeled (true) or icon-only (false) when `expanded` is omitted. The built-in toggle owns the width after mount. |
15
- | onExpandedChange | `(expanded: boolean) => void` | `—` | Called with the next width state each time the expand/collapse control is activated. Required to track the width when `expanded` is set. |
15
+ | onExpandedChange | `(expanded: boolean) => void` | `—` | Called with the next width state each time the expand/collapse control is activated. Required to track the width when `expanded` is set. Collapsing hides open nested destination lists on accordion rows. |
16
16
  | mobile | `boolean` | `—` | Top-bar chrome when true; side rail when false. SSR hosts must pass `true` or `false` so server and client markup match. Omit only in client-only surfaces to follow the theme mobile range (`--uxl-theme-layout-mobile-screen-width-max`). The menu opens a right sheet with the same center and bottom slots as the rail. |
17
17
  | logoSlot | `boolean` | `true` | Renders the logo region on the expanded rail and the mobile top bar. Set `false` to leave it empty; the collapsed rail never shows the logo. |
18
18
  | logoSlotContent | `ReactNode` | `undefined` | Content for the logo region, typically a decorative **Logo**. Rendered only when `logoSlot` is true; omit for a neutral placeholder block. |
@@ -45,7 +45,7 @@ Extends `HTMLAttributes` — supports standard HTML attributes.
45
45
  ## Notes
46
46
 
47
47
  - **Rail**: Logo, expand/collapse control, `centerSlotContent` (`NavigationSideGroup` rows), and `bottomSlotContent`. Renders an `<aside>`. Groups remain named `<nav>` landmarks — do not wrap this chrome in another `nav`.
48
- - **Width**: Set `expanded` for the labeled rail and `expanded={false}` for the icon-only rail. Hosts own when to pass those values (tablet-and-up). This component does not hard-code those widths.
48
+ - **Width**: Set `expanded` for the labeled rail and `expanded={false}` for the icon-only rail. Hosts own when to pass those values (tablet-and-up). This component does not hard-code those widths. Collapsing hides any open accordion nested lists (focus moves to the parent if a nested row had it) and shows the selected appearance on an accordion parent when a nested row is the current page. Expanding restores that open state unless the flyout was dismissed while collapsed.
49
49
  - **Mobile**: Pass `mobile` (`true` or `false`) in SSR so the first paint matches hydration. The server snapshot for an omitted `mobile` is always the rail, so a phone viewport would swap to the top bar on the client. Omit `mobile` only in client-only surfaces (for example Storybook) to follow the theme mobile range (`--uxl-theme-layout-mobile-screen-width-max`). When the mobile layout is shown, the menu control opens a right `Sheet` (no visible title, labeled Menu for assistive tech) with the same `centerSlotContent` and `bottomSlotContent` as the rail (labeled rows, not the collapsed icon rail). The logo stays on the top bar. Use `menuOpen` / `onMenuOpenChange` to control the sheet (for example to close it after navigation). `onMenuClick` still fires on the menu control.
50
50
  - **Slots**: Omit a slot to fill demo content. Pass `false` to leave a region empty. `logoSlot={false}` hides the logo on the expanded rail and the mobile bar.
51
51
  - **Bottom slot**: Typical account rows use `List` + `ListItem`. List item text stays on one line during the rail width transition (same as destination labels) so it is revealed horizontally instead of wrapping.
@@ -17,10 +17,10 @@ Destination row for a vertical side navigation rail. Shows a leading icon, label
17
17
  | trailingSlotContent | `ReactNode` | `undefined` | Content for the trailing region, typically a `LabelBadge`, short text, or a decorative icon. Rendered only when `trailingSlot` is true, and it must stay non-interactive because it sits inside the row's own link or button. |
18
18
  | subNavigationItemsSlot | `ReactNode` | `undefined` | Nested `NavigationSideSubItem` rows revealed when `accordion` is true; takes precedence over `children`. On the collapsed rail they are re-rendered as a `List` of `ListItem`s inside the flyout. |
19
19
  | children | `ReactNode` | `undefined` | Nested `NavigationSideSubItem` rows revealed when `accordion` is true, used when `subNavigationItemsSlot` is omitted. Not the row label — that is `label`. On the collapsed rail they become a `List` of `ListItem`s in the flyout. |
20
- | activated | `boolean` | `false` | Marks this row as the current page: filled surface plus a leading indicator bar. Destination rows also get `aria-current="page"`; accordion parents do not, so set it on the matching child instead. |
20
+ | activated | `boolean` | `false` | Marks this row as the current page: filled surface plus a leading indicator bar. Destination rows also get `aria-current="page"`; accordion parents do not, so set it on the matching child instead. When the rail is collapsed, an accordion parent also shows this selected appearance if any nested `NavigationSideSubItem` is activated (pass the sub item directly, not wrapped in a custom component). |
21
21
  | navCollapsed | `boolean` | `false` | Icon-only rail layout: hides the label, the trailing slot, and the inline accordion panel, and shows the label in a tooltip on hover and focus. Inherited from the enclosing `NavigationSideGroup` when omitted. |
22
22
  | accordion | `boolean` | `false` | Turns the row into an expandable parent with a trailing chevron that reveals its nested rows. The control is always a button, so `href` is ignored. |
23
- | open | `boolean` | `undefined` | Controlled expanded state of the accordion panel (or of the collapsed-rail flyout). Pass with `onOpenChange`; omit to use `defaultOpen` instead. |
23
+ | open | `boolean` | `undefined` | Controlled expanded state of the accordion panel (or of the collapsed-rail flyout). Pass with `onOpenChange`; omit to use `defaultOpen` instead. Collapsing the rail hides an open nested list even if this stays true; pass `true` again (or let the user open the flyout) to show it while collapsed. |
24
24
  | defaultOpen | `boolean` | `false` | Whether the accordion panel starts expanded when `open` is omitted. |
25
25
  | onOpenChange | `(open: boolean) => void` | `undefined` | Called with the next expanded state each time the accordion parent toggles. |
26
26
  | notificationBadge | `boolean` | `false` | Overlays an unread dot on the leading icon. The dot itself is decorative; the state is folded into the control name as `"{label}, notifications"`. |
@@ -48,8 +48,8 @@ Destination row for a vertical side navigation rail. Shows a leading icon, label
48
48
  - Do not use as a top or header nav link (`NavLink`). Nested accordion destinations use `NavigationSideSubItem`.
49
49
  - Compose inside `NavigationSideGroup` (a `<nav>` list). This row is a list item; other HTML attributes (`aria-*`, `data-*`, `id`) go to the interactive control, and `className` is on the row wrapper.
50
50
  - Renders as an anchor when `href` is set; otherwise a button. Accordion parents are **not** destinations: `href` is ignored and the control is always a button. Put a section landing page in a `NavigationSideSubItem` child instead.
51
- - Set `activated` for the current page — fill and leading indicator. Destinations also set `aria-current="page"`; accordion parents do not. When a child is the current page, set `activated` on that child, not only on the parent.
52
- - Set `accordion` for a parent that reveals `subNavigationItemsSlot` (or `children`): `NavigationSideSubItem` rows (`href` for destinations). Omit `open` for uncontrolled (`defaultOpen` + `onOpenChange`). The trigger is a button with `aria-expanded` / `aria-controls`; the nested list animates open and closed (inert and `aria-hidden` when collapsed). When `navCollapsed` is also true, those rows open in a disclosure flyout (`Menu` `pattern="disclosure"`, `direction="Right Top"`) as a `List` of interactive `ListItem`s. Keyboard: Enter/Space toggles; Tab moves through destinations; Tab-out and Escape close and return focus to the trigger.
51
+ - Set `activated` for the current page — fill and leading indicator. Destinations also set `aria-current="page"`; accordion parents do not. When a child is the current page, set `activated` on that child, not only on the parent. Collapsing the rail still shows the selected appearance on that accordion parent so the section remains visible on the icon-only rail.
52
+ - Set `accordion` for a parent that reveals `subNavigationItemsSlot` (or `children`): `NavigationSideSubItem` rows (`href` for destinations). Omit `open` for uncontrolled (`defaultOpen` + `onOpenChange`). The trigger is a button with `aria-expanded` / `aria-controls`; the nested list animates open and closed (inert and `aria-hidden` when collapsed). Collapsing the rail hides any open nested list (and moves keyboard focus to the parent if a nested row had it). Expanding restores that open state unless the flyout was dismissed while collapsed. Opening a disclosure flyout from the collapsed parent is still available. When `navCollapsed` is also true, those rows open in a disclosure flyout (`Menu` `pattern="disclosure"`, `direction="Right Top"`) as a `List` of interactive `ListItem`s. Keyboard: Enter/Space toggles; Tab moves through destinations; Tab-out and Escape close and return focus to the trigger.
53
53
  - The leading icon is always shown. Group collapsed layout with `NavigationSideGroup` (`collapsed`) or set `navCollapsed`.
54
54
  - When `navCollapsed`, the visible label is hidden; pass `label` (or `aria-label`) for the accessible name. A tooltip shows the label on hover and focus.
55
55
  - When `notificationBadge` is true, the dot stays decorative; unread state is included in the control name (`"{label}, notifications"`). Override with `aria-label` when you need a different name.
@@ -14,7 +14,7 @@ Nested destination row under an accordion side-nav parent.
14
14
  | leadingIcon | `boolean` | `true` | Renders the leading icon before the label. Set `false` for text-only nested rows, which is the usual look under an accordion parent. |
15
15
  | leadingIconName | `string` | `"outbound"` | Material Symbol name for the leading icon. Ignored when `leadingIcon` is false. |
16
16
  | iconVariant | `"sharpUnfilled" \| "sharpFilled"` | `"sharpUnfilled"` | Fill style for the leading icon: `sharpUnfilled` for the outline glyph, `sharpFilled` for the solid one. |
17
- | activated | `boolean` | `false` | Marks this row as the current page: filled surface, leading indicator bar, and `aria-current="page"`. Set it here rather than on the accordion parent. |
17
+ | activated | `boolean` | `false` | Marks this row as the current page: filled surface, leading indicator bar, and `aria-current="page"`. Set it here rather than on the accordion parent. When the rail is collapsed, that parent also shows the selected appearance. |
18
18
  | href | `string` | `undefined` | Destination URL, which renders the row as an anchor instead of a button. Omit for a button that only reports clicks through `onClick`. |
19
19
  | className | `string` | `—` | |
20
20
 
@@ -34,6 +34,6 @@ Nested destination row under an accordion side-nav parent.
34
34
 
35
35
  - Use as a child of an accordion `NavigationSideItem`, not as a top-level rail row (`NavigationSideItem`). Compose inside `NavigationSideGroup` so this row is a list item in the side `nav`.
36
36
  - Renders as an anchor when `href` is set; otherwise a button. `className` is on the row wrapper; other HTML attributes (`aria-*`, `data-*`, `id`) go to the interactive control.
37
- - Set `activated` for the current page — fill, leading indicator, and `aria-current="page"`.
37
+ - Set `activated` for the current page — fill, leading indicator, and `aria-current="page"`. When the rail is collapsed, the accordion parent also shows the selected appearance so the section remains visible on the icon-only rail.
38
38
  - Set `leadingIcon={false}` to hide the icon.
39
- - When the parent accordion is collapsed (`navCollapsed`), this row is presented as a `ListItem` in the disclosure flyout (a list of links, not a menu).
39
+ - When the parent accordion is collapsed (`navCollapsed`), this row is presented as a `ListItem` in the disclosure flyout (a list of links, not a menu). Collapsing the rail hides an open nested list; the parent can still open the flyout.
@@ -221,7 +221,12 @@ Use `List` + `ListItem` for menus and lists. Prefer `centerText` / `bottomText`
221
221
  Prefer **`ListControls`** when each row has title/description plus an embedded trailing control (settings and exclusive choices). Page layout: harness [recipe-settings-page.md](../uxelle-design-harness/recipe-settings-page.md). Set **`controlType`** to **`switch`**, **`checkbox`**, or **`radio`**. Same selection roles as **`RadioGroup`** / **`CheckboxGroup`** for checkbox/radio, list layout instead of stacked form options. Use **`interactive`** **`ListItem`** rows with **`embedded`** Switch / Checkbox / Radio in **`trailing`**. Set **`dense`** on **`ListControls`** (or per **`ListItem`**) for compact row padding.
222
222
 
223
223
  ### Icon variants
224
- Icons use `sharpFilled` or `sharpUnfilled`. Default is `sharpUnfilled`.
224
+ Icons use `sharpFilled` or `sharpUnfilled`. Default is `sharpUnfilled`. GPF chrome
225
+ names always resolve from `@uxelle/components`. Any other Material Symbol requires
226
+ `import "@uxelle/icons/register"` in the host (`@uxelle/a2ui` already does this).
227
+ Unknown names render no glyph (empty span, production-silent) — a visual-only miss
228
+ that a11y tests will not catch on icon-only buttons that already have `aria-label`.
229
+ Type product names with `UxelleIconName` from `@uxelle/icons`.
225
230
 
226
231
  ### Theme tokens
227
232
  Generated app CSS uses `--uxl-color-switcher-*` and layout/page-chrome tokens. Do not use `--uxl-component-*` in app CSS (those tokens belong inside components). Focus rings on catalog controls already use `--uxl-color-switcher-interactive-icon`.
@@ -10,11 +10,11 @@ Card-styled readout for a headline figure with a label, optional qualifier, info
10
10
 
11
11
  | Prop | Type | Default | Description |
12
12
  |------|------|---------|-------------|
13
- | order | `"labelFirst" \| "valueFirst"` | `"labelFirst" as StatTileOrder` | Vertical order of the label row and the value row. |
14
- | label | `ReactNode` | `—` | Label in the label row. Occupies remaining width beside the info control. Pass a `string` (or `number`) to render with the default label typography (`Text type="Body Medium"`), or pass a `ReactNode` for full control. When you pass a `ReactNode` you own its accessibility: use meaningful text (not color or icon alone) and keep it non-interactive so it can name the group. |
15
- | value | `ReactNode` | `—` | Headline figure in the value row. Pass a `string` (or `number`) to render with the default value typography (`Text type="Display Medium"` as `<p>`), or pass a `ReactNode` for full control. A library `Text` node is rendered as `<p>` so a grid of StatTiles does not become a heading outline. When you pass any other `ReactNode`, make sure the figure reads as text and is not conveyed through color or an icon alone (WCAG 1.4.1). |
13
+ | order | `"labelFirst" \| "valueFirst"` | `"labelFirst"` | Vertical order of the label row and the value row. |
14
+ | label | `ReactNode` | `—` | Label in the label row. Occupies remaining width beside the info control and stays on one line: overflow ellipsizes. Hover or focus a truncated label to read the full name in a tooltip. A string (or number) is wrapped in **Text** with `type="Body Medium"`; pass a `Text` node for a different type (truncation is still applied). When you pass a `Text` or other `ReactNode` you own its accessibility: use meaningful text (not color or icon alone) and keep it non-interactive so it can name the group. |
15
+ | value | `ReactNode` | `—` | Headline figure in the value row. A string (or number) is wrapped in **Text** with `type="Display Small"` as `<p>`; pass a `Text` node for a different type. A `Text` keeps its `type` and is rendered as `<p>` so a grid of StatTiles does not become a heading outline. Any other `ReactNode` is allowed; make sure the figure reads as text and is not conveyed through color or an icon alone (WCAG 1.4.1). |
16
16
  | qualifier | `boolean` | `true` | When true, allows the qualifier to render when content is present. |
17
- | qualifierContent | `ReactNode` | `—` | Qualifier beside the value. Pass a `string` (or `number`) to render with the default qualifier typography (`Text type="Body Medium"`), or a `ReactNode` for full control. Only rendered when `qualifier` is true and content is present. When you pass a `ReactNode`, ensure it reads as text; do not rely on color alone to distinguish comparison periods. |
17
+ | qualifierContent | `ReactNode` | `—` | Qualifier beside the value. A string (or number) is wrapped in **Text** with `type="Body Medium"`; pass a `Text` node for a different type. Only rendered when `qualifier` is true and content is present. When you pass a `Text` or other `ReactNode`, ensure it reads as text; do not rely on color alone to distinguish comparison periods. |
18
18
  | tooltipSlot | `boolean` | `true` | When true, allows the tooltip slot to render when content is present. |
19
19
  | tooltipContent | `ReactNode` | `—` | Info control at the end of the label row (typically `Tooltip` wrapping `IconButton`). Only rendered when `tooltipSlot` is true and content is present. Icon-only triggers within this slot must carry an accessible name via `aria-label` on the interactive element (e.g. `<IconButton aria-label="More information" />`). |
20
20
  | trailingSlot | `boolean` | `false` | When true, allows the trailing region to render when content is present. The slot accepts any content. |
@@ -70,7 +70,7 @@ Card-styled readout for a headline figure with a label, optional qualifier, info
70
70
  // ReactNode override — caller owns accessibility (semantic element, text)
71
71
  <StatTile
72
72
  label={<Text type="Body Medium" width={false}>Fill rate</Text>}
73
- value={<Text type="Display Medium" as="p">94%</Text>}
73
+ value={<Text type="Display Small" as="p">94%</Text>}
74
74
  />
75
75
  ```
76
76
 
@@ -78,7 +78,7 @@ Card-styled readout for a headline figure with a label, optional qualifier, info
78
78
  // Polling dashboard — aria-live on the value announces only the change
79
79
  <StatTile
80
80
  label="Active sessions"
81
- value={<Text type="Display Medium" as="p" aria-live="polite">{count}</Text>}
81
+ value={<Text type="Display Small" as="p" aria-live="polite">{count}</Text>}
82
82
  />
83
83
  ```
84
84
 
@@ -111,40 +111,11 @@ Card-styled readout for a headline figure with a label, optional qualifier, info
111
111
 
112
112
  ## Notes
113
113
 
114
- - Pass a `string` (or `number`) to `label`, `value`, and `qualifierContent` to
115
- - render with each region's default typography — no `Text` wrapper needed. Pass
116
- - a `ReactNode` instead when you need full control (e.g. truncation, a custom
117
- - element); in that case you own its accessibility (see each prop). A library
118
- - `Text` passed as `value` is rendered as `<p>`.
119
- - Pass a library `Tooltip` (typically wrapping `IconButton`) through
120
- - `tooltipContent`. Toggle regions with `qualifier`, `tooltipSlot`, and
121
- - `trailingSlot`; empty content is not rendered. `trailingSlotContent` is an
122
- - open slot — any content is allowed. Surface, elevation, radius, and
123
- - padding come from `Card`. StatTile is a standalone tile: place it next to
124
- - other StatTiles in a grid. Do not wrap it in `Card`. A figure inside a chart
125
- - card or table cell is a different surface — compose `Text` there.
126
- - **Accessibility.** The root is a `role="group"` region. It is named
127
- - automatically from `label` (via `aria-labelledby`), so the group is announced
128
- - with context in both orders — including `"valueFirst"`, where the value
129
- - precedes the label in the DOM. Supply your own `aria-label` or
130
- - `aria-labelledby` to override the derived name; this is **required** when
131
- - there is no `label` (e.g. `aria-label="Median first response time, 1 day 12
132
- - hours"`) so the figure is not announced without context. In development,
133
- - StatTile warns when content is present but the group has no name. The trailing
134
- - slot is open, so its accessibility is the caller's responsibility — see
135
- - `trailingSlotContent`.
136
- - **Navigation.** Do not put `onClick` on StatTile — the root is a named group,
137
- - not a control, and a wrapped link would nest interactives when a tooltip is
138
- - present. Put the destination in `trailingSlotContent` as a `Link` or
139
- - `IconButton`.
114
+ - `label`, `value`, and `qualifierContent` each accept a string (or number) or a **Text** node. A string is wrapped in that region's default `Text` type — no wrapper needed. Pass a `Text` when you need a different `type` or a live region. A `Text` passed as `value` keeps its `type` and is rendered as `<p>`. The label always truncates to one line so a row of tiles stays scannable; hover or focus a truncated label to read the full name (`Text` Body Small). This overflow tooltip is separate from `tooltipContent` (the info control).
115
+ - Pass a library `Tooltip` (typically wrapping `IconButton`) through `tooltipContent`. Toggle regions with `qualifier`, `tooltipSlot`, and `trailingSlot`; empty content is not rendered. `trailingSlotContent` is an open slot — any content is allowed. Surface, elevation, radius, and padding come from `Card`. StatTile is a standalone tile: place it next to other StatTiles in a grid. Do not wrap it in `Card`. A figure inside a chart card or table cell is a different surface — compose `Text` there.
116
+ - **Accessibility.** The root is a `role="group"` region. It is named automatically from `label` (via `aria-labelledby`), so the group is announced with context in both orders — including `"valueFirst"`, where the value precedes the label in the DOM. Supply your own `aria-label` or `aria-labelledby` to override the derived name; this is **required** when there is no `label` (e.g. `aria-label="Median first response time, 1 day 12 hours"`) so the figure is not announced without context. In development, StatTile warns when content is present but the group has no name. The trailing slot is open, so its accessibility is the caller's responsibility — see `trailingSlotContent`.
117
+ - **Navigation.** Do not put `onClick` on StatTile — the root is a named group, not a control, and a wrapped link would nest interactives when a tooltip is present. Put the destination in `trailingSlotContent` as a `Link` or `IconButton`.
140
118
  - Set `order` to `"valueFirst"` to put the value row above the label row.
141
- - The root fills its parent by default (`fullWidth`). Set `fullWidth={false}`
142
- - to hug contents. Long label, value, and qualifier copy wrap inside the tile.
143
- - While data is loading, replace the tile with a `Skeleton` sized to the card
144
- - (full-width rectangle) inside `SkeletonGroup`. Do not pass a skeleton as
145
- - `value`.
146
- - For polling dashboards where the value updates in place, prefer putting
147
- - `aria-live="polite"` on the **value** node so only the changing figure is
148
- - announced (a root-level `aria-live` also works but re-announces the whole
149
- - group). When using a `string` `value`, wrap it yourself to place the live
150
- - region precisely.
119
+ - The root fills its parent by default (`fullWidth`). Set `fullWidth={false}` to hug contents. Long value and qualifier copy wrap inside the tile; the label ellipsizes instead of wrapping.
120
+ - While data is loading, replace the tile with a `Skeleton` sized to the card (full-width rectangle) inside `SkeletonGroup`. Do not pass a skeleton as `value`.
121
+ - For polling dashboards where the value updates in place, prefer putting `aria-live="polite"` on the **value** node so only the changing figure is announced (a root-level `aria-live` also works but re-announces the whole group). When using a `string` `value`, wrap it yourself to place the live region precisely.
@@ -12,10 +12,11 @@ Peer dependencies: React 18 or 19.
12
12
  npm install @uxelle/components @uxelle/themes
13
13
  ```
14
14
 
15
+ Import the two stylesheets:
16
+
15
17
  ```tsx
16
- import "@uxelle/components/styles.css";
17
- import "@uxelle/components/index.css";
18
- import "@uxelle/themes/themes/open-source/open-source.css";
18
+ import "@uxelle/components/base.css";
19
+ import "@uxelle/themes/open-source.css";
19
20
 
20
21
  import { Button } from "@uxelle/components";
21
22
 
@@ -24,22 +25,52 @@ function App() {
24
25
  }
25
26
  ```
26
27
 
27
- `styles.css` loads Material Symbols and the global baseline. `index.css` ships the bundled component rules. Load both stylesheets in the host app — the package JS does not import CSS automatically.
28
+ Then set the theme attributes on `<html>`:
29
+
30
+ ```html
31
+ <html data-open-source="light" data-color-switcher="default"></html>
32
+ ```
33
+
34
+ Both attributes are required. Every `--uxl-*` token is declared under
35
+ `[data-open-source="light"]` or `[data-open-source="dark"]`, so components render
36
+ unstyled without them. See [Themes](#themes) to switch modes at runtime.
37
+
38
+ `base.css` is the single component stylesheet. `@uxelle/components/styles.css`
39
+ (global baseline) and `@uxelle/components/index.css` (component rules)
40
+ stay exported if you need to load them separately.
28
41
 
29
42
  ### Next.js / SSR
30
43
 
31
- - Import the three stylesheets in `app/layout.tsx` (or a top-level provider). JS side effects will not style App Router.
44
+ - Import both stylesheets in `app/layout.tsx` (or a top-level provider). JS side effects will not style App Router.
32
45
  - Published `@uxelle/components` JS is prefixed with `"use client"`. Importing any component places that file in the client graph.
33
46
  - Pass `mobile={true}` or `mobile={false}` on `NavigationSide` so server and client markup match. Omit `mobile` only in client-only surfaces.
34
47
 
35
48
  Optional: for an agent-driven UI surface, also install `@uxelle/a2ui`.
36
49
 
50
+ ### Icons
51
+
52
+ `@uxelle/components` ships GPF chrome glyphs (`close`, `chevron_*`, `check`, and other names the library itself uses). For any other Material Symbol Sharp name, install `@uxelle/icons` and register once before the first `Icon` render:
53
+
54
+ ```bash
55
+ npm install @uxelle/icons
56
+ ```
57
+
58
+ ```tsx
59
+ import "@uxelle/icons/register";
60
+ ```
61
+
62
+ `@uxelle/a2ui` registers the full catalog when you import it. Unknown names render no glyph (an empty span). Development warns; production is silent, so an icon-only control with `aria-label` can still pass a11y tests while showing nothing. Register `@uxelle/icons` before the first render, or type names with `UxelleIconName` from `@uxelle/icons`.
63
+
37
64
  ## Themes
38
65
 
39
- Theme CSS is published as `@uxelle/themes` and generated from design tokens. Apply a theme by importing one CSS file. Set `data-open-source="light"` or `data-open-source="dark"` on `<html>`, plus `data-color-switcher="default"`. Optional: wrap the app with `UXelleThemeProvider` so those attributes stay in sync.
66
+ Theme CSS is published as `@uxelle/themes` and generated from design tokens. Quick Start covers the import and the two required attributes; this section covers changing them.
67
+
68
+ - `data-open-source` — `"light"` or `"dark"`
69
+ - `data-color-switcher` — `"default"`, `"default-subtle"`, `"success"`, `"warning"`, `"danger"`, or `"info"`
70
+
71
+ To drive both from React instead of hardcoding them, wrap the app with `UXelleThemeProvider`, which sets the attributes on `document.documentElement`:
40
72
 
41
73
  ```tsx
42
- import "@uxelle/themes/themes/open-source/open-source.css";
43
74
  import { UXelleThemeProvider } from "@uxelle/themes/react";
44
75
 
45
76
  function AppShell({ children }: { children: React.ReactNode }) {
@@ -13,6 +13,10 @@ Hosts inject `uxelleCatalogSchema` (and optionally a recipe's messages) into the
13
13
  agent prompt and render the result. Do not open package source to "discover" props —
14
14
  use the catalog schema the host provides.
15
15
 
16
+ Importing `@uxelle/a2ui` registers the full Material Symbols Sharp catalog. React
17
+ hosts that do not use A2UI must `import "@uxelle/icons/register"` themselves if
18
+ generated UI uses icon names beyond GPF chrome.
19
+
16
20
  ## Hard constraints
17
21
 
18
22
  - **Catalog adapters only.** No raw HTML, no custom components.
@@ -70,6 +74,11 @@ chips; a chip wider than its container ellipsizes so dismiss stays visible.
70
74
  Exclusive `A2uiChoiceChip` selection is one checked value in host state (toggles,
71
75
  not radios). Use `A2uiRadioGroup` or `A2uiSegmentedControl` for radio semantics.
72
76
 
77
+ `A2uiStatTile` labels stay on one line and ellipsize; hover or focus a truncated
78
+ label to read the full name. Pass `label` as a string — do not wrap it in
79
+ `A2uiText` or set truncation. Value and qualifier copy wrap. The overflow tooltip
80
+ is automatic and separate from `tooltipContent`.
81
+
73
82
  Known gaps — substitute rather than invent:
74
83
 
75
84
  | React | A2UI |
@@ -26,7 +26,7 @@ This file covers **composition**: landmarks, heading ranks, names, and live regi
26
26
  - `aria-describedby` — extra help, not the name (`fieldDescription`, instructions).
27
27
  - `aria-hidden="true"` — decorative / redundant graphics (default `Icon`).
28
28
  - `aria-live="polite"` — status that appears without moving focus. `assertive` only for urgent interruptions.
29
- - `aria-current="page"` — current item in nav (`NavLink`, or `activated` on `NavigationSideItem` / `NavigationSideSubItem` destinations — not accordion parents).
29
+ - `aria-current="page"` — current item in nav (`NavLink`, or `activated` on `NavigationSideItem` / `NavigationSideSubItem` destinations — not accordion parents). When the side rail is collapsed, an accordion parent of an activated nested row still shows the selected appearance so the current section remains visible on the icon-only rail.
30
30
  - `aria-expanded` — only on a real disclosure control, not on every section.
31
31
  - `aria-pressed` — toggle buttons (`IconButton` `activated`); `Switch` uses `checked` instead.
32
32
 
@@ -14,6 +14,10 @@ Load component and theme CSS as in
14
14
  [getting-started.md](../uxelle-components/getting-started.md). Do not set
15
15
  `font-family` in app CSS — `Text` uses theme tokens.
16
16
 
17
+ If generated UI uses Material Symbol names beyond GPF chrome (`home`, `mail`,
18
+ `campaign`, …), add `import "@uxelle/icons/register"` once at the app entry.
19
+ `@uxelle/a2ui` already registers the full catalog.
20
+
17
21
  On `<html>`:
18
22
 
19
23
  - Theme mode (host-owned): `data-open-source="light" | "dark"` (other themes use
@@ -32,7 +32,8 @@ sections
32
32
 
33
33
  KPI and card grids reflow with `auto-fit` / `minmax` (no JS); the header stacks and
34
34
  its actions go `fullWidth` below tablet; the primary `Table` scrolls horizontally
35
- when narrow. See [how-to-page-layout.md](how-to-page-layout.md#responsiveness).
35
+ when narrow. StatTile labels ellipsize on one line so a KPI row stays scannable.
36
+ See [how-to-page-layout.md](how-to-page-layout.md#responsiveness).
36
37
 
37
38
  ## Spacing
38
39
 
@@ -49,7 +50,8 @@ KPIs rather than a dense monitoring surface — but pick one mode and hold it.
49
50
  ## A11y
50
51
 
51
52
  One `main`, one `h1`. Name each region (KPIs `aria-label="Key metrics"`, the primary
52
- surface by its heading). KPI deltas pair color with a sign or word. Loading and empty
53
+ surface by its heading). KPI deltas pair color with a sign or word. Truncated KPI
54
+ labels keep the full name on the group; hover or focus reveals it. Loading and empty
53
55
  for the primary surface follow [states](recipe-states.md). See
54
56
  [how-to-accessibility.md](how-to-accessibility.md).
55
57
 
@@ -24,6 +24,8 @@ A small set of headline numbers — revenue, active users, uptime, "10k+ teams."
24
24
  - Do not put `onClick` on StatTile — put a `Link` or `IconButton` in
25
25
  `trailingSlotContent`.
26
26
  - Do not signal a delta with color alone — pair it with a sign or word.
27
+ - Do not wrap StatTile labels yourself. The label stays on one line and
28
+ ellipsizes; hover or focus a truncated label to read the full name.
27
29
 
28
30
  ## Regions
29
31
 
@@ -50,10 +52,10 @@ band on a `spacious` page it adds breathing with the **Band** step (`large-15`)
50
52
  ## A11y
51
53
 
52
54
  Each figure and its label read together. StatTile names the group from `label`
53
- automatically (including `valueFirst`). If there is no label, set `aria-label`
54
- that combines the figure and its meaning. If the trailing slot holds a delta,
55
- keep the meaning in text, not color alone. See
56
- [how-to-accessibility.md](how-to-accessibility.md).
55
+ automatically (including `valueFirst`), including when the visible label is
56
+ ellipsized. If there is no label, set `aria-label` that combines the figure and
57
+ its meaning. If the trailing slot holds a delta, keep the meaning in text, not
58
+ color alone. See [how-to-accessibility.md](how-to-accessibility.md).
57
59
 
58
60
  ## Color
59
61