@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,203 @@
1
+ # How to: page layout and responsiveness
2
+
3
+ The home for laying out a React **page body** and making it responsive. (A2UI
4
+ chat surfaces are inherently narrow — see [a2ui.md](a2ui.md).)
5
+
6
+ Page chrome comes from theme variables (do not invent `uxl-breakpoint-*` singular):
7
+
8
+ - `--uxl-breakpoints-max-container-width` — cap the body column so large viewports keep side space
9
+ - `--uxl-breakpoints-margin` — padding **around** the body
10
+ - `--uxl-breakpoints-gutter` — gap **between** columns (`fr` grids, card rows)
11
+
12
+ Stacks inside the column use `--uxl-theme-layout-spacing-*` ([spacing-steps.md](spacing-steps.md)).
13
+
14
+ ## Content column
15
+
16
+ One capped, centered column carries the page:
17
+
18
+ ```tsx
19
+ <Layout
20
+ display="flex"
21
+ flexDirection="column"
22
+ width="100%"
23
+ maxWidth="var(--uxl-breakpoints-max-container-width)"
24
+ mh="auto"
25
+ p="var(--uxl-breakpoints-margin)"
26
+ gap="var(--uxl-theme-layout-spacing-medium-12)"
27
+ >
28
+ ```
29
+
30
+ `mh="auto"` centers the column; `medium-12` is the gap between page sections. Cap
31
+ with the token, not a literal (`1120px` / `1600px`), and keep page internals off
32
+ the theme-internal `--uxl-theme-layout-*-gutter` / `*-margin`.
33
+
34
+ If the host already renders `<main>` (the skip-link target), keep this column a
35
+ `div`; otherwise set `as="main"`.
36
+
37
+ **One-column reading content** (settings, a form-only view, or a form / FAQ /
38
+ prose section inside a wider page) caps at the **reading measure** rather than the
39
+ container token — see [Reading measure](#reading-measure).
40
+
41
+ ## Reading measure
42
+
43
+ Text you read in a single column — a form, an FAQ, a paragraph block — is easiest
44
+ to scan at roughly `maxWidth="600px"`. Treat that as one shared measure, applied
45
+ the same way everywhere it appears:
46
+
47
+ - **Cap the whole section, not the inner control.** Put `width="100%"`,
48
+ `maxWidth="600px"`, `mh="auto"` on the section wrapper so its heading, body,
49
+ fields, or list share one left edge and one measure. Capping only the form (or
50
+ only the accordion) leaves the heading full-width and the block stranded left of
51
+ center.
52
+ - **Center it.** A block capped narrower than its column must set `mh="auto"`, or
53
+ it reads as left-aligned and unbalanced against the wider content around it.
54
+ - **Keep the measure consistent across siblings.** When a page stacks reading
55
+ sections — e.g. an FAQ above a demo form — give them the *same* `600px` measure
56
+ and centering so they line up as one column instead of each finding its own
57
+ width.
58
+ - **A whole one-column page** (account settings, a form-only view) caps its page
59
+ column at `600px` the same way — keep the margin and centering
60
+ ([recipe-settings-page.md](recipe-settings-page.md)).
61
+
62
+ Do not apply `600px` to tables, card grids, or mixed / multi-column layouts —
63
+ those keep `--uxl-breakpoints-max-container-width`.
64
+
65
+ ## Columns and cards
66
+
67
+ ```tsx
68
+ <Layout display="grid" gridTemplateColumns="1fr 1fr" gap="var(--uxl-breakpoints-gutter)">
69
+ ```
70
+
71
+ Put gutter on column gaps, margin on the outer padding — not the other way round.
72
+
73
+ ## Responsiveness
74
+
75
+ Design mobile-first and let layouts reflow up. Reach for methods in this order.
76
+
77
+ ### 1. Intrinsic reflow (primary method)
78
+
79
+ Prefer fluid CSS that adapts with no breakpoint values and no JS:
80
+
81
+ - **Grids that reflow by width** — `repeat(auto-fit, minmax(MIN, 1fr))` so the
82
+ column count follows available space:
83
+
84
+ ```tsx
85
+ <Layout
86
+ display="grid"
87
+ gridTemplateColumns="repeat(auto-fit, minmax(16rem, 1fr))"
88
+ gap="var(--uxl-breakpoints-gutter)"
89
+ >
90
+ ```
91
+
92
+ - **Clusters that wrap** — `flexWrap="wrap"` for chip rows and toolbars that may
93
+ need a second line.
94
+ - **Fluid measures** — `minmax()` / `clamp()` for column widths.
95
+ - **The chrome tokens** already remap per breakpoint, so the capped column,
96
+ margin, and gutter tighten on small screens for free.
97
+
98
+ This covers page columns, card/metric/feature grids, and most cluster wrapping.
99
+
100
+ ### 2. Component-native responsiveness
101
+
102
+ Prefer components that adapt themselves (`Navigation`, `Sheet`, `Hero`
103
+ `contentDirection`) over app-level logic. Confirm a component's built-in behavior
104
+ in its doc before adding anything.
105
+
106
+ ### 3. Token-driven JS (narrow fallback)
107
+
108
+ CSS media queries cannot read `var()`, so when a switch is bound to a component
109
+ **prop** and cannot be done with flow — e.g. `ButtonGroup` `direction` row<->column,
110
+ or swapping nav for a drawer — read the theme breakpoint bound and track it. Never
111
+ write the px literal:
112
+
113
+ ```tsx
114
+ import { useEffect, useState } from "react";
115
+
116
+ // Tracks a theme breakpoint bound. The px comes from the theme, so it stays
117
+ // correct across platforms/themes — do not hardcode it.
118
+ function useBreakpointUp(
119
+ band: "tablet" | "desktop" | "desktop-plus" = "tablet",
120
+ ): boolean {
121
+ const [up, setUp] = useState(true); // desktop-first default avoids a mobile flash on hydrate
122
+ useEffect(() => {
123
+ if (typeof window === "undefined") return;
124
+ const min = getComputedStyle(document.documentElement)
125
+ .getPropertyValue(`--uxl-theme-layout-${band}-screen-width-min`)
126
+ .trim();
127
+ if (!min) return;
128
+ const media = window.matchMedia(`(min-width: ${min})`);
129
+ const sync = () => setUp(media.matches);
130
+ sync();
131
+ media.addEventListener("change", sync);
132
+ return () => media.removeEventListener("change", sync);
133
+ }, [band]);
134
+ return up;
135
+ }
136
+
137
+ const tabletUp = useBreakpointUp("tablet");
138
+ ```
139
+
140
+ Bands: `tablet` (from `--uxl-theme-layout-tablet-screen-width-min`), `desktop`,
141
+ `desktop-plus`. Use this only where intrinsic reflow cannot express the change.
142
+
143
+ ### Reflow patterns at a glance
144
+
145
+ - **Grids**: 1 -> 2 -> 3(-4)-up via `auto-fit` / `minmax`; use the JS helper only
146
+ when a component needs an explicit count.
147
+ - **Header / query clusters**: title over actions, search full-width with
148
+ filters/export below, when narrow; one row when wide (see below).
149
+ - **Navigation**: collapse to an `IconButton` menu opening a `Sheet` / `Menu`
150
+ drawer when narrow; verify `Navigation`'s built-in behavior first.
151
+ - **Tables**: `Table` scrolls horizontally when narrow (no stacked-row API); use
152
+ `PaginationSimple` where full `Pagination` is too wide.
153
+ - **Hero / media**: `Hero` `contentDirection` handles narrow stacking; related-field
154
+ grids and summary lists collapse to one column.
155
+ - **Touch targets**: catalog controls meet 24x24 CSS px — do not shrink hit areas.
156
+
157
+ ## Keep controls with what they operate
158
+
159
+ Group a control with the surface it drives; `medium-12` is the gap between
160
+ *sections*, not between a control and its target.
161
+
162
+ - **Page navigation** (`Navigation` / `NavLink`) changes routes — don't repeat
163
+ those destinations as in-page tabs.
164
+ - **Data navigation** (tabs that swap a table's rows) sits on that table, in
165
+ `Table` `topSlot` first — not under the page title. See [recipe-data-table-page.md](recipe-data-table-page.md).
166
+ - **One-row clusters** (title + page actions, search + export): keep
167
+ `flexWrap="nowrap"` (Layout default). `Lockup` and `Textfield` are `width: 100%`,
168
+ so let the `Lockup` be the flex sibling that shrinks and put `flexShrink={0}` on
169
+ the action cluster. Bound a search field with `width="16rem"` `flexShrink={0}`.
170
+ To stack when tight, switch the header `flexDirection` to `column` below tablet —
171
+ not `flexWrap="wrap"`.
172
+ - **Related form fields** (first/last name, city/state/ZIP): `display="grid"` with
173
+ `fr` tracks and `small-4` gap; collapse to one column when narrow. See
174
+ [recipe-form-section.md](recipe-form-section.md).
175
+
176
+ ## Action clusters
177
+
178
+ Two or more text `Button`s in a `ButtonGroup` sit in a **row from tablet up** and
179
+ a **column below tablet**, using the `direction` prop (with `fullWidth` when
180
+ stacked). Do not add app CSS `flex-direction` or invented breakpoints.
181
+
182
+ ```tsx
183
+ const tabletUp = useBreakpointUp("tablet");
184
+
185
+ <ButtonGroup
186
+ direction={tabletUp ? "row" : "column"}
187
+ fullWidth={!tabletUp}
188
+ aria-label="Form actions"
189
+ >
190
+ ```
191
+
192
+ **Switch** (row/column): form Save/Cancel, dialog confirm/cancel, page
193
+ Delete/Upload, banner CTAs with two+ text buttons.
194
+
195
+ **Stay row** at every width: `Navigation` trailing, a table query bar
196
+ (Filter/Export beside search), icon-only clusters. Do not use the tablet switch to
197
+ restyle card grids — those reflow intrinsically ([recipe-card-grid.md](recipe-card-grid.md)).
198
+
199
+ ## A2UI
200
+
201
+ Chat adapters allow only `var(--uxl-theme-layout-spacing-*)` (or `0`) — no
202
+ breakpoint chrome tokens and no `matchMedia`. Default to a single column; for
203
+ action clusters use `"direction": "column"`. See [a2ui.md](a2ui.md).
@@ -0,0 +1,72 @@
1
+ # Design principles
2
+
3
+ International Typographic (Swiss) principles, each mapped to a concrete uxElle
4
+ mechanism. These apply to every experience. They are **context-aware**: product
5
+ surfaces are maximally calm and dense-but-legible; marketing surfaces are
6
+ expressive, but within the *same* grid, hierarchy, and limited palette.
7
+
8
+ ## Principles -> mechanism
9
+
10
+ - **Grid and alignment.** Lay content on one grid: a capped, centered column
11
+ (`--uxl-breakpoints-max-container-width`, `mh="auto"`, `--uxl-breakpoints-margin`)
12
+ and `fr` tracks with `--uxl-breakpoints-gutter`. Everything aligns to it.
13
+ See [how-to-page-layout.md](how-to-page-layout.md).
14
+ - **Hierarchy.** Lead with one clear focal point per view. Exactly one `h1`,
15
+ unbroken heading ranks, and a small set of `Text` type roles (Display for
16
+ headlines, Body for copy). Let size and weight — not color — carry rank.
17
+ - **Restraint / minimalism.** Prefer fewer elements. Few type roles, few
18
+ accents, one dominant brand band per view. If a divider, box, or color is not
19
+ doing a job, remove it. Whitespace is the default separator.
20
+ - **Whitespace and rhythm.** Space is structural. Use the ramp deliberately:
21
+ tight stacks `micro`, related controls `small-4`, card/section padding
22
+ `medium-8`, page-section gaps `medium-12`, hero breathing `large-13`+.
23
+ See [spacing-steps.md](spacing-steps.md).
24
+ - **Clarity and legibility.** Objective, plain language. Render copy with `Text`
25
+ so type, line-height, and contrast come from the theme; never set font-size or
26
+ hex in app CSS.
27
+ - **Consistency.** Compose catalog components and `--uxl-*` tokens, not bespoke
28
+ markup. The same job looks the same everywhere.
29
+ - **Feedback and state.** Design the empty, loading, and error states, not only
30
+ the happy path. Use `Banner` / status palettes for messages and
31
+ `Skeleton` / `Spinner` for pending work. See [recipe-states.md](recipe-states.md).
32
+ - **Responsiveness.** Mobile-first. Build within the breakpoint system with
33
+ intrinsic CSS (flex-wrap, `minmax`, `auto-fit`) so layouts reflow without px
34
+ literals. See [how-to-page-layout.md](how-to-page-layout.md).
35
+ - **Color discipline.** Product surfaces stay on `default` / `default-subtle`
36
+ plus semantic status; marketing surfaces add brand bands for emphasis. Never
37
+ hardcode color; status is never signaled by color alone. See
38
+ [how-to-color.md](how-to-color.md).
39
+ - **Objectivity, content-first.** Let real content lead. On marketing surfaces
40
+ that means concrete proof — metrics, customer/integration logos, testimonials —
41
+ over decoration.
42
+
43
+ ## Marketing composition (distilled)
44
+
45
+ A dependable rhythm for a public page, each section mapped to a piece. Keep one
46
+ brand accent, generous section spacing, and a strong grid throughout.
47
+
48
+ 1. **Hero** — value proposition + primary/secondary action + product visual
49
+ ([recipe-hero.md](recipe-hero.md)).
50
+ 2. **Trust bar** — customer or integration logos ([recipe-logo-wall.md](recipe-logo-wall.md)).
51
+ 3. **Feature sections** — alternating media + copy, optionally tab-switched
52
+ ([recipe-feature-section.md](recipe-feature-section.md)).
53
+ 4. **Stat tiles** — a few large figures with labels ([recipe-stat-callouts.md](recipe-stat-callouts.md)).
54
+ 5. **Testimonials** — quote + attribution ([recipe-testimonial.md](recipe-testimonial.md)).
55
+ 6. **Pricing** — comparable tiers with one recommended ([recipe-pricing.md](recipe-pricing.md)).
56
+ 7. **Closing CTA** — a single decisive action ([recipe-cta-band.md](recipe-cta-band.md)).
57
+ 8. **Footer** — categorized links and legal ([recipe-footer.md](recipe-footer.md)).
58
+
59
+ The whole flow is assembled in [recipe-landing-page.md](recipe-landing-page.md).
60
+
61
+ ## Product vs marketing
62
+
63
+ | | Product | Marketing |
64
+ |---|---|---|
65
+ | Palette | `default` / `default-subtle` + status | + brand bands for emphasis |
66
+ | Density | Compact, information-dense | Airy, one idea per band |
67
+ | Type | Restrained; Body-led | Expressive; Display-led headlines |
68
+ | Motion / accents | Minimal | `DynamicAngle*` accents, still restrained |
69
+ | Goal | Get the task done | Build trust and convert |
70
+
71
+ Same grid, same tokens, same components. The difference is emphasis, not a
72
+ different system.
@@ -0,0 +1,168 @@
1
+ # Recipe: app chrome
2
+
3
+ ## When
4
+
5
+ React app frame: skip link, [`Navigation`](../uxelle-components/Navigation.md), scrolling `<main>`, optional [`Footer`](../uxelle-components/Footer.md). Serves both product and marketing shells.
6
+
7
+ ## When not
8
+
9
+ - Not an `AppChrome` / `PageShell` component.
10
+ - Theme CSS and `data-*`: [how-to-host.md](how-to-host.md).
11
+ - A2UI chat surfaces: skip link, 100% viewport shell, and html `data-*` are host-owned. Do not emit this recipe as A2UI. Optional `A2uiNavigation` / `A2uiFooter` only when the host already has a surface.
12
+
13
+ ## Regions
14
+
15
+ skip link → `Navigation` (header) and/or `NavigationSide` (side rail wrapping `NavigationSideGroup` rows) → `<main id="main-content">` → content column ([how-to-page-layout.md](how-to-page-layout.md)) → optional `Footer` **inside that same `<main>`**
16
+
17
+ ## Spacing
18
+
19
+ Nav is a catalog component (its own tokens). Main is the scrollport (no page padding). The **page column inside main** uses breakpoint margin / max-container-width (one-column settings may use `600px` — [recipe-settings-page.md](recipe-settings-page.md)). When present, `Footer` sits after that column in the same scrollport.
20
+
21
+ Do not wrap `Footer` in extra `Layout` padding. Band padding is catalog-owned: two-band footers use `--uxl-breakpoints-padding-large-13`; a **single** band (legal-only, unused slot `null`) uses `--uxl-theme-layout-spacing-small-4` on the block axis. That is the related-controls step, not page-section (`medium-12`) or hero (`large-13`).
22
+
23
+ ## A11y
24
+
25
+ [how-to-accessibility.md](how-to-accessibility.md) and skip-link CSS in [how-to-host.md](how-to-host.md). One `main`. `aria-current="page"` on the current `NavLink` or `NavigationSideItem` / `NavigationSideSubItem` (`activated`). Decorative `Logo` / `Icon` beside a visible product name: `aria-hidden`.
26
+
27
+ `NavLink` in `Navigation` is **header** page navigation. `NavigationSide` is the side-rail **chrome** (`<aside>`, or a `<header>` when `mobile`). `NavigationSideGroup` is the **side** navigation landmark for the experience (a named `<nav>` of destination rows) and belongs in `NavigationSide` `centerSlotContent`. Do not wrap `NavigationSide` or `NavigationSideGroup` in another `nav`. Pass `aria-label` when more than one group is on the page. Accordion parents are not destinations — put section landing pages on `NavigationSideSubItem` children. Tabs that only change which rows appear in a table are **data** navigation — put them on the table ([recipe-data-table-page.md](recipe-data-table-page.md)), not in `centerSlot` and not as a second nav under the page title.
28
+
29
+ ## Side rail
30
+
31
+ Compose `NavigationSide` with `centerSlotContent` of `NavigationSideGroup` + `NavigationSideItem` (optional `accordion` with `NavigationSideSubItem` children). Set `expanded={false}` on `NavigationSide` for the icon-only rail (groups inherit collapsed layout). SSR hosts must pass `mobile={true}` or `mobile={false}` so server and client markup match. Omit `mobile` only in client-only surfaces to follow the theme mobile range. Do not hard-code viewport widths. Trailing slot content on destination rows must be non-interactive. When the mobile layout is shown, the menu control opens a right `Sheet` with the same `centerSlotContent` and `bottomSlotContent` as the rail (labeled rows). Use `menuOpen` / `onMenuOpenChange` to close it after navigation.
32
+
33
+ ## Footer
34
+
35
+ Optional — omit `Footer` when the page has none; place it **inside** `<main>` after the page column so it scrolls with the page. Pass **only the bands this experience needs**: `null` hides a band (same pattern as Navigation `bottomSlot={null}`), real nodes fill one; never leave a slot off in generated UI (omitted `undefined` fills catalog demo content). Do not wrap `Footer` in extra `p` / `gap` — band padding is catalog-owned (single band `small-4`, two bands the large step).
36
+
37
+ Footer content architecture — the brand block, categorized link columns, the legal/utility bar, and how to lay each band out as a tidy cluster — lives in [recipe-footer.md](recipe-footer.md).
38
+
39
+ ## React
40
+
41
+ Import from `@uxelle/components`. Put skip-link CSS in the host stylesheet.
42
+
43
+ ```tsx
44
+ <Layout
45
+ display="flex"
46
+ flexDirection="column"
47
+ height="100%"
48
+ style={{
49
+ backgroundColor: "var(--uxl-color-switcher-background)",
50
+ overflow: "hidden",
51
+ }}
52
+ >
53
+ <a className="uxl-skip-link" href="#main-content">
54
+ Skip to main content
55
+ </a>
56
+ <Layout flexShrink={0}>
57
+ <Navigation
58
+ bottomSlot={null}
59
+ leadingSlot={
60
+ <Text type="Condensed Alt" truncation width={false}>
61
+ Product name
62
+ </Text>
63
+ }
64
+ centerSlot={
65
+ <>
66
+ <NavLink label="Home" href="/" aria-current="page" />
67
+ <NavLink label="Settings" href="/settings" />
68
+ </>
69
+ }
70
+ trailingSlot={
71
+ <IconButton emphasis="low" size="small" iconName="search" aria-label="Search" />
72
+ }
73
+ />
74
+ </Layout>
75
+ <main id="main-content" tabIndex={-1} style={{ flexGrow: 1, minHeight: 0, overflow: "auto" }}>
76
+ <Layout
77
+ display="flex"
78
+ flexDirection="column"
79
+ width="100%"
80
+ maxWidth="var(--uxl-breakpoints-max-container-width)"
81
+ mh="auto"
82
+ p="var(--uxl-breakpoints-margin)"
83
+ gap="var(--uxl-theme-layout-spacing-medium-12)"
84
+ >
85
+ {/* page body — recipe-page-shell.md / recipe-form-section.md / recipe-data-table-page.md / recipe-settings-page.md */}
86
+ </Layout>
87
+ <Footer
88
+ topMainContent={null}
89
+ bottomMainContent={
90
+ <Text type="Body Small" width={false}>
91
+ © Organization
92
+ </Text>
93
+ }
94
+ />
95
+ </main>
96
+ </Layout>
97
+ ```
98
+
99
+ ## Responsive
100
+
101
+ - **Primary nav collapses on mobile.** Below tablet, swap the center [`NavLink`](../uxelle-components/NavLink.md) set for an [`IconButton`](../uxelle-components/IconButton.md) (`iconName="menu"`, `aria-label="Menu"`) in `trailingSlot` that opens a [`Sheet`](../uxelle-components/Sheet.md) (or [`Menu`](../uxelle-components/Menu.md)) holding the same destinations as a `List` of `NavLink`s. Confirm `Navigation`'s built-in behavior first and only add the drawer for what it does not handle; switch with `useBreakpointUp("tablet")` ([how-to-page-layout.md](how-to-page-layout.md#responsiveness)).
102
+ - **Trailing utilities stay a row** at every width (search, language, Log in / Register) — [how-to-page-layout.md](how-to-page-layout.md#action-clusters).
103
+ - **Footer** columns wrap intrinsically ([recipe-footer.md](recipe-footer.md)); band padding is catalog-owned. **Content column** reflows through the chrome tokens with nothing to set per breakpoint.
104
+
105
+ ## A2UI
106
+
107
+ When-not for this recipe. Host owns chrome (skip link, viewport shell, html `data-*`). Catalog adapters: `A2uiNavigation`, `A2uiFooter`, `A2uiBannerAnnouncement` if the surface needs them — no `getUxelleRecipe("app-chrome")`. For the side rail only, `getUxelleRecipe("navigation-side")` from `@uxelle/a2ui`: trailing badges are `A2uiLabelBadge` with `"emphasis": "high"`; the account row is `A2uiList` + `A2uiListItem`. See [a2ui.md](a2ui.md).
108
+
109
+ ## Help and marketing chrome
110
+
111
+ Use the same `Navigation` when there are **no** primary app links (help center, marketing). Pass `bottomSlot={null}` to hide the secondary bar. Unused **primary** slots must not be omitted — omitted (`undefined`) or `null` on leading/center/trailing still fill catalog demo content (`??`). Pass `centerSlot={false}` when there is no center nav.
112
+
113
+ Nav trailing (search, language, Log in / Register) stays a **row** at every width — [how-to-page-layout.md](how-to-page-layout.md#action-clusters). Do not stack those controls on mobile.
114
+
115
+ `Breadcrumbs` belong in the **page column**, not in `Navigation`. Related articles are an in-page `Link` list under the article, not nav or crumbs.
116
+
117
+ ```tsx
118
+ <Navigation
119
+ bottomSlot={null}
120
+ leadingSlot={
121
+ <a href="/" aria-label="Home">
122
+ <Logo name="generic" width={40} interactive aria-hidden />
123
+ </a>
124
+ }
125
+ centerSlot={false}
126
+ trailingSlot={
127
+ <>
128
+ <LanguageSelector value="EN" />
129
+ <Button emphasis="low" size="small">
130
+ <Text type="Button" width>
131
+ Log in
132
+ </Text>
133
+ </Button>
134
+ <Button emphasis="high" size="small">
135
+ <Text type="Button" width>
136
+ Register
137
+ </Text>
138
+ </Button>
139
+ </>
140
+ }
141
+ />
142
+ ```
143
+
144
+ In the page column, before the `h1`:
145
+
146
+ ```tsx
147
+ <Breadcrumbs>
148
+ <Breadcrumb label="Help home" href="/" />
149
+ <Breadcrumb label="Sending money" href="/sending-money" />
150
+ <Breadcrumb label="Can I send exact amounts?" />
151
+ </Breadcrumbs>
152
+ ```
153
+
154
+ Last `Breadcrumb` is the current page (`currentPage` or no `href`). Ancestor crumbs pass `href`.
155
+
156
+ After the article body, related links stay in the page column:
157
+
158
+ ```tsx
159
+ <Layout as="section" display="flex" flexDirection="column" gap="var(--uxl-theme-layout-spacing-small-4)">
160
+ <Text type="Display Extra Small" as="h2" width={false}>
161
+ Related articles
162
+ </Text>
163
+ <Layout display="flex" flexDirection="column" gap="var(--uxl-theme-layout-spacing-micro-2)">
164
+ <Link href="/help/batch-payments">Sending exact amounts — Batch payments</Link>
165
+ <Link href="/help/guaranteed-rate">What if I miss my guaranteed rate?</Link>
166
+ </Layout>
167
+ </Layout>
168
+ ```
@@ -0,0 +1,104 @@
1
+ # Recipe: card grid
2
+
3
+ A shared **piece**: a row of equal cells that reflows by width. Used for feature /
4
+ benefit grids, case-study or product tiles, and dashboard groupings.
5
+
6
+ Composes [`Card`](../uxelle-components/Card.md) (single content surface) or
7
+ [`ProductCard`](../uxelle-components/ProductCard.md) (fixed top / center / bottom
8
+ regions), plus [`Text`](../uxelle-components/Text.md),
9
+ [`LabelBadge`](../uxelle-components/LabelBadge.md), and
10
+ [`Image`](../uxelle-components/Image.md).
11
+
12
+ ## When
13
+
14
+ Three-ish comparable cells in a row on a React page — features, benefits, case
15
+ studies, product tiles, or dashboard cards.
16
+
17
+ ## When not
18
+
19
+ - Not a `CardGrid` component — it is a `Layout` grid of cards.
20
+ - Use `ProductCard` only when the tile needs fixed top / center / bottom regions;
21
+ otherwise `Card`.
22
+ - Do not use `--uxl-breakpoints-margin` as the gap (that token is page inset) — use
23
+ `--uxl-breakpoints-gutter`.
24
+
25
+ ## Regions
26
+
27
+ page column -> grid (`auto-fit` / `minmax` tracks, `gap` = gutter) -> `Card` /
28
+ `ProductCard` cells
29
+
30
+ ## Responsive
31
+
32
+ Reflows intrinsically — no JS, no breakpoint literals. `repeat(auto-fit,
33
+ minmax(MIN, 1fr))` drops from 3-up to 2-up to 1-up as width shrinks; pick `MIN`
34
+ (e.g. `16rem`) for the smallest comfortable card. Cards fill the row at every width.
35
+
36
+ ## Spacing
37
+
38
+ `gap="var(--uxl-breakpoints-gutter)"`. Card inner padding is component-owned. The
39
+ page column still uses [how-to-page-layout.md](how-to-page-layout.md).
40
+
41
+ ## A11y
42
+
43
+ Name the group when the cards are a set (`aria-label` on the grid `Layout`).
44
+ Headings inside cards continue page rank (`h2` under the page `h1`). A `ProductCard`
45
+ CTA names itself; if the whole card is the link, the heading text is the name. See
46
+ [how-to-accessibility.md](how-to-accessibility.md).
47
+
48
+ ## React
49
+
50
+ Feature / benefit grid (`Card`):
51
+
52
+ ```tsx
53
+ <Layout
54
+ display="grid"
55
+ gridTemplateColumns="repeat(auto-fit, minmax(16rem, 1fr))"
56
+ gap="var(--uxl-breakpoints-gutter)"
57
+ aria-label="Highlights"
58
+ >
59
+ <Card
60
+ cardContentSlot={
61
+ <Layout display="flex" flexDirection="column" gap="var(--uxl-theme-layout-spacing-small-4)">
62
+ <Icon iconName="bolt" variant="sharpUnfilled" aria-hidden />
63
+ <Text type="Display Extra Small" as="h2" width={false}>Fast by default</Text>
64
+ <Text type="Body Medium" width={false}>Ship in minutes with sensible presets.</Text>
65
+ </Layout>
66
+ }
67
+ />
68
+ {/* repeat Card cells */}
69
+ </Layout>
70
+ ```
71
+
72
+ Case-study / product tiles (`ProductCard`, media + CTA):
73
+
74
+ ```tsx
75
+ <Layout
76
+ display="grid"
77
+ gridTemplateColumns="repeat(auto-fit, minmax(18rem, 1fr))"
78
+ gap="var(--uxl-breakpoints-gutter)"
79
+ aria-label="Customer stories"
80
+ >
81
+ <ProductCard
82
+ topSlotContent={
83
+ <>
84
+ <LabelBadge label="Case study" emphasis="low" />
85
+ <Text type="Display Extra Small" as="h2" width={false}>40% faster close</Text>
86
+ <Text type="Body Medium" width={false}>How Northwind cut cycle time.</Text>
87
+ </>
88
+ }
89
+ centerSlotContent={<Image src="/case/northwind.jpg" alt="" aspectRatio="16:9" />}
90
+ bottomSlotContent={
91
+ <Button fullWidth emphasis="medium">
92
+ <Text type="Button" width>Read story</Text>
93
+ </Button>
94
+ }
95
+ />
96
+ {/* repeat ProductCard cells */}
97
+ </Layout>
98
+ ```
99
+
100
+ ## A2UI
101
+
102
+ `A2uiLayout` has no `gridTemplateColumns` — stack `A2uiCard` / `A2uiProductCard` in
103
+ a column with `var(--uxl-theme-layout-spacing-*)` `gap`. No
104
+ `getUxelleRecipe("card-grid")` ([a2ui.md](a2ui.md)).
@@ -0,0 +1,91 @@
1
+ # Recipe: CTA band
2
+
3
+ A marketing **piece**: a full-width brand band near the end of a page with one
4
+ decisive action. The closing counterpart to the [hero](recipe-hero.md).
5
+
6
+ Composes [`Layout`](../uxelle-components/Layout.md) (the band),
7
+ [`Lockup`](../uxelle-components/Lockup.md),
8
+ [`ButtonGroup`](../uxelle-components/ButtonGroup.md), and optionally a
9
+ [`DynamicAngle*`](../uxelle-components/DynamicAngleShapeSurface.md) accent.
10
+
11
+ ## When
12
+
13
+ A single, decisive conversion moment: "Start free," "Talk to sales."
14
+
15
+ ## When not
16
+
17
+ - One primary action (an optional secondary is fine) — not a menu of links.
18
+ - Section heading is `h2`, not another `h1`.
19
+ - Keep it to one dominant brand band per page; if the hero is loud, this can be
20
+ quieter, or vice versa.
21
+
22
+ ## Regions
23
+
24
+ full-bleed `section` (`data-color-switcher`) -> capped inner column -> `Lockup`
25
+ (`h2` + body) + `ButtonGroup`, optional decorative `DynamicAngle*`
26
+
27
+ ## Responsive
28
+
29
+ The band is full-bleed; the inner column caps with
30
+ `--uxl-breakpoints-max-container-width` and centers. Actions are a row on wide
31
+ viewports and stack `fullWidth` below tablet (`useBreakpointUp("tablet")`). See
32
+ [how-to-page-layout.md](how-to-page-layout.md#action-clusters).
33
+
34
+ ## Spacing
35
+
36
+ `large-13`+ block padding for the band; `medium-8` between the lockup and actions
37
+ ([spacing-steps.md](spacing-steps.md)).
38
+
39
+ ## A11y
40
+
41
+ Section `h2`; name the region with it. A `DynamicAngle*` accent is decorative
42
+ (`aria-hidden`) and is muted under `prefers-reduced-motion`. Style all text with role
43
+ tokens so contrast holds on the palette. See [how-to-accessibility.md](how-to-accessibility.md).
44
+
45
+ ## Color
46
+
47
+ Set `data-color-switcher` on the **section** and style with `--uxl-color-switcher-*`
48
+ role tokens — never hardcode hex. Pick the brand palette by the look you want
49
+ ([how-to-color.md](how-to-color.md)).
50
+
51
+ ## React
52
+
53
+ ```tsx
54
+ <Layout
55
+ as="section"
56
+ data-color-switcher="brand-1"
57
+ display="flex"
58
+ justifyContent="center"
59
+ p="var(--uxl-theme-layout-spacing-large-13)"
60
+ style={{ backgroundColor: "var(--uxl-color-switcher-background)" }}
61
+ >
62
+ <Layout
63
+ display="flex"
64
+ flexDirection="column"
65
+ alignItems="center"
66
+ gap="var(--uxl-theme-layout-spacing-medium-8)"
67
+ width="100%"
68
+ maxWidth="var(--uxl-breakpoints-max-container-width)"
69
+ >
70
+ <Lockup
71
+ topSlot={false}
72
+ title={<Text type="Display Medium" as="h2" width={false}>Ready to ship faster?</Text>}
73
+ body={<Text type="Body Medium" width={false}>Start free — no card required.</Text>}
74
+ />
75
+ <ButtonGroup
76
+ direction={tabletUp ? "row" : "column"}
77
+ fullWidth={!tabletUp}
78
+ aria-label="Get started"
79
+ >
80
+ <Button emphasis="high"><Text type="Button" width>Start free</Text></Button>
81
+ <Button emphasis="medium"><Text type="Button" width>Talk to sales</Text></Button>
82
+ </ButtonGroup>
83
+ </Layout>
84
+ </Layout>
85
+ ```
86
+
87
+ ## A2UI
88
+
89
+ No `getUxelleRecipe("cta-band")` — wrap an `A2uiLayout` (with `data-color-switcher`)
90
+ around stacked `A2uiText` and an `A2uiButtonGroup` (`"direction": "column"`). See
91
+ [a2ui.md](a2ui.md).