@uxelle/skills 0.2.1-beta.0 → 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/README.md +3 -5
- package/dist/index.js +106 -96
- package/index.json +106 -96
- package/package.json +1 -1
- package/skills/uxelle-components/ChoiceChip.md +1 -1
- package/skills/uxelle-components/ChoiceChipGroup.md +3 -3
- package/skills/uxelle-components/FilterChip.md +2 -2
- package/skills/uxelle-components/FilterChipGroup.md +1 -1
- package/skills/uxelle-components/Hero.md +2 -2
- package/skills/uxelle-components/Icon.md +4 -4
- package/skills/uxelle-components/Image.md +10 -3
- package/skills/uxelle-components/MultiSelect.md +3 -2
- package/skills/uxelle-components/NavigationSide.md +3 -3
- package/skills/uxelle-components/NavigationSideItem.md +4 -4
- package/skills/uxelle-components/NavigationSideSubItem.md +3 -3
- package/skills/uxelle-components/SKILL.md +9 -2
- package/skills/uxelle-components/Select.md +2 -1
- package/skills/uxelle-components/StatTile.md +13 -42
- package/skills/uxelle-components/Table.md +7 -4
- package/skills/uxelle-components/getting-started.md +85 -0
- package/skills/uxelle-design-harness/SKILL.md +44 -20
- package/skills/uxelle-design-harness/a2ui.md +31 -2
- package/skills/uxelle-design-harness/density.md +138 -0
- package/skills/uxelle-design-harness/how-to-accessibility.md +3 -1
- package/skills/uxelle-design-harness/how-to-color.md +6 -3
- package/skills/uxelle-design-harness/how-to-host.md +8 -21
- package/skills/uxelle-design-harness/how-to-page-layout.md +51 -6
- package/skills/uxelle-design-harness/principles.md +10 -6
- package/skills/uxelle-design-harness/recipe-app-chrome.md +20 -16
- package/skills/uxelle-design-harness/recipe-card-grid.md +14 -5
- package/skills/uxelle-design-harness/recipe-cta-band.md +16 -6
- package/skills/uxelle-design-harness/recipe-dashboard-overview.md +21 -7
- package/skills/uxelle-design-harness/recipe-data-table-page.md +30 -137
- package/skills/uxelle-design-harness/recipe-feature-section.md +9 -7
- package/skills/uxelle-design-harness/recipe-footer.md +7 -4
- package/skills/uxelle-design-harness/recipe-form-section.md +2 -2
- package/skills/uxelle-design-harness/recipe-hero.md +35 -15
- package/skills/uxelle-design-harness/recipe-landing-page.md +27 -4
- package/skills/uxelle-design-harness/recipe-logo-wall.md +11 -9
- package/skills/uxelle-design-harness/recipe-multi-step-flow.md +5 -3
- package/skills/uxelle-design-harness/recipe-page-header.md +14 -4
- package/skills/uxelle-design-harness/recipe-page-shell.md +2 -2
- package/skills/uxelle-design-harness/recipe-pricing.md +5 -2
- package/skills/uxelle-design-harness/recipe-query-bar.md +21 -5
- package/skills/uxelle-design-harness/recipe-record-detail.md +5 -2
- package/skills/uxelle-design-harness/recipe-settings-page.md +3 -3
- package/skills/uxelle-design-harness/recipe-stat-callouts.md +14 -7
- package/skills/uxelle-design-harness/recipe-states.md +9 -4
- package/skills/uxelle-design-harness/recipe-summary-list.md +5 -3
- package/skills/uxelle-design-harness/recipe-template.md +6 -0
- package/skills/uxelle-design-harness/recipe-testimonial.md +5 -3
- package/skills/uxelle-design-harness/spacing-steps.md +17 -8
- package/skills/uxelle-design-harness/tokens.md +45 -4
|
@@ -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.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: uxelle-components
|
|
3
|
-
description: Reference for uxElle Generative Product Foundation (GPF) components. Use when generating UI with uxElle, implementing designs, building layouts, or when the user mentions uxElle components
|
|
3
|
+
description: Reference for uxElle Generative Product Foundation (GPF) components. Use when generating UI with uxElle, implementing designs, building layouts, installing or setting up a React or Next.js app with @uxelle/components, loading theme CSS, or when the user mentions uxElle components or enterprise UI.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# uxElle Components — Generative Workflow Reference
|
|
@@ -10,6 +10,7 @@ Reference for accurately using uxElle Generative Product Foundation (GPF) compon
|
|
|
10
10
|
## Context discipline
|
|
11
11
|
|
|
12
12
|
- Load this SKILL.md when working with uxElle components.
|
|
13
|
+
- New app or host without packages and CSS? Read [getting-started.md](getting-started.md) first, then [how-to-host.md](../uxelle-design-harness/how-to-host.md). Skip getting-started when the host already loads theme CSS, component CSS, and `data-*`.
|
|
13
14
|
- Load individual component files (e.g. Button.md, ListItem.md) only when you need that component's props or examples.
|
|
14
15
|
- Do not preload all component files.
|
|
15
16
|
|
|
@@ -220,7 +221,12 @@ Use `List` + `ListItem` for menus and lists. Prefer `centerText` / `bottomText`
|
|
|
220
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.
|
|
221
222
|
|
|
222
223
|
### Icon variants
|
|
223
|
-
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`.
|
|
224
230
|
|
|
225
231
|
### Theme tokens
|
|
226
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`.
|
|
@@ -236,6 +242,7 @@ For secondary nav or brand hero bands: `data-color-switcher="brand-2"` on second
|
|
|
236
242
|
|
|
237
243
|
## Additional Resources
|
|
238
244
|
|
|
245
|
+
- [Getting started](getting-started.md) — install `@uxelle/components` and `@uxelle/themes`, load CSS, theme provider, Next.js/SSR
|
|
239
246
|
- [Design harness](../uxelle-design-harness/SKILL.md) — principles, variables, page chrome, responsiveness, WCAG AA, recipes
|
|
240
247
|
- [Form field contract](../../docs/guides/form-field-contract.md) — HTML `value` / `checked` (not Figma `Selected` / `Activated`)
|
|
241
248
|
- [Design variables (`--uxl-*`)](../uxelle-design-harness/tokens.md) — spacing, radius, elevation, color switcher, type
|
|
@@ -34,6 +34,7 @@ Single-select dropdown with label, design tokens, optional `FieldMessage`, and l
|
|
|
34
34
|
| readOnly | `boolean` | `false` | Shows the current selection but blocks opening the listbox and hides the trailing chevron; the trigger stays focusable and reports `aria-readonly`. |
|
|
35
35
|
| required | `boolean` | `—` | Adds the asterisk to `Label` and `aria-required` on the trigger. Advisory only — no native constraint validation runs on the hidden input. |
|
|
36
36
|
| name | `string` | `—` | Form field name for the hidden input that posts the selected value. No input is rendered while nothing is selected, so an empty selection is never submitted. |
|
|
37
|
+
| onClick | `MouseEventHandler<HTMLDivElement>` | `—` | Fired on the field chrome. `currentTarget` is the trigger wrapper, not the combobox input. |
|
|
37
38
|
| disabled | `boolean` | `false` | Blocks interaction, removes the trigger from the tab order, and applies disabled styling; a `name` hidden input still submits the current value. |
|
|
38
39
|
|
|
39
40
|
<!-- prettier-ignore-end -->
|
|
@@ -51,5 +52,5 @@ Single-select dropdown with label, design tokens, optional `FieldMessage`, and l
|
|
|
51
52
|
- **Simple API** — pass `options` with `value` / `onChange` for controlled usage; optional `leadingIcon` / `leadingIconName`.
|
|
52
53
|
- **Composable API** — pass `children` to render custom `ListItem` rows in the listbox; pass `leading` for a custom leading slot. Each row should spread `listboxOptionProps()` plus selection handlers (see dev console when the list opens).
|
|
53
54
|
- **Accessibility**: Visible label uses `<Label htmlFor>`; the combobox name uses `aria-labelledby` on the label text only (not `fieldDescription`, which is linked via `aria-describedby`). `FieldMessage` uses `aria-describedby` (info) or `aria-errormessage` (error). Pass `id` for stable ids; use `aria-label` when `label={false}`.
|
|
54
|
-
- **Focus**: Tab focuses the
|
|
55
|
+
- **Focus**: The combobox is a readonly `<input>` (not a `<button>`) so Safari's default Tab cycle — text fields, not buttons — can land on it, and a click actually focuses the control. **Arrow** keys move the active option (`aria-activedescendant`) while open. **Enter** commits the active option; **Escape** closes the list. Options use `tabIndex={-1}` and are not separate tab stops.
|
|
55
56
|
- **Forms**: With `name`, a hidden input posts the selected value; when nothing is selected, no hidden input is rendered.
|
|
@@ -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"
|
|
14
|
-
| label | `ReactNode` | `—` | Label in the label row. Occupies remaining width beside the info control.
|
|
15
|
-
| value | `ReactNode` | `—` | Headline figure in the value row.
|
|
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.
|
|
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
|
|
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
|
|
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
|
-
-
|
|
115
|
-
-
|
|
116
|
-
- a `
|
|
117
|
-
-
|
|
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
|
-
-
|
|
143
|
-
-
|
|
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.
|
|
@@ -11,7 +11,7 @@ Composable table shell and semantic grid for tabular data. Columns support resiz
|
|
|
11
11
|
| Prop | Type | Default | Description |
|
|
12
12
|
|------|------|---------|-------------|
|
|
13
13
|
| topSlot | `ReactNode` | `—` | Band rendered above the table and outside its horizontal scrollport — typically a title, toolbar, filters, or search. Omitted entirely when unset. |
|
|
14
|
-
| bottomSlot | `ReactNode` | `—` | Band rendered below the table and outside its horizontal scrollport — typically
|
|
14
|
+
| bottomSlot | `ReactNode` | `—` | Band rendered below the table and outside its horizontal scrollport — typically **Pagination** or bulk actions. Omitted entirely when unset. |
|
|
15
15
|
| tableSlot | `ReactNode` | `—` | The table itself: one **TableGrid** with its head, body, rows, and cells, placed in the scroll region that drives the horizontal scroll hint. Same slot as `children`, and it wins when both are passed. |
|
|
16
16
|
| children | `ReactNode` | `—` | Alias for `tableSlot`, ignored when `tableSlot` is also passed. Compose one `TableGrid` (give it `aria-label` and `gridTemplateColumns`) holding a `TableHead` with a `TableRow` of `TableHeaderCell` children, then a `TableBody` with one `TableRow` of `TableCell` children per record — all exported from `@uxelle/components`. |
|
|
17
17
|
|
|
@@ -23,7 +23,7 @@ Extends `HTMLAttributes` — supports standard HTML attributes.
|
|
|
23
23
|
|
|
24
24
|
```tsx
|
|
25
25
|
const columns = [
|
|
26
|
-
{ flex: 0
|
|
26
|
+
{ flex: 0, minWidth: 48, maxWidth: 72 },
|
|
27
27
|
{ flex: 1, minWidth: 120 },
|
|
28
28
|
] as const;
|
|
29
29
|
|
|
@@ -44,12 +44,15 @@ function OrdersGrid() {
|
|
|
44
44
|
<OrdersGrid />
|
|
45
45
|
</TableColumnSizingProvider>
|
|
46
46
|
}
|
|
47
|
+
bottomSlot={
|
|
48
|
+
<Pagination totalItems={100} defaultItemsPerPage={10} aria-label="Orders pagination" />
|
|
49
|
+
}
|
|
47
50
|
/>
|
|
48
51
|
```
|
|
49
52
|
|
|
50
53
|
|
|
51
54
|
## Notes
|
|
52
55
|
|
|
53
|
-
- Optional top and bottom slots frame the table region;
|
|
54
|
-
- Horizontal scroll and the trailing-edge hint appear automatically when content overflows the container. Define column proportions with **TableColumnSizingProvider** and **useColumnGridTemplate**.
|
|
56
|
+
- Optional top and bottom slots frame the table region; pass **Pagination** in `bottomSlot` when the dataset is paged. A horizontal scroll hint appears when the grid is wider than its container. Fixed columns are implemented as sticky cells inside one `TableGrid`—not as a separate grid—so each row stays a single unit for assistive technology. Use **TableColumnPinProvider** or `pinned="start"` on **TableHeaderCell** and **TableCell** with **usePinnedStartOffsets** to keep leading columns visible during horizontal scroll.
|
|
57
|
+
- Horizontal scroll and the trailing-edge hint appear automatically when content overflows the container. Define column proportions with **TableColumnSizingProvider** and **useColumnGridTemplate**. Set `flex: 0` and `maxWidth` on compact columns (selection, icons) so leftover `fr` space does not stretch them on a wide viewport. `flex: 0` without a pixel `minWidth` or `maxWidth` sizes the track to content (`max-content`) instead of collapsing. `maxWidth` on a growing column (`flex > 0`) is a resize ceiling only. Cell copy wraps and the row height follows the tallest cell. Header titles stay on one line and ellipsize when the column is narrower than the name. Pass `truncation` on **Text** in a body cell only when that column must stay single-line. Resizing a column persists every column as a pixel track at the widths on screen, so siblings are not stretched or shrunk to fit the container. Call **useResetTableColumnWidths** to restore every column to its template.
|
|
55
58
|
- Layout is left-to-right only; right-to-left document direction is not supported yet.
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
<!-- Generated from README.md (getting-started region). Do not edit. -->
|
|
2
|
+
|
|
3
|
+
# Getting started
|
|
4
|
+
|
|
5
|
+
New app or host without uxElle packages and CSS? Read this file first, then set up page chrome in [how-to-host.md](../uxelle-design-harness/how-to-host.md). Component APIs live in [SKILL.md](SKILL.md). Optional A2UI runtime: [a2ui.md](../uxelle-design-harness/a2ui.md). Do not preload every component file.
|
|
6
|
+
|
|
7
|
+
## Quick Start
|
|
8
|
+
|
|
9
|
+
Peer dependencies: React 18 or 19.
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install @uxelle/components @uxelle/themes
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Import the two stylesheets:
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
import "@uxelle/components/base.css";
|
|
19
|
+
import "@uxelle/themes/open-source.css";
|
|
20
|
+
|
|
21
|
+
import { Button } from "@uxelle/components";
|
|
22
|
+
|
|
23
|
+
function App() {
|
|
24
|
+
return <Button emphasis="high">Get Started</Button>;
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
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.
|
|
41
|
+
|
|
42
|
+
### Next.js / SSR
|
|
43
|
+
|
|
44
|
+
- Import both stylesheets in `app/layout.tsx` (or a top-level provider). JS side effects will not style App Router.
|
|
45
|
+
- Published `@uxelle/components` JS is prefixed with `"use client"`. Importing any component places that file in the client graph.
|
|
46
|
+
- Pass `mobile={true}` or `mobile={false}` on `NavigationSide` so server and client markup match. Omit `mobile` only in client-only surfaces.
|
|
47
|
+
|
|
48
|
+
Optional: for an agent-driven UI surface, also install `@uxelle/a2ui`.
|
|
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
|
+
|
|
64
|
+
## Themes
|
|
65
|
+
|
|
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`:
|
|
72
|
+
|
|
73
|
+
```tsx
|
|
74
|
+
import { UXelleThemeProvider } from "@uxelle/themes/react";
|
|
75
|
+
|
|
76
|
+
function AppShell({ children }: { children: React.ReactNode }) {
|
|
77
|
+
return (
|
|
78
|
+
<UXelleThemeProvider theme="open-source" darkMode={false} colorSwitcher="default">
|
|
79
|
+
{children}
|
|
80
|
+
</UXelleThemeProvider>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Open Source uses **Open Sans** (self-hosted in the theme package). Do not set `font-family` in app CSS — `Text` uses theme tokens.
|
|
@@ -37,29 +37,38 @@ palette. The full mapping of principles to uxElle mechanisms lives in
|
|
|
37
37
|
1. Load this `SKILL.md`. The foundations below always apply.
|
|
38
38
|
2. Read the variable foundation once: [tokens.md](tokens.md) and
|
|
39
39
|
[spacing-steps.md](spacing-steps.md).
|
|
40
|
-
3.
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
4.
|
|
40
|
+
3. Pick the **density mode** for the experience — `compact` (data-dense product),
|
|
41
|
+
`standard` (task product, the default), `spacious` (marketing). One mode per
|
|
42
|
+
experience, chosen before any layout: [density.md](density.md).
|
|
43
|
+
4. Creating a **new** app/host? Install and load CSS from
|
|
44
|
+
[getting-started.md](../uxelle-components/getting-started.md), then the
|
|
45
|
+
shell in [how-to-host.md](how-to-host.md). Building a page body in an
|
|
46
|
+
existing host? [how-to-page-layout.md](how-to-page-layout.md) (layout
|
|
47
|
+
**and** responsiveness), [how-to-color.md](how-to-color.md), and
|
|
48
|
+
[how-to-accessibility.md](how-to-accessibility.md).
|
|
49
|
+
5. Prefer an existing component. Link its doc at point of use, e.g.
|
|
44
50
|
`Table` -> `../uxelle-components/Table.md`. Import from `@uxelle/components`.
|
|
45
|
-
|
|
51
|
+
6. Otherwise follow a recipe (see the index). If nothing fits, compose `Layout` +
|
|
46
52
|
`Text` + primitives on the grid.
|
|
47
|
-
|
|
48
|
-
|
|
53
|
+
7. Building a runtime surface? Read [a2ui.md](a2ui.md) and each recipe's **A2UI** note.
|
|
54
|
+
8. Before you finish, run the quality checklist below.
|
|
49
55
|
|
|
50
56
|
## Decision tree
|
|
51
57
|
|
|
52
|
-
- **New app/host** (
|
|
53
|
-
|
|
58
|
+
- **New app/host** (packages not installed, or CSS / `data-*` not loaded) ->
|
|
59
|
+
[getting-started.md](../uxelle-components/getting-started.md), then
|
|
60
|
+
[how-to-host.md](how-to-host.md) for skip link, `main`, and column chrome.
|
|
61
|
+
Composing inside an existing host -> skip both; do not set theme `data-*` on `<html>`.
|
|
54
62
|
- **Product experience** (signed-in app):
|
|
55
63
|
app frame -> [recipe-app-chrome.md](recipe-app-chrome.md);
|
|
56
|
-
landing/overview -> [recipe-dashboard-overview.md](recipe-dashboard-overview.md);
|
|
57
|
-
records list -> [recipe-data-table-page.md](recipe-data-table-page.md);
|
|
64
|
+
landing/overview -> [recipe-dashboard-overview.md](recipe-dashboard-overview.md) (`compact`);
|
|
65
|
+
records list -> [recipe-data-table-page.md](recipe-data-table-page.md) (`compact`);
|
|
58
66
|
one record -> [recipe-record-detail.md](recipe-record-detail.md);
|
|
59
67
|
settings -> [recipe-settings-page.md](recipe-settings-page.md);
|
|
60
68
|
wizard/checkout -> [recipe-multi-step-flow.md](recipe-multi-step-flow.md);
|
|
61
69
|
generic body -> [recipe-page-shell.md](recipe-page-shell.md).
|
|
62
|
-
|
|
70
|
+
Unmarked product recipes default to `standard` ([density.md](density.md)).
|
|
71
|
+
- **Marketing experience** (public, `spacious`): whole page -> [recipe-landing-page.md](recipe-landing-page.md),
|
|
63
72
|
composed from the marketing pieces below.
|
|
64
73
|
- **A piece** (part of an experience): page intro -> [recipe-page-header.md](recipe-page-header.md);
|
|
65
74
|
filter/search toolbar -> [recipe-query-bar.md](recipe-query-bar.md);
|
|
@@ -79,6 +88,11 @@ palette. The full mapping of principles to uxElle mechanisms lives in
|
|
|
79
88
|
padding / margin. Page chrome (inset, column cap, `fr` gaps) uses
|
|
80
89
|
`--uxl-breakpoints-margin` / `-max-container-width` / `-gutter`. See
|
|
81
90
|
[spacing-steps.md](spacing-steps.md) and [how-to-page-layout.md](how-to-page-layout.md).
|
|
91
|
+
- **Density**: choose the step by **role** (Flush / Tight / Control / Group /
|
|
92
|
+
Section / Band) and the heading type by **rank**, both resolved through one
|
|
93
|
+
**mode** for the whole experience — `compact` | `standard` | `spacious`. Page
|
|
94
|
+
chrome and component internals are density-invariant. Never mix modes or tighten
|
|
95
|
+
one role alone. See [density.md](density.md).
|
|
82
96
|
- **Color**: style via `--uxl-color-switcher-*` roles; apply a palette with
|
|
83
97
|
`data-color-switcher` on a **region** (never invent switcher names). See
|
|
84
98
|
[how-to-color.md](how-to-color.md).
|
|
@@ -86,10 +100,12 @@ palette. The full mapping of principles to uxElle mechanisms lives in
|
|
|
86
100
|
never use `--uxl-component-*` in app/recipe CSS (those belong inside components).
|
|
87
101
|
- **No hardcoded values**: no hex, no font-family, no breakpoint px. Read
|
|
88
102
|
breakpoint bounds from theme variables when JS is unavoidable
|
|
89
|
-
([how-to-page-layout.md](how-to-page-layout.md)).
|
|
90
|
-
content (a form/settings page, or a form / FAQ / prose section on a wider
|
|
91
|
-
may cap at the `maxWidth="600px"` **reading measure
|
|
92
|
-
`mh="auto"` ([how-to-page-layout.md](how-to-page-layout.md#reading-measure))
|
|
103
|
+
([how-to-page-layout.md](how-to-page-layout.md)). Two exceptions: one-column
|
|
104
|
+
reading content (a form/settings page, or a form / FAQ / prose section on a wider
|
|
105
|
+
page) may cap at the `maxWidth="600px"` **reading measure**, always centered with
|
|
106
|
+
`mh="auto"` ([how-to-page-layout.md](how-to-page-layout.md#reading-measure)); and
|
|
107
|
+
**intrinsic sizes** — `minmax()` mins and a bounded search field — use `rem`
|
|
108
|
+
([how-to-page-layout.md](how-to-page-layout.md#intrinsic-sizes-in-rem)).
|
|
93
109
|
- **Components first**: prefer a catalog component over bespoke markup; link its
|
|
94
110
|
doc at point of use (`../uxelle-components/<Name>.md`); do not open package source.
|
|
95
111
|
- **Recipes are patterns, not exports**: never add `PageShell` / `AppChrome` /
|
|
@@ -104,14 +120,21 @@ Every generated screen must pass this. It is the definition of done.
|
|
|
104
120
|
|
|
105
121
|
- **Grid & rhythm**: content sits on the grid; one capped content column via
|
|
106
122
|
`--uxl-breakpoints-max-container-width` / `-gutter` / `-margin`; section spacing
|
|
107
|
-
from the ramp
|
|
123
|
+
from the ramp at the experience's **Section** step. One-column reading sections
|
|
108
124
|
(form / FAQ / prose) share the `600px` reading measure and are centered
|
|
109
125
|
(`mh="auto"`); a decision cluster (form actions) is separated from its fields by
|
|
110
|
-
|
|
126
|
+
the **Section** step, not the field gap.
|
|
127
|
+
- **Density**: one mode declared for the surface and applied to every role; the gap
|
|
128
|
+
roles (Tight -> Control -> Group -> Section) stay ~1.5x apart; page chrome and
|
|
129
|
+
component internals unchanged; no breakpoint logic added for density
|
|
130
|
+
([density.md](density.md)).
|
|
111
131
|
- **Tokens only**: spacing from `--uxl-theme-layout-spacing-*`; color from
|
|
112
132
|
`--uxl-color-switcher-*`; no hardcoded hex or px, no invented `--uxl-*`, no
|
|
113
133
|
`--uxl-component-*` in app CSS, no `var(--x, fallback)`.
|
|
114
|
-
- **Hierarchy**: exactly one `h1`; heading ranks unbroken;
|
|
134
|
+
- **Hierarchy**: exactly one `h1`; heading ranks unbroken; each rank resolved to a
|
|
135
|
+
**distinct** `Text` type through the density mode — consecutive ranks never share
|
|
136
|
+
a type (an `h1` and `h2` both on `Display Extra Small` flatten the page), and
|
|
137
|
+
headings inside a `Card` step down one stop ([density.md](density.md)).
|
|
115
138
|
- **Responsive**: reflows mobile -> up with no px literals; clusters stack, `Table`
|
|
116
139
|
scrolls, nav collapses; touch targets stay >= 24 CSS px.
|
|
117
140
|
- **Color discipline**: at most one dominant brand band per view; status palettes
|
|
@@ -126,9 +149,10 @@ Every generated screen must pass this. It is the definition of done.
|
|
|
126
149
|
|
|
127
150
|
**Foundations** (always apply): [principles.md](principles.md) ·
|
|
128
151
|
[tokens.md](tokens.md) · [spacing-steps.md](spacing-steps.md) ·
|
|
152
|
+
[density.md](density.md) ·
|
|
129
153
|
[how-to-page-layout.md](how-to-page-layout.md) · [how-to-color.md](how-to-color.md) ·
|
|
130
154
|
[how-to-accessibility.md](how-to-accessibility.md) · [how-to-host.md](how-to-host.md) ·
|
|
131
|
-
[a2ui.md](a2ui.md)
|
|
155
|
+
[getting-started](../uxelle-components/getting-started.md) · [a2ui.md](a2ui.md)
|
|
132
156
|
|
|
133
157
|
**Experiences** — product: [app-chrome](recipe-app-chrome.md) ·
|
|
134
158
|
[page-shell](recipe-page-shell.md) · [dashboard-overview](recipe-dashboard-overview.md) ·
|
|
@@ -13,12 +13,20 @@ 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.
|
|
19
23
|
- **No `className` / `style`.** Styling comes from the theme and adapter props.
|
|
20
24
|
- **Layout-spacing tokens only.** `A2uiLayout` accepts `0` or
|
|
21
25
|
`var(--uxl-theme-layout-spacing-*)`; never emit page-chrome breakpoint tokens.
|
|
26
|
+
- **`standard` or `compact` density only.** Chat surfaces are narrow and have no
|
|
27
|
+
full-bleed bands, so `spacious` never applies and the **Band** role has no
|
|
28
|
+
meaning. Use `standard` by default, `compact` for data-heavy runtime output
|
|
29
|
+
([density.md](density.md)).
|
|
22
30
|
- **No responsive JS.** There is no `matchMedia`. Default to a single, narrow
|
|
23
31
|
column (chat is mobile-like) and stack action clusters (`"direction": "column"`).
|
|
24
32
|
- **Host owns theme.** Never set theme `data-*` or a skip link from adapters; the
|
|
@@ -45,12 +53,32 @@ recipe JSON into the customer app.
|
|
|
45
53
|
Most catalog components map 1:1 to an `A2ui<Name>` adapter (e.g. `Button` ->
|
|
46
54
|
`A2uiButton`, `Textfield` -> `A2uiTextfield`, `Textarea` -> `A2uiTextarea`,
|
|
47
55
|
`Table` -> `A2uiTable`, `Sheet` -> `A2uiSheet`, `Hero` -> `A2uiHero`,
|
|
48
|
-
`Stepper` -> `A2uiStepper` + `A2uiStepperItem`,
|
|
56
|
+
`Image` -> `A2uiImage`, `Stepper` -> `A2uiStepper` + `A2uiStepperItem`,
|
|
57
|
+
`ListControls` -> `A2uiListControls`,
|
|
49
58
|
`Banner` -> `A2uiBanner`, `LabelBadge` -> `A2uiLabelBadge`, `StatTile` -> `A2uiStatTile`,
|
|
59
|
+
`ChoiceChip` -> `A2uiChoiceChip`, `ChoiceChipGroup` -> `A2uiChoiceChipGroup`,
|
|
60
|
+
`FilterChip` -> `A2uiFilterChip`, `FilterChipGroup` -> `A2uiFilterChipGroup`,
|
|
50
61
|
`DynamicAngle*` -> `A2uiDynamicAngle*`). `Table` has the full family (`A2uiTableGrid`,
|
|
51
62
|
`A2uiTableHead`, `A2uiTableBody`, `A2uiTableRow`, `A2uiTableCell`,
|
|
52
63
|
`A2uiTableHeaderCell`, `A2uiTableColumnSizingProvider`).
|
|
53
64
|
|
|
65
|
+
`A2uiImage` box size (`width`, `height`, `minWidth`, `maxWidth`, `minHeight`,
|
|
66
|
+
`maxHeight`) is `number|string` — numbers are pixels, strings pass through as CSS
|
|
67
|
+
(including `var(--uxl-…)`). `aspectRatio` crops and computes the missing axis.
|
|
68
|
+
Omit size and `aspectRatio` on an `A2uiImage` nested in `A2uiHero`.
|
|
69
|
+
|
|
70
|
+
`A2uiChoiceChip` / `A2uiFilterChip` take a `label` string. Do not nest `A2uiText`
|
|
71
|
+
inside a chip. Choice-chip labels use Condensed when unchecked and Condensed Alt
|
|
72
|
+
when checked; filter-chip labels use Condensed. `A2uiFilterChipGroup` wraps whole
|
|
73
|
+
chips; a chip wider than its container ellipsizes so dismiss stays visible.
|
|
74
|
+
Exclusive `A2uiChoiceChip` selection is one checked value in host state (toggles,
|
|
75
|
+
not radios). Use `A2uiRadioGroup` or `A2uiSegmentedControl` for radio semantics.
|
|
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
|
+
|
|
54
82
|
Known gaps — substitute rather than invent:
|
|
55
83
|
|
|
56
84
|
| React | A2UI |
|
|
@@ -66,7 +94,8 @@ If a design needs a gap component as its backbone, say so in the recipe's
|
|
|
66
94
|
|
|
67
95
|
## Translating a React recipe
|
|
68
96
|
|
|
69
|
-
1. Keep the same region order
|
|
97
|
+
1. Keep the same region order. Keep the layout-spacing values too, unless the React
|
|
98
|
+
surface was `spacious` — then re-resolve its roles at `standard`.
|
|
70
99
|
2. Swap each component for its adapter; expand `Lockup` to stacked `A2uiText`.
|
|
71
100
|
3. Replace `useBreakpointUp` switches with a single narrow column.
|
|
72
101
|
4. Replace React state with host bindings.
|