@uxelle/skills 0.2.0-beta.2
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/LICENSE +674 -0
- package/README.md +58 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.js +586 -0
- package/index.json +573 -0
- package/package.json +57 -0
- package/skills/uxelle-components/Accordion.md +51 -0
- package/skills/uxelle-components/AccordionGroup.md +39 -0
- package/skills/uxelle-components/Banner.md +73 -0
- package/skills/uxelle-components/BannerAnnouncement.md +63 -0
- package/skills/uxelle-components/Breadcrumb.md +38 -0
- package/skills/uxelle-components/Breadcrumbs.md +32 -0
- package/skills/uxelle-components/Bubble.md +34 -0
- package/skills/uxelle-components/Button.md +54 -0
- package/skills/uxelle-components/ButtonGroup.md +43 -0
- package/skills/uxelle-components/Card.md +35 -0
- package/skills/uxelle-components/Checkbox.md +57 -0
- package/skills/uxelle-components/CheckboxGroup.md +47 -0
- package/skills/uxelle-components/ChoiceChip.md +44 -0
- package/skills/uxelle-components/ChoiceChipGroup.md +50 -0
- package/skills/uxelle-components/DatePicker.md +59 -0
- package/skills/uxelle-components/DatePickerRange.md +73 -0
- package/skills/uxelle-components/Dialog.md +83 -0
- package/skills/uxelle-components/Divider.md +37 -0
- package/skills/uxelle-components/DynamicAngleLine.md +36 -0
- package/skills/uxelle-components/DynamicAngleLineCombo.md +30 -0
- package/skills/uxelle-components/DynamicAngleShape.md +32 -0
- package/skills/uxelle-components/DynamicAngleShapeSurface.md +35 -0
- package/skills/uxelle-components/DynamicAngleTexture.md +29 -0
- package/skills/uxelle-components/DynamicAngleTextureText.md +35 -0
- package/skills/uxelle-components/FieldMessage.md +31 -0
- package/skills/uxelle-components/FileUpload.md +53 -0
- package/skills/uxelle-components/FilterChip.md +35 -0
- package/skills/uxelle-components/FilterChipGroup.md +42 -0
- package/skills/uxelle-components/Footer.md +34 -0
- package/skills/uxelle-components/Hero.md +61 -0
- package/skills/uxelle-components/Icon.md +43 -0
- package/skills/uxelle-components/IconButton.md +44 -0
- package/skills/uxelle-components/Image.md +40 -0
- package/skills/uxelle-components/Label.md +35 -0
- package/skills/uxelle-components/LabelBadge.md +58 -0
- package/skills/uxelle-components/LanguageSelector.md +38 -0
- package/skills/uxelle-components/LanguageSelectorButton.md +34 -0
- package/skills/uxelle-components/Layout.md +81 -0
- package/skills/uxelle-components/LinearProgress.md +53 -0
- package/skills/uxelle-components/Link.md +52 -0
- package/skills/uxelle-components/List.md +35 -0
- package/skills/uxelle-components/ListControls.md +100 -0
- package/skills/uxelle-components/ListItem.md +69 -0
- package/skills/uxelle-components/Lockup.md +50 -0
- package/skills/uxelle-components/Logo.md +39 -0
- package/skills/uxelle-components/Menu.md +48 -0
- package/skills/uxelle-components/MultiSelect.md +54 -0
- package/skills/uxelle-components/NavLink.md +44 -0
- package/skills/uxelle-components/Navigation.md +61 -0
- package/skills/uxelle-components/NavigationSide.md +51 -0
- package/skills/uxelle-components/NavigationSideGroup.md +36 -0
- package/skills/uxelle-components/NavigationSideItem.md +56 -0
- package/skills/uxelle-components/NavigationSideSubItem.md +39 -0
- package/skills/uxelle-components/NotificationBadge.md +62 -0
- package/skills/uxelle-components/Pagination.md +49 -0
- package/skills/uxelle-components/PaginationComplex.md +51 -0
- package/skills/uxelle-components/PaginationSimple.md +44 -0
- package/skills/uxelle-components/ProductCard.md +46 -0
- package/skills/uxelle-components/Quantity.md +45 -0
- package/skills/uxelle-components/Radio.md +55 -0
- package/skills/uxelle-components/RadioGroup.md +49 -0
- package/skills/uxelle-components/SKILL.md +242 -0
- package/skills/uxelle-components/Scrim.md +28 -0
- package/skills/uxelle-components/SegmentedControl.md +118 -0
- package/skills/uxelle-components/Select.md +55 -0
- package/skills/uxelle-components/Sheet.md +31 -0
- package/skills/uxelle-components/Skeleton.md +35 -0
- package/skills/uxelle-components/Spinner.md +33 -0
- package/skills/uxelle-components/StatTile.md +150 -0
- package/skills/uxelle-components/Stepper.md +73 -0
- package/skills/uxelle-components/Switch.md +41 -0
- package/skills/uxelle-components/Tab.md +41 -0
- package/skills/uxelle-components/Table.md +55 -0
- package/skills/uxelle-components/TabsCompact.md +37 -0
- package/skills/uxelle-components/TabsStretch.md +35 -0
- package/skills/uxelle-components/Text.md +56 -0
- package/skills/uxelle-components/Textarea.md +77 -0
- package/skills/uxelle-components/Textfield.md +62 -0
- package/skills/uxelle-components/Tooltip.md +33 -0
- package/skills/uxelle-design-harness/SKILL.md +149 -0
- package/skills/uxelle-design-harness/a2ui.md +73 -0
- package/skills/uxelle-design-harness/how-to-accessibility.md +31 -0
- package/skills/uxelle-design-harness/how-to-color.md +82 -0
- package/skills/uxelle-design-harness/how-to-host.md +108 -0
- package/skills/uxelle-design-harness/how-to-page-layout.md +203 -0
- package/skills/uxelle-design-harness/principles.md +72 -0
- package/skills/uxelle-design-harness/recipe-app-chrome.md +168 -0
- package/skills/uxelle-design-harness/recipe-card-grid.md +104 -0
- package/skills/uxelle-design-harness/recipe-cta-band.md +91 -0
- package/skills/uxelle-design-harness/recipe-dashboard-overview.md +118 -0
- package/skills/uxelle-design-harness/recipe-data-table-page.md +323 -0
- package/skills/uxelle-design-harness/recipe-feature-section.md +96 -0
- package/skills/uxelle-design-harness/recipe-footer.md +156 -0
- package/skills/uxelle-design-harness/recipe-form-section.md +163 -0
- package/skills/uxelle-design-harness/recipe-hero.md +89 -0
- package/skills/uxelle-design-harness/recipe-landing-page.md +94 -0
- package/skills/uxelle-design-harness/recipe-logo-wall.md +80 -0
- package/skills/uxelle-design-harness/recipe-multi-step-flow.md +108 -0
- package/skills/uxelle-design-harness/recipe-page-header.md +102 -0
- package/skills/uxelle-design-harness/recipe-page-shell.md +82 -0
- package/skills/uxelle-design-harness/recipe-pricing.md +83 -0
- package/skills/uxelle-design-harness/recipe-query-bar.md +108 -0
- package/skills/uxelle-design-harness/recipe-record-detail.md +96 -0
- package/skills/uxelle-design-harness/recipe-settings-page.md +170 -0
- package/skills/uxelle-design-harness/recipe-stat-callouts.md +111 -0
- package/skills/uxelle-design-harness/recipe-states.md +119 -0
- package/skills/uxelle-design-harness/recipe-summary-list.md +88 -0
- package/skills/uxelle-design-harness/recipe-template.md +62 -0
- package/skills/uxelle-design-harness/recipe-testimonial.md +73 -0
- package/skills/uxelle-design-harness/spacing-steps.md +57 -0
- package/skills/uxelle-design-harness/tokens.md +101 -0
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# Recipe: settings page
|
|
2
|
+
|
|
3
|
+
## When
|
|
4
|
+
|
|
5
|
+
Account, workspace, or notification preferences: a page lockup, then sections of [`ListControls`](../uxelle-components/ListControls.md) and/or [recipe-form-section.md](recipe-form-section.md) fields. The **whole page** is one column.
|
|
6
|
+
|
|
7
|
+
## When not
|
|
8
|
+
|
|
9
|
+
- Not a `SettingsPage` component.
|
|
10
|
+
- Do not fake toggles with clickable `ListItem` rows and no `Switch`.
|
|
11
|
+
- Do not wrap each setting row in `Card` — `ListControls` + `ListItem` is the row.
|
|
12
|
+
- Theme owns color, radius, and borders. Do not copy a screenshot’s palette.
|
|
13
|
+
- Do not use this page’s `600px` cap on a table, card grid, or any page that needs the theme max width.
|
|
14
|
+
- Do not put unrelated fields on one row (email next to language).
|
|
15
|
+
- A2UI: no `getUxelleRecipe("settings-page")` yet. `Lockup` has no adapter — stack `A2uiText`. Use `A2uiListControls` + `A2uiListItem`. Layout-spacing only. `A2uiLayout` has no `maxWidth` or `gridTemplateColumns` — do not emit `600px`; stack given-name / family-name.
|
|
16
|
+
|
|
17
|
+
## Regions
|
|
18
|
+
|
|
19
|
+
narrow page column (`600px`) → page `Lockup` (`h1`) → sections (`h2` lockup + `ListControls` and/or form fields)
|
|
20
|
+
|
|
21
|
+
## Column width
|
|
22
|
+
|
|
23
|
+
When the **entire page** is one column, cap it with `maxWidth="600px"` instead of `--uxl-breakpoints-max-container-width`. Keep `width="100%"`, `mh="auto"`, and `p="var(--uxl-breakpoints-margin)"`. Do not change the theme token on `<html>`. Do not invent a `--uxl-*` for this width.
|
|
24
|
+
|
|
25
|
+
Tables, card grids, and mixed layouts keep the theme max. See [how-to-page-layout.md](how-to-page-layout.md).
|
|
26
|
+
|
|
27
|
+
## Related fields
|
|
28
|
+
|
|
29
|
+
Parts of **one value** share a row. Use `display="grid"` so each `Textfield` / `Select` (they are `width: 100%` of the parent) gets a track. Gap: `var(--uxl-theme-layout-spacing-small-4)`. Do not `flexWrap="wrap"` them.
|
|
30
|
+
|
|
31
|
+
| Value | Tracks | Fields |
|
|
32
|
+
| --- | --- | --- |
|
|
33
|
+
| Person name | `1fr 1fr` | First name, last name (separate fields — not one “Full name”) |
|
|
34
|
+
| Mailing locality | `2fr 1fr 1fr` | City, state, ZIP |
|
|
35
|
+
| Calendar date | `1fr 1fr 1fr` | Month, day, year |
|
|
36
|
+
|
|
37
|
+
Standalone fields (email, language, phone, about) stay in the section stack at `medium-8`. About / bio is [`Textarea`](../uxelle-components/Textarea.md), not `Textfield`. More related-field examples: [recipe-form-section.md](recipe-form-section.md#related-fields).
|
|
38
|
+
|
|
39
|
+
## Spacing
|
|
40
|
+
|
|
41
|
+
Page column: [how-to-page-layout.md](how-to-page-layout.md). Section gap: `medium-12`. Lockup internals: `micro-2`. Form field stack: `medium-8`. Inline related fields: `small-4`. Do not add extra padding around `ListControls`. Form actions: [how-to-page-layout.md](how-to-page-layout.md#action-clusters).
|
|
42
|
+
|
|
43
|
+
## A11y
|
|
44
|
+
|
|
45
|
+
[how-to-accessibility.md](how-to-accessibility.md). One `h1`, section titles `h2`. Each embedded `Switch` / `Checkbox` / `Radio` has `aria-label` matching the row title (the row is the focus target; the control is `embedded`). `ListControls` `controlType="switch"` for independent preferences. Name fields: `autoComplete="given-name"` / `"family-name"`.
|
|
46
|
+
|
|
47
|
+
## Responsive
|
|
48
|
+
|
|
49
|
+
The single `600px` column already fits mobile. Below tablet, collapse multi-track related-field grids (city/state/ZIP, month/day/year) to one column and stack the action `ButtonGroup` (`useBreakpointUp("tablet")`, [how-to-page-layout.md](how-to-page-layout.md#action-clusters)). A two-field name row (`1fr 1fr`) can stay.
|
|
50
|
+
|
|
51
|
+
## React
|
|
52
|
+
|
|
53
|
+
Import from `@uxelle/components`. Pass `bottomText` (or `""`) so the catalog demo line does not show. `leadingSlot` + `leading` for a decorative `Icon` (`aria-hidden`). `tabletUp` comes from `useBreakpointUp("tablet")` ([how-to-page-layout.md](how-to-page-layout.md#action-clusters)).
|
|
54
|
+
|
|
55
|
+
```tsx
|
|
56
|
+
<Layout
|
|
57
|
+
display="flex"
|
|
58
|
+
flexDirection="column"
|
|
59
|
+
width="100%"
|
|
60
|
+
maxWidth="600px"
|
|
61
|
+
mh="auto"
|
|
62
|
+
p="var(--uxl-breakpoints-margin)"
|
|
63
|
+
gap="var(--uxl-theme-layout-spacing-medium-12)"
|
|
64
|
+
>
|
|
65
|
+
<Lockup
|
|
66
|
+
topSlot={false}
|
|
67
|
+
title={
|
|
68
|
+
<Text type="Display Extra Small" as="h1" width={false}>
|
|
69
|
+
Settings
|
|
70
|
+
</Text>
|
|
71
|
+
}
|
|
72
|
+
body={
|
|
73
|
+
<Text type="Body Medium" width={false}>
|
|
74
|
+
Adjust your personal notification preferences. These apply only to your account.
|
|
75
|
+
</Text>
|
|
76
|
+
}
|
|
77
|
+
/>
|
|
78
|
+
<Layout as="section" display="flex" flexDirection="column" gap="var(--uxl-theme-layout-spacing-small-4)">
|
|
79
|
+
<Lockup
|
|
80
|
+
topSlot={false}
|
|
81
|
+
title={
|
|
82
|
+
<Text id="workspace-notifications-heading" type="Display Extra Small" as="h2" width={false}>
|
|
83
|
+
Workspace notifications
|
|
84
|
+
</Text>
|
|
85
|
+
}
|
|
86
|
+
body={
|
|
87
|
+
<Text type="Body Medium" width={false}>
|
|
88
|
+
Choose which updates you want to receive.
|
|
89
|
+
</Text>
|
|
90
|
+
}
|
|
91
|
+
/>
|
|
92
|
+
<ListControls controlType="switch" aria-labelledby="workspace-notifications-heading">
|
|
93
|
+
<ListItem
|
|
94
|
+
interactive
|
|
95
|
+
leadingSlot
|
|
96
|
+
leading={<Icon iconName="language" variant="sharpUnfilled" aria-hidden />}
|
|
97
|
+
trailingSlot
|
|
98
|
+
trailing={<Switch embedded defaultChecked aria-label="Domain configuration updates" />}
|
|
99
|
+
centerText={<Text type="Body Small Alt">Domain configuration updates</Text>}
|
|
100
|
+
bottomText={<Text type="Condensed">Updates to your custom domain configuration.</Text>}
|
|
101
|
+
/>
|
|
102
|
+
<ListItem
|
|
103
|
+
interactive
|
|
104
|
+
leadingSlot
|
|
105
|
+
leading={<Icon iconName="link" variant="sharpUnfilled" aria-hidden />}
|
|
106
|
+
trailingSlot
|
|
107
|
+
trailing={<Switch embedded defaultChecked aria-label="Monthly links usage summary" />}
|
|
108
|
+
centerText={<Text type="Body Small Alt">Monthly links usage summary</Text>}
|
|
109
|
+
bottomText={
|
|
110
|
+
<Text type="Condensed">Monthly summary of your top links and totals created.</Text>
|
|
111
|
+
}
|
|
112
|
+
/>
|
|
113
|
+
</ListControls>
|
|
114
|
+
</Layout>
|
|
115
|
+
<Layout as="section" display="flex" flexDirection="column" gap="var(--uxl-theme-layout-spacing-medium-8)">
|
|
116
|
+
<Layout display="flex" flexDirection="column" gap="var(--uxl-theme-layout-spacing-micro-2)">
|
|
117
|
+
<Text type="Display Extra Small" as="h2" width={false}>
|
|
118
|
+
Account
|
|
119
|
+
</Text>
|
|
120
|
+
<Text type="Body Medium" width={false}>
|
|
121
|
+
Name, email, language, and about for this personal account.
|
|
122
|
+
</Text>
|
|
123
|
+
</Layout>
|
|
124
|
+
<Layout display="flex" flexDirection="column" gap="var(--uxl-theme-layout-spacing-medium-8)">
|
|
125
|
+
<Layout
|
|
126
|
+
display="grid"
|
|
127
|
+
gridTemplateColumns="1fr 1fr"
|
|
128
|
+
gap="var(--uxl-theme-layout-spacing-small-4)"
|
|
129
|
+
>
|
|
130
|
+
<Textfield labelText="First name" name="givenName" autoComplete="given-name" />
|
|
131
|
+
<Textfield labelText="Last name" name="familyName" autoComplete="family-name" />
|
|
132
|
+
</Layout>
|
|
133
|
+
<Textfield labelText="Email" name="email" type="email" autoComplete="email" />
|
|
134
|
+
<Select
|
|
135
|
+
labelText="Language"
|
|
136
|
+
name="language"
|
|
137
|
+
options={[
|
|
138
|
+
{ value: "EN", label: "English" },
|
|
139
|
+
{ value: "ES", label: "Español" },
|
|
140
|
+
{ value: "DE", label: "Deutsch" },
|
|
141
|
+
]}
|
|
142
|
+
/>
|
|
143
|
+
<Textarea
|
|
144
|
+
labelText="About"
|
|
145
|
+
fieldDescription="Shown on your profile."
|
|
146
|
+
name="bio"
|
|
147
|
+
autoGrow
|
|
148
|
+
counter
|
|
149
|
+
softOverflow
|
|
150
|
+
softOverflowMessage="Stay under the recommended limit."
|
|
151
|
+
maxLength={280}
|
|
152
|
+
/>
|
|
153
|
+
</Layout>
|
|
154
|
+
<ButtonGroup
|
|
155
|
+
direction={tabletUp ? "row" : "column"}
|
|
156
|
+
fullWidth={!tabletUp}
|
|
157
|
+
aria-label="Account form actions"
|
|
158
|
+
>
|
|
159
|
+
<Button emphasis="medium">Cancel</Button>
|
|
160
|
+
<Button emphasis="high">Save</Button>
|
|
161
|
+
</ButtonGroup>
|
|
162
|
+
</Layout>
|
|
163
|
+
</Layout>
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
If the host already renders `<main>` and the page column (skip-link target), omit `as="main"` and extra `p` on this stack — still set `maxWidth="600px"` on the settings column (or on the host column when this is the only page in view).
|
|
167
|
+
|
|
168
|
+
## A2UI
|
|
169
|
+
|
|
170
|
+
Same stacks and `var(--uxl-theme-layout-spacing-*)`. Bind switch state. No `600px` column and no name-row grid until `A2uiLayout` exposes those props — stack `A2uiTextfield` for given-name and family-name, and `A2uiTextarea` (`autoGrow`) for about. Runtime: no `getUxelleRecipe("settings-page")` yet — use `A2uiListControls` + `A2uiListItem` ([a2ui.md](a2ui.md)).
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# Recipe: stat callouts
|
|
2
|
+
|
|
3
|
+
A shared **piece**: a few large figures with labels. Product dashboards use them as
|
|
4
|
+
KPIs; marketing pages use them as proof. Two to four is the sweet spot.
|
|
5
|
+
|
|
6
|
+
Composes [`StatTile`](../uxelle-components/StatTile.md) (dashboard KPIs),
|
|
7
|
+
[`Text`](../uxelle-components/Text.md) (Display for marketing proof figures),
|
|
8
|
+
and optional [`Divider`](../uxelle-components/Divider.md). StatTile
|
|
9
|
+
`trailingSlotContent` is an open slot; a [`LabelBadge`](../uxelle-components/LabelBadge.md)
|
|
10
|
+
delta is one common choice.
|
|
11
|
+
|
|
12
|
+
## When
|
|
13
|
+
|
|
14
|
+
A small set of headline numbers — revenue, active users, uptime, "10k+ teams."
|
|
15
|
+
|
|
16
|
+
## When not
|
|
17
|
+
|
|
18
|
+
- More than ~4 figures, or anything needing sort/filter -> a
|
|
19
|
+
[data-table-page](recipe-data-table-page.md).
|
|
20
|
+
- A single dashboard KPI is `StatTile`, not a grid. A section intro is `Lockup`.
|
|
21
|
+
- Do not wrap `StatTile` in `Card` — StatTile already uses Card for its surface.
|
|
22
|
+
Place StatTiles next to each other in a grid. A figure inside a chart card or
|
|
23
|
+
table cell is `Text`, not StatTile.
|
|
24
|
+
- Do not put `onClick` on StatTile — put a `Link` or `IconButton` in
|
|
25
|
+
`trailingSlotContent`.
|
|
26
|
+
- Do not signal a delta with color alone — pair it with a sign or word.
|
|
27
|
+
|
|
28
|
+
## Regions
|
|
29
|
+
|
|
30
|
+
section -> grid (`auto-fit` / `minmax`) -> `StatTile` (dashboard) or figure `Text` +
|
|
31
|
+
label `Text` (marketing)
|
|
32
|
+
|
|
33
|
+
## Responsive
|
|
34
|
+
|
|
35
|
+
Reflows intrinsically with `repeat(auto-fit, minmax(MIN, 1fr))` — 4-up to 2-up to
|
|
36
|
+
1-up as width shrinks, no JS. `StatTile` defaults to `fullWidth` so each cell fills
|
|
37
|
+
its track. A row variant can use vertical `Divider`s between cells that simply wrap.
|
|
38
|
+
|
|
39
|
+
## Spacing
|
|
40
|
+
|
|
41
|
+
`gap="var(--uxl-breakpoints-gutter)"` between cells. As a marketing band, add
|
|
42
|
+
section breathing with `large-13`+ block padding
|
|
43
|
+
([spacing-steps.md](spacing-steps.md)).
|
|
44
|
+
|
|
45
|
+
## A11y
|
|
46
|
+
|
|
47
|
+
Each figure and its label read together. StatTile names the group from `label`
|
|
48
|
+
automatically (including `valueFirst`). If there is no label, set `aria-label`
|
|
49
|
+
that combines the figure and its meaning. If the trailing slot holds a delta,
|
|
50
|
+
keep the meaning in text, not color alone. See
|
|
51
|
+
[how-to-accessibility.md](how-to-accessibility.md).
|
|
52
|
+
|
|
53
|
+
## Color
|
|
54
|
+
|
|
55
|
+
On a marketing page these often sit in a brand band — set `data-color-switcher` on
|
|
56
|
+
the **section** and style figures/labels with role tokens ([how-to-color.md](how-to-color.md)).
|
|
57
|
+
Keep one dominant brand band per view. Dashboard KPIs stay on `default`; if a
|
|
58
|
+
delta uses a status palette, pair it with a sign or word.
|
|
59
|
+
|
|
60
|
+
## React
|
|
61
|
+
|
|
62
|
+
Plain figures (marketing proof band):
|
|
63
|
+
|
|
64
|
+
```tsx
|
|
65
|
+
<Layout
|
|
66
|
+
as="section"
|
|
67
|
+
display="grid"
|
|
68
|
+
gridTemplateColumns="repeat(auto-fit, minmax(12rem, 1fr))"
|
|
69
|
+
gap="var(--uxl-breakpoints-gutter)"
|
|
70
|
+
aria-label="By the numbers"
|
|
71
|
+
>
|
|
72
|
+
<Layout display="flex" flexDirection="column" gap="var(--uxl-theme-layout-spacing-micro-2)">
|
|
73
|
+
<Text type="Display Medium" as="p" width={false}>10k+</Text>
|
|
74
|
+
<Text type="Body Small" width={false}>Teams onboarded</Text>
|
|
75
|
+
</Layout>
|
|
76
|
+
{/* repeat cells */}
|
|
77
|
+
</Layout>
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Dashboard KPIs (`StatTile`; trailing slot is open — this example uses a delta):
|
|
81
|
+
|
|
82
|
+
```tsx
|
|
83
|
+
<Layout
|
|
84
|
+
display="grid"
|
|
85
|
+
gridTemplateColumns="repeat(auto-fit, minmax(14rem, 1fr))"
|
|
86
|
+
gap="var(--uxl-breakpoints-gutter)"
|
|
87
|
+
aria-label="Key metrics"
|
|
88
|
+
>
|
|
89
|
+
<StatTile
|
|
90
|
+
label="Monthly revenue"
|
|
91
|
+
value="$1.24M"
|
|
92
|
+
trailingSlot
|
|
93
|
+
trailingSlotContent={
|
|
94
|
+
<LabelBadge
|
|
95
|
+
label="+8.2%"
|
|
96
|
+
dataColorSwitcher="success"
|
|
97
|
+
emphasis="low"
|
|
98
|
+
leadingIcon
|
|
99
|
+
leadingIconName="trending_up"
|
|
100
|
+
/>
|
|
101
|
+
}
|
|
102
|
+
/>
|
|
103
|
+
{/* repeat StatTile cells */}
|
|
104
|
+
</Layout>
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## A2UI
|
|
108
|
+
|
|
109
|
+
No `getUxelleRecipe("stat-callouts")` and no `gridTemplateColumns` — stack
|
|
110
|
+
`A2uiStatTile` (dashboard) or `A2uiText` pairs (marketing) in a column
|
|
111
|
+
([a2ui.md](a2ui.md)).
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# Recipe: states
|
|
2
|
+
|
|
3
|
+
A shared **piece**: the empty, loading, error, and inline-feedback states every
|
|
4
|
+
data region needs. Designing these is part of the definition of done — not an
|
|
5
|
+
afterthought.
|
|
6
|
+
|
|
7
|
+
Composes [`Skeleton`](../uxelle-components/Skeleton.md) /
|
|
8
|
+
[`SkeletonGroup`](../uxelle-components/Skeleton.md),
|
|
9
|
+
[`Spinner`](../uxelle-components/Spinner.md),
|
|
10
|
+
[`Banner`](../uxelle-components/Banner.md),
|
|
11
|
+
[`Text`](../uxelle-components/Text.md), and [`Button`](../uxelle-components/Button.md).
|
|
12
|
+
|
|
13
|
+
## When
|
|
14
|
+
|
|
15
|
+
Any region backed by data or an async action: lists, tables, cards, forms, panels.
|
|
16
|
+
|
|
17
|
+
## When not
|
|
18
|
+
|
|
19
|
+
- Do not blank the whole page for a partial load — scope the state to the region.
|
|
20
|
+
- Do not signal status with color alone; pair with an icon or words
|
|
21
|
+
([how-to-color.md](how-to-color.md)).
|
|
22
|
+
- Do not swap a `SkeletonGroup` out of the tree while loading — keep it mounted and
|
|
23
|
+
toggle `loading`.
|
|
24
|
+
|
|
25
|
+
## Loading
|
|
26
|
+
|
|
27
|
+
Mirror the final layout with `SkeletonGroup` + `Skeleton` shapes; use `Spinner`
|
|
28
|
+
for an in-place operation (and disable its trigger while pending).
|
|
29
|
+
|
|
30
|
+
```tsx
|
|
31
|
+
<SkeletonGroup loading={isLoading} aria-label="Loading invoices" loadedLabel="Invoices loaded">
|
|
32
|
+
{isLoading ? (
|
|
33
|
+
<Layout display="flex" flexDirection="column" gap="var(--uxl-theme-layout-spacing-small-4)">
|
|
34
|
+
<Skeleton type="rectangle" size="small" />
|
|
35
|
+
<Skeleton type="rectangle" size="small" />
|
|
36
|
+
<Skeleton type="rectangle" size="small" />
|
|
37
|
+
</Layout>
|
|
38
|
+
) : (
|
|
39
|
+
<InvoiceTable rows={rows} />
|
|
40
|
+
)}
|
|
41
|
+
</SkeletonGroup>
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Empty
|
|
45
|
+
|
|
46
|
+
Keep surrounding chrome (header, tabs, query bar); put a `role="status"` sentence
|
|
47
|
+
where the data would be, plus the action that creates the first item.
|
|
48
|
+
|
|
49
|
+
```tsx
|
|
50
|
+
<Layout
|
|
51
|
+
display="flex"
|
|
52
|
+
flexDirection="column"
|
|
53
|
+
alignItems="center"
|
|
54
|
+
gap="var(--uxl-theme-layout-spacing-small-4)"
|
|
55
|
+
p="var(--uxl-theme-layout-spacing-large-13)"
|
|
56
|
+
>
|
|
57
|
+
<Icon iconName="inbox" variant="sharpUnfilled" aria-hidden />
|
|
58
|
+
<Text type="Display Extra Small" as="h2" width={false}>No invoices yet</Text>
|
|
59
|
+
<Text type="Body Medium" width={false} role="status">
|
|
60
|
+
Invoices you create will appear here.
|
|
61
|
+
</Text>
|
|
62
|
+
<Button emphasis="high">
|
|
63
|
+
<Text type="Button" width>New invoice</Text>
|
|
64
|
+
</Button>
|
|
65
|
+
</Layout>
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Error
|
|
69
|
+
|
|
70
|
+
A recoverable, in-flow failure uses a non-dismissible `danger` `Banner` with a retry
|
|
71
|
+
action.
|
|
72
|
+
|
|
73
|
+
```tsx
|
|
74
|
+
<Banner
|
|
75
|
+
dataColorSwitcher="danger"
|
|
76
|
+
dismissible={false}
|
|
77
|
+
alert
|
|
78
|
+
leadingSlotContent={<Icon iconName="error" variant="sharpUnfilled" aria-hidden />}
|
|
79
|
+
actionsSlotContent={
|
|
80
|
+
<ButtonGroup direction="row" aria-label="Error actions">
|
|
81
|
+
<Button emphasis="medium" onClick={retry}>
|
|
82
|
+
<Text type="Button" width>Try again</Text>
|
|
83
|
+
</Button>
|
|
84
|
+
</ButtonGroup>
|
|
85
|
+
}
|
|
86
|
+
>
|
|
87
|
+
<Text type="Body Medium Alt" width>We couldn't load invoices.</Text>
|
|
88
|
+
</Banner>
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Inline feedback
|
|
92
|
+
|
|
93
|
+
Task results use a `Banner` with the matching status palette. For a banner injected
|
|
94
|
+
after load, set `aria-live` (or `alert` for urgent).
|
|
95
|
+
|
|
96
|
+
```tsx
|
|
97
|
+
<Banner
|
|
98
|
+
dataColorSwitcher="success"
|
|
99
|
+
dismissible
|
|
100
|
+
open={open}
|
|
101
|
+
onDismiss={() => setOpen(false)}
|
|
102
|
+
leadingSlotContent={<Icon iconName="check_circle" variant="sharpUnfilled" aria-hidden />}
|
|
103
|
+
>
|
|
104
|
+
<Text type="Body Medium Alt" width>Invoice sent.</Text>
|
|
105
|
+
</Banner>
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## A11y
|
|
109
|
+
|
|
110
|
+
`SkeletonGroup` exposes `aria-busy` and announces load/complete; individual shapes
|
|
111
|
+
are hidden. `Spinner` is its own live region (do not override its roles). Empty copy
|
|
112
|
+
is `role="status"`. Status color is always paired with an icon and text. See
|
|
113
|
+
[how-to-accessibility.md](how-to-accessibility.md).
|
|
114
|
+
|
|
115
|
+
## A2UI
|
|
116
|
+
|
|
117
|
+
No `getUxelleRecipe("states")`. `SkeletonGroup` has no adapter — repeat
|
|
118
|
+
`A2uiSkeleton`; use `A2uiSpinner` and `A2uiBanner` (bind `open` / status). See
|
|
119
|
+
[a2ui.md](a2ui.md).
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Recipe: summary list
|
|
2
|
+
|
|
3
|
+
A shared **piece**: a compact key/value summary — the "at a glance" facts of a
|
|
4
|
+
record, order, or account. Used by [record-detail](recipe-record-detail.md),
|
|
5
|
+
review steps ([multi-step-flow](recipe-multi-step-flow.md)), and side panels.
|
|
6
|
+
|
|
7
|
+
Composes [`Text`](../uxelle-components/Text.md), optional
|
|
8
|
+
[`LabelBadge`](../uxelle-components/LabelBadge.md) for status values, and optional
|
|
9
|
+
[`Divider`](../uxelle-components/Divider.md). It is a native definition list
|
|
10
|
+
(`Layout` cannot render `dl`/`dt`/`dd`).
|
|
11
|
+
|
|
12
|
+
## When
|
|
13
|
+
|
|
14
|
+
A short set of label -> value pairs presented for scanning, not editing.
|
|
15
|
+
|
|
16
|
+
## When not
|
|
17
|
+
|
|
18
|
+
- Not editable fields — that is a [form-section](recipe-form-section.md).
|
|
19
|
+
- Not tabular rows of records — that is a [data-table-page](recipe-data-table-page.md).
|
|
20
|
+
- Do not fake it with two loose `Text` columns; use `dl` / `dt` / `dd` so the
|
|
21
|
+
pairing is programmatic.
|
|
22
|
+
|
|
23
|
+
## Regions
|
|
24
|
+
|
|
25
|
+
`dl` grid -> repeating `dt` (label) + `dd` (value, optional status `LabelBadge`)
|
|
26
|
+
|
|
27
|
+
## Responsive
|
|
28
|
+
|
|
29
|
+
Two columns (`max-content 1fr`) read well down to mobile. When labels or values are
|
|
30
|
+
long, stack each pair (label above value) below tablet by switching
|
|
31
|
+
`gridTemplateColumns` to `1fr` (`useBreakpointUp("tablet")`,
|
|
32
|
+
[how-to-page-layout.md](how-to-page-layout.md#responsiveness)).
|
|
33
|
+
|
|
34
|
+
## Spacing
|
|
35
|
+
|
|
36
|
+
Row/column `gap` = `var(--uxl-theme-layout-spacing-small-4)`. Reset the browser
|
|
37
|
+
`dd` indent with `margin: 0`. Group inside a section at the page rhythm
|
|
38
|
+
([spacing-steps.md](spacing-steps.md)).
|
|
39
|
+
|
|
40
|
+
## A11y
|
|
41
|
+
|
|
42
|
+
`dl` / `dt` / `dd` make the pairing explicit to assistive tech. A status value uses
|
|
43
|
+
`LabelBadge` whose visible text carries the meaning; keep `dataColorSwitcher` paired
|
|
44
|
+
with that text ([how-to-color.md](how-to-color.md)). Missing values use an em dash
|
|
45
|
+
(`—`), not an empty cell.
|
|
46
|
+
|
|
47
|
+
## React
|
|
48
|
+
|
|
49
|
+
```tsx
|
|
50
|
+
const tabletUp = useBreakpointUp("tablet");
|
|
51
|
+
|
|
52
|
+
<dl
|
|
53
|
+
style={{
|
|
54
|
+
display: "grid",
|
|
55
|
+
gridTemplateColumns: tabletUp ? "max-content 1fr" : "1fr",
|
|
56
|
+
columnGap: "var(--uxl-theme-layout-spacing-medium-8)",
|
|
57
|
+
rowGap: "var(--uxl-theme-layout-spacing-small-4)",
|
|
58
|
+
margin: 0,
|
|
59
|
+
}}
|
|
60
|
+
>
|
|
61
|
+
<dt>
|
|
62
|
+
<Text type="Condensed" width={false}>Status</Text>
|
|
63
|
+
</dt>
|
|
64
|
+
<dd style={{ margin: 0 }}>
|
|
65
|
+
<LabelBadge label="Active" dataColorSwitcher="success" emphasis="low" />
|
|
66
|
+
</dd>
|
|
67
|
+
|
|
68
|
+
<dt>
|
|
69
|
+
<Text type="Condensed" width={false}>Owner</Text>
|
|
70
|
+
</dt>
|
|
71
|
+
<dd style={{ margin: 0 }}>
|
|
72
|
+
<Text type="Body Small Alt" width={false}>Dana Lee</Text>
|
|
73
|
+
</dd>
|
|
74
|
+
|
|
75
|
+
<dt>
|
|
76
|
+
<Text type="Condensed" width={false}>Created</Text>
|
|
77
|
+
</dt>
|
|
78
|
+
<dd style={{ margin: 0 }}>
|
|
79
|
+
<Text type="Body Small Alt" width={false}>Mar 3, 2026</Text>
|
|
80
|
+
</dd>
|
|
81
|
+
</dl>
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## A2UI
|
|
85
|
+
|
|
86
|
+
No `getUxelleRecipe("summary-list")` and no `dl` adapter — stack `A2uiText` label /
|
|
87
|
+
value pairs in a single `A2uiLayout` column, with `A2uiLabelBadge` for status values
|
|
88
|
+
([a2ui.md](a2ui.md)).
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Contributing a recipe
|
|
2
|
+
|
|
3
|
+
For **design-system authors** adding a recipe to uxElle — not for generating
|
|
4
|
+
product UI. Generators use the existing `recipe-*.md` files. Never add recipes,
|
|
5
|
+
`PageShell`, `AppChrome`, or `FormSection` components to a customer app; uxElle owns
|
|
6
|
+
recipes, components, and themes — contribute changes back.
|
|
7
|
+
|
|
8
|
+
## Taxonomy
|
|
9
|
+
|
|
10
|
+
- **Experience** — a whole screen (dashboard, data-table page, landing page).
|
|
11
|
+
- **Piece** — a part reused across experiences (page header, query bar, states,
|
|
12
|
+
hero, pricing).
|
|
13
|
+
- **Track** — product (signed-in, calm, dense) or marketing (public, expressive).
|
|
14
|
+
Same grid, tokens, and components ([principles.md](principles.md)).
|
|
15
|
+
|
|
16
|
+
File name: `recipe-<id>.md`, `<id>` kebab-case. If registered for A2UI, the runtime
|
|
17
|
+
export is `getUxelleRecipe("<id>")` from `@uxelle/a2ui`.
|
|
18
|
+
|
|
19
|
+
## Required sections
|
|
20
|
+
|
|
21
|
+
Match the voice and order of the existing recipes — positive-first, precise, no
|
|
22
|
+
narration.
|
|
23
|
+
|
|
24
|
+
1. **Title + one-line intro** — state experience vs piece and what it composes,
|
|
25
|
+
linking each component at point of use: `[`Table`](../uxelle-components/Table.md)`.
|
|
26
|
+
2. **When** / **When not** — one sentence each plus the real constraints
|
|
27
|
+
(host-owned, missing adapters, what belongs elsewhere).
|
|
28
|
+
3. **Regions** — host -> layout -> sections/slots, in order.
|
|
29
|
+
4. **Responsive** — required. How it reflows: intrinsic first (`flex-wrap`,
|
|
30
|
+
`auto-fit`/`minmax`, chrome tokens); `useBreakpointUp("<band>")` only for
|
|
31
|
+
prop-level switches. Never hardcode breakpoint px
|
|
32
|
+
([how-to-page-layout.md](how-to-page-layout.md#responsiveness)).
|
|
33
|
+
5. **Spacing** — stacks use `var(--uxl-theme-layout-spacing-*)` (or `0`); page
|
|
34
|
+
chrome uses the breakpoint tokens ([spacing-steps.md](spacing-steps.md)).
|
|
35
|
+
6. **A11y** — landmarks, heading ranks, names, live regions
|
|
36
|
+
([how-to-accessibility.md](how-to-accessibility.md)).
|
|
37
|
+
7. **Color** — include when the recipe uses palettes or brand bands: set
|
|
38
|
+
`data-color-switcher` on the region, style with role tokens
|
|
39
|
+
([how-to-color.md](how-to-color.md)).
|
|
40
|
+
8. **React** — `@uxelle/components` composition.
|
|
41
|
+
9. **A2UI** — `getUxelleRecipe("<id>")` if registered; otherwise say "compose
|
|
42
|
+
adapters" and call out gaps (e.g. `Lockup` has no adapter) ([a2ui.md](a2ui.md)).
|
|
43
|
+
|
|
44
|
+
## Rules
|
|
45
|
+
|
|
46
|
+
- **Tokens only** — never invent `--uxl-*`, never `var(--x, fallback)`, never
|
|
47
|
+
`--uxl-component-*` in recipe CSS. No hardcoded hex, font-family, or breakpoint px
|
|
48
|
+
(a one-column form/settings page may cap at `600px`).
|
|
49
|
+
- **Components first** — compose catalog components and link their docs at point of
|
|
50
|
+
use; do not open package source.
|
|
51
|
+
- **Verify the code** — confirm every prop against the component's doc before
|
|
52
|
+
writing it; prefer showing the real composition over pseudo-props.
|
|
53
|
+
- **Quality bar** — the recipe's output must pass the checklist in
|
|
54
|
+
[SKILL.md](SKILL.md#quality-checklist-run-before-you-finish).
|
|
55
|
+
|
|
56
|
+
## A2UI spec shape (when registering a runtime recipe)
|
|
57
|
+
|
|
58
|
+
- **id** — kebab-case; export is `getUxelleRecipe("<id>")`.
|
|
59
|
+
- **when** / **when-not** — one sentence plus constraints.
|
|
60
|
+
- **adapters** — the catalog adapters used.
|
|
61
|
+
- **messages** — the A2UI message tree. Do not duplicate it into the customer app.
|
|
62
|
+
- A recipe that cannot work in both tracks must say so in **when-not**.
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Recipe: testimonial
|
|
2
|
+
|
|
3
|
+
A marketing **piece**: a customer quote with attribution. Show one prominently or a
|
|
4
|
+
few in a grid as social proof.
|
|
5
|
+
|
|
6
|
+
Composes [`Card`](../uxelle-components/Card.md),
|
|
7
|
+
[`Text`](../uxelle-components/Text.md),
|
|
8
|
+
[`Lockup`](../uxelle-components/Lockup.md), and optionally
|
|
9
|
+
[`Logo`](../uxelle-components/Logo.md) or [`Image`](../uxelle-components/Image.md)
|
|
10
|
+
for the customer mark or portrait.
|
|
11
|
+
|
|
12
|
+
## When
|
|
13
|
+
|
|
14
|
+
A concrete, attributed quote that backs up a claim.
|
|
15
|
+
|
|
16
|
+
## When not
|
|
17
|
+
|
|
18
|
+
- Anonymous or unattributed copy is not a testimonial — attribute it.
|
|
19
|
+
- A row of company marks alone is a [logo-wall](recipe-logo-wall.md).
|
|
20
|
+
- Use real semantics: the quote is a `blockquote`, the attribution a `figcaption`.
|
|
21
|
+
|
|
22
|
+
## Regions
|
|
23
|
+
|
|
24
|
+
`figure` -> `blockquote` (quote `Text`) -> `figcaption` (name, role/company,
|
|
25
|
+
optional `Logo`)
|
|
26
|
+
|
|
27
|
+
## Responsive
|
|
28
|
+
|
|
29
|
+
A single testimonial is a centered, measure-capped block. A set reflows with
|
|
30
|
+
`repeat(auto-fit, minmax(MIN, 1fr))` — 3-up to 1-up by width, no JS
|
|
31
|
+
([card-grid](recipe-card-grid.md)).
|
|
32
|
+
|
|
33
|
+
## Spacing
|
|
34
|
+
|
|
35
|
+
`small-4` between the quote and attribution; `--uxl-breakpoints-gutter` between cards
|
|
36
|
+
in a grid; `large-13`+ block padding as a band ([spacing-steps.md](spacing-steps.md)).
|
|
37
|
+
|
|
38
|
+
## A11y
|
|
39
|
+
|
|
40
|
+
Use `figure` / `blockquote` / `figcaption` so the quote and its source are
|
|
41
|
+
associated. A customer `Logo` is decorative when the name is in text
|
|
42
|
+
(`aria-hidden`), otherwise give it an accessible name. See
|
|
43
|
+
[how-to-accessibility.md](how-to-accessibility.md).
|
|
44
|
+
|
|
45
|
+
## React
|
|
46
|
+
|
|
47
|
+
```tsx
|
|
48
|
+
<Card
|
|
49
|
+
cardContentSlot={
|
|
50
|
+
<figure style={{ margin: 0 }}>
|
|
51
|
+
<blockquote style={{ margin: 0 }}>
|
|
52
|
+
<Text type="Display Extra Small" as="p" width={false}>
|
|
53
|
+
"We cut onboarding from weeks to days."
|
|
54
|
+
</Text>
|
|
55
|
+
</blockquote>
|
|
56
|
+
<figcaption>
|
|
57
|
+
<Layout display="flex" flexDirection="column" gap="var(--uxl-theme-layout-spacing-micro-2)">
|
|
58
|
+
<Text type="Body Small Alt" width={false}>Dana Lee</Text>
|
|
59
|
+
<Text type="Condensed" width={false}>VP Operations, Northwind</Text>
|
|
60
|
+
</Layout>
|
|
61
|
+
</figcaption>
|
|
62
|
+
</figure>
|
|
63
|
+
}
|
|
64
|
+
/>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
For a set, place several of these `Card`s in a
|
|
68
|
+
`repeat(auto-fit, minmax(18rem, 1fr))` grid.
|
|
69
|
+
|
|
70
|
+
## A2UI
|
|
71
|
+
|
|
72
|
+
No `getUxelleRecipe("testimonial")` and no `figure` adapter — compose `A2uiCard`
|
|
73
|
+
with stacked `A2uiText` (quote, then name, then role) ([a2ui.md](a2ui.md)).
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Layout spacing ramp
|
|
2
|
+
|
|
3
|
+
Use the theme spacing variables on React `Layout` and A2UI `A2uiLayout` for
|
|
4
|
+
`gap`, padding, and margin. Pass `0` for flush, otherwise
|
|
5
|
+
`var(--uxl-theme-layout-spacing-*)`. The canonical list is `LAYOUT_SPACING_VARS`
|
|
6
|
+
from `@uxelle/components`.
|
|
7
|
+
|
|
8
|
+
The ramp is numbered **0-16 across the whole scale** (not "size word + arbitrary
|
|
9
|
+
number"). The size band must match the index, so `medium-10` exists but
|
|
10
|
+
`large-10` does not.
|
|
11
|
+
|
|
12
|
+
| Band | Steps | Tokens |
|
|
13
|
+
|------|-------|--------|
|
|
14
|
+
| Micro | 0-3 | `--uxl-theme-layout-spacing-micro-0` … `-micro-3` |
|
|
15
|
+
| Small | 4-6 | `--uxl-theme-layout-spacing-small-4` … `-small-6` |
|
|
16
|
+
| Medium | 7-12 | `--uxl-theme-layout-spacing-medium-7` … `-medium-12` |
|
|
17
|
+
| Large | 13-16 | `--uxl-theme-layout-spacing-large-13` … `-large-16` |
|
|
18
|
+
|
|
19
|
+
## Choosing a step
|
|
20
|
+
|
|
21
|
+
- **Flush** (line boxes already separate lines, e.g. name over email) -> `0`.
|
|
22
|
+
- **Tight stacks / chips** -> `micro-2`.
|
|
23
|
+
- **Related controls / field stacks** -> `small-4`. Same-row clusters (search +
|
|
24
|
+
export, filter + facet) use this gap and stay `nowrap`.
|
|
25
|
+
- **Card / section padding** (also the default A2UI gap) -> `medium-8`.
|
|
26
|
+
- **Page-section gap** (lockup vs table vs a later block) -> `medium-12`.
|
|
27
|
+
- **Hero / extra breathing inside a section** -> `large-13`+.
|
|
28
|
+
|
|
29
|
+
Do not use the page-section gap (`medium-12`) between a table's tabs and its grid —
|
|
30
|
+
those belong together at `small-4`.
|
|
31
|
+
|
|
32
|
+
## Usage
|
|
33
|
+
|
|
34
|
+
React:
|
|
35
|
+
|
|
36
|
+
```tsx
|
|
37
|
+
<Layout display="flex" gap="var(--uxl-theme-layout-spacing-medium-8)" p="var(--uxl-theme-layout-spacing-large-13)">
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
A2UI — the same strings, not numbers and not aliases:
|
|
41
|
+
|
|
42
|
+
```json
|
|
43
|
+
{ "gap": "var(--uxl-theme-layout-spacing-medium-8)" }
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Set `display="flex"` (or `grid`) when using `gap`. Legacy aliases
|
|
47
|
+
(`none` | `xs` | `sm` | `md` | `lg` | `xl`) still resolve on React, but **do not
|
|
48
|
+
emit them in new generated UI** — use the indexed tokens.
|
|
49
|
+
|
|
50
|
+
## Not part of this ramp
|
|
51
|
+
|
|
52
|
+
Page inset, column cap, and column/`fr` gaps are **page chrome**, not ramp steps:
|
|
53
|
+
`--uxl-breakpoints-margin` / `-max-container-width` / `-gutter`. See
|
|
54
|
+
[how-to-page-layout.md](how-to-page-layout.md) and [tokens.md](tokens.md). One-column
|
|
55
|
+
reading content (a settings/form page, or a form / FAQ / prose section on a wider
|
|
56
|
+
page) may cap at the `maxWidth="600px"` reading measure — centered — instead of the
|
|
57
|
+
theme max ([how-to-page-layout.md](how-to-page-layout.md#reading-measure)).
|