@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.
Files changed (117) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +58 -0
  3. package/dist/index.d.ts +26 -0
  4. package/dist/index.js +586 -0
  5. package/index.json +573 -0
  6. package/package.json +57 -0
  7. package/skills/uxelle-components/Accordion.md +51 -0
  8. package/skills/uxelle-components/AccordionGroup.md +39 -0
  9. package/skills/uxelle-components/Banner.md +73 -0
  10. package/skills/uxelle-components/BannerAnnouncement.md +63 -0
  11. package/skills/uxelle-components/Breadcrumb.md +38 -0
  12. package/skills/uxelle-components/Breadcrumbs.md +32 -0
  13. package/skills/uxelle-components/Bubble.md +34 -0
  14. package/skills/uxelle-components/Button.md +54 -0
  15. package/skills/uxelle-components/ButtonGroup.md +43 -0
  16. package/skills/uxelle-components/Card.md +35 -0
  17. package/skills/uxelle-components/Checkbox.md +57 -0
  18. package/skills/uxelle-components/CheckboxGroup.md +47 -0
  19. package/skills/uxelle-components/ChoiceChip.md +44 -0
  20. package/skills/uxelle-components/ChoiceChipGroup.md +50 -0
  21. package/skills/uxelle-components/DatePicker.md +59 -0
  22. package/skills/uxelle-components/DatePickerRange.md +73 -0
  23. package/skills/uxelle-components/Dialog.md +83 -0
  24. package/skills/uxelle-components/Divider.md +37 -0
  25. package/skills/uxelle-components/DynamicAngleLine.md +36 -0
  26. package/skills/uxelle-components/DynamicAngleLineCombo.md +30 -0
  27. package/skills/uxelle-components/DynamicAngleShape.md +32 -0
  28. package/skills/uxelle-components/DynamicAngleShapeSurface.md +35 -0
  29. package/skills/uxelle-components/DynamicAngleTexture.md +29 -0
  30. package/skills/uxelle-components/DynamicAngleTextureText.md +35 -0
  31. package/skills/uxelle-components/FieldMessage.md +31 -0
  32. package/skills/uxelle-components/FileUpload.md +53 -0
  33. package/skills/uxelle-components/FilterChip.md +35 -0
  34. package/skills/uxelle-components/FilterChipGroup.md +42 -0
  35. package/skills/uxelle-components/Footer.md +34 -0
  36. package/skills/uxelle-components/Hero.md +61 -0
  37. package/skills/uxelle-components/Icon.md +43 -0
  38. package/skills/uxelle-components/IconButton.md +44 -0
  39. package/skills/uxelle-components/Image.md +40 -0
  40. package/skills/uxelle-components/Label.md +35 -0
  41. package/skills/uxelle-components/LabelBadge.md +58 -0
  42. package/skills/uxelle-components/LanguageSelector.md +38 -0
  43. package/skills/uxelle-components/LanguageSelectorButton.md +34 -0
  44. package/skills/uxelle-components/Layout.md +81 -0
  45. package/skills/uxelle-components/LinearProgress.md +53 -0
  46. package/skills/uxelle-components/Link.md +52 -0
  47. package/skills/uxelle-components/List.md +35 -0
  48. package/skills/uxelle-components/ListControls.md +100 -0
  49. package/skills/uxelle-components/ListItem.md +69 -0
  50. package/skills/uxelle-components/Lockup.md +50 -0
  51. package/skills/uxelle-components/Logo.md +39 -0
  52. package/skills/uxelle-components/Menu.md +48 -0
  53. package/skills/uxelle-components/MultiSelect.md +54 -0
  54. package/skills/uxelle-components/NavLink.md +44 -0
  55. package/skills/uxelle-components/Navigation.md +61 -0
  56. package/skills/uxelle-components/NavigationSide.md +51 -0
  57. package/skills/uxelle-components/NavigationSideGroup.md +36 -0
  58. package/skills/uxelle-components/NavigationSideItem.md +56 -0
  59. package/skills/uxelle-components/NavigationSideSubItem.md +39 -0
  60. package/skills/uxelle-components/NotificationBadge.md +62 -0
  61. package/skills/uxelle-components/Pagination.md +49 -0
  62. package/skills/uxelle-components/PaginationComplex.md +51 -0
  63. package/skills/uxelle-components/PaginationSimple.md +44 -0
  64. package/skills/uxelle-components/ProductCard.md +46 -0
  65. package/skills/uxelle-components/Quantity.md +45 -0
  66. package/skills/uxelle-components/Radio.md +55 -0
  67. package/skills/uxelle-components/RadioGroup.md +49 -0
  68. package/skills/uxelle-components/SKILL.md +242 -0
  69. package/skills/uxelle-components/Scrim.md +28 -0
  70. package/skills/uxelle-components/SegmentedControl.md +118 -0
  71. package/skills/uxelle-components/Select.md +55 -0
  72. package/skills/uxelle-components/Sheet.md +31 -0
  73. package/skills/uxelle-components/Skeleton.md +35 -0
  74. package/skills/uxelle-components/Spinner.md +33 -0
  75. package/skills/uxelle-components/StatTile.md +150 -0
  76. package/skills/uxelle-components/Stepper.md +73 -0
  77. package/skills/uxelle-components/Switch.md +41 -0
  78. package/skills/uxelle-components/Tab.md +41 -0
  79. package/skills/uxelle-components/Table.md +55 -0
  80. package/skills/uxelle-components/TabsCompact.md +37 -0
  81. package/skills/uxelle-components/TabsStretch.md +35 -0
  82. package/skills/uxelle-components/Text.md +56 -0
  83. package/skills/uxelle-components/Textarea.md +77 -0
  84. package/skills/uxelle-components/Textfield.md +62 -0
  85. package/skills/uxelle-components/Tooltip.md +33 -0
  86. package/skills/uxelle-design-harness/SKILL.md +149 -0
  87. package/skills/uxelle-design-harness/a2ui.md +73 -0
  88. package/skills/uxelle-design-harness/how-to-accessibility.md +31 -0
  89. package/skills/uxelle-design-harness/how-to-color.md +82 -0
  90. package/skills/uxelle-design-harness/how-to-host.md +108 -0
  91. package/skills/uxelle-design-harness/how-to-page-layout.md +203 -0
  92. package/skills/uxelle-design-harness/principles.md +72 -0
  93. package/skills/uxelle-design-harness/recipe-app-chrome.md +168 -0
  94. package/skills/uxelle-design-harness/recipe-card-grid.md +104 -0
  95. package/skills/uxelle-design-harness/recipe-cta-band.md +91 -0
  96. package/skills/uxelle-design-harness/recipe-dashboard-overview.md +118 -0
  97. package/skills/uxelle-design-harness/recipe-data-table-page.md +323 -0
  98. package/skills/uxelle-design-harness/recipe-feature-section.md +96 -0
  99. package/skills/uxelle-design-harness/recipe-footer.md +156 -0
  100. package/skills/uxelle-design-harness/recipe-form-section.md +163 -0
  101. package/skills/uxelle-design-harness/recipe-hero.md +89 -0
  102. package/skills/uxelle-design-harness/recipe-landing-page.md +94 -0
  103. package/skills/uxelle-design-harness/recipe-logo-wall.md +80 -0
  104. package/skills/uxelle-design-harness/recipe-multi-step-flow.md +108 -0
  105. package/skills/uxelle-design-harness/recipe-page-header.md +102 -0
  106. package/skills/uxelle-design-harness/recipe-page-shell.md +82 -0
  107. package/skills/uxelle-design-harness/recipe-pricing.md +83 -0
  108. package/skills/uxelle-design-harness/recipe-query-bar.md +108 -0
  109. package/skills/uxelle-design-harness/recipe-record-detail.md +96 -0
  110. package/skills/uxelle-design-harness/recipe-settings-page.md +170 -0
  111. package/skills/uxelle-design-harness/recipe-stat-callouts.md +111 -0
  112. package/skills/uxelle-design-harness/recipe-states.md +119 -0
  113. package/skills/uxelle-design-harness/recipe-summary-list.md +88 -0
  114. package/skills/uxelle-design-harness/recipe-template.md +62 -0
  115. package/skills/uxelle-design-harness/recipe-testimonial.md +73 -0
  116. package/skills/uxelle-design-harness/spacing-steps.md +57 -0
  117. 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)).