@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,156 @@
|
|
|
1
|
+
# Recipe: footer
|
|
2
|
+
|
|
3
|
+
A shared **piece**: the page footer — a directory of links anchored by brand
|
|
4
|
+
identity, over a quiet legal/utility bar. Used by [app-chrome](recipe-app-chrome.md)
|
|
5
|
+
(product) and [landing-page](recipe-landing-page.md) (marketing).
|
|
6
|
+
|
|
7
|
+
Composes [`Footer`](../uxelle-components/Footer.md) (two bands),
|
|
8
|
+
[`Layout`](../uxelle-components/Layout.md),
|
|
9
|
+
[`Text`](../uxelle-components/Text.md), [`Link`](../uxelle-components/Link.md),
|
|
10
|
+
[`Logo`](../uxelle-components/Logo.md), and — for marketing — a
|
|
11
|
+
[`Button`](../uxelle-components/Button.md) CTA and social marks.
|
|
12
|
+
|
|
13
|
+
## When
|
|
14
|
+
|
|
15
|
+
The closing chrome of a page: wayfinding to the rest of the site, plus identity and
|
|
16
|
+
legal. Place `Footer` **inside** `<main>` after the page column so it scrolls with the
|
|
17
|
+
page ([app-chrome](recipe-app-chrome.md)).
|
|
18
|
+
|
|
19
|
+
## When not
|
|
20
|
+
|
|
21
|
+
- Not primary navigation — top-level app routes stay in `Navigation`
|
|
22
|
+
([app-chrome](recipe-app-chrome.md)).
|
|
23
|
+
- Omit `Footer` entirely on focused flows (checkout, auth) that should not invite
|
|
24
|
+
exit.
|
|
25
|
+
- Pass real content to the bands you use; omitting a slot (`undefined`) fills catalog
|
|
26
|
+
demo content, and `null` hides a band.
|
|
27
|
+
|
|
28
|
+
## Information architecture
|
|
29
|
+
|
|
30
|
+
The ideals the layout serves — adapt them, don't copy any one site.
|
|
31
|
+
|
|
32
|
+
- **Anchor with identity.** A brand block (logo + a one-line tagline or mission,
|
|
33
|
+
optionally a CTA and social marks) grounds the footer; on marketing it adds a last
|
|
34
|
+
conversion nudge.
|
|
35
|
+
- **Group links by user intent.** A handful of labeled categories — what you offer,
|
|
36
|
+
resources, company, legal/trust — each a heading over a vertical link list. Name
|
|
37
|
+
groups for how people look for things, not your org chart. Sub-group a large
|
|
38
|
+
category instead of spawning another top-level column.
|
|
39
|
+
- **Separate a quiet utility bar.** Copyright and registration, legal links
|
|
40
|
+
(privacy / terms / cookie / disclosures), and social or app badges sit in a
|
|
41
|
+
lower-emphasis band beneath the directory.
|
|
42
|
+
- **Scale density to the surface.** The same architecture ranges from a compact
|
|
43
|
+
legal-only bar (a focused product app) up to a rich multi-column directory (a
|
|
44
|
+
marketing site). Add columns as the story grows; don't force a big directory onto a
|
|
45
|
+
small app.
|
|
46
|
+
- **Isolate fine print.** Regulated or legal-heavy products add a dense
|
|
47
|
+
disclosure/disclaimer block — keep it last, low-emphasis, and out of the primary
|
|
48
|
+
link flow.
|
|
49
|
+
- **Make outbound obvious.** Mark external or cross-property links (e.g. a trailing
|
|
50
|
+
outbound icon) so they read as leaving.
|
|
51
|
+
|
|
52
|
+
## Regions
|
|
53
|
+
|
|
54
|
+
`Footer` `topMainContent` (the **directory**): brand block + categorized link
|
|
55
|
+
columns, optional CTA and social · `Footer` `bottomMainContent` (the **utility
|
|
56
|
+
bar**): copyright + legal links, optional social; optional fine-print block last.
|
|
57
|
+
|
|
58
|
+
The component places logo/legal in the bottom band and links/CTAs/social in the top
|
|
59
|
+
band by default — follow that grain unless the design needs otherwise.
|
|
60
|
+
|
|
61
|
+
## Lay out the bands as tidy clusters
|
|
62
|
+
|
|
63
|
+
Each band reads as a cluster, not content stretched to the band edges.
|
|
64
|
+
|
|
65
|
+
- **Directory (top band).** Lead with the brand block, then the link groups. With a
|
|
66
|
+
few groups (2–4), lay them out as a **left-aligned wrapping cluster** with intrinsic
|
|
67
|
+
widths and `--uxl-breakpoints-gutter` between them — do **not** stretch a couple of
|
|
68
|
+
groups across the full width with `auto-fit` / `1fr` tracks (two `1fr` columns leave
|
|
69
|
+
a hollow center that reads off-center). Reserve
|
|
70
|
+
`repeat(auto-fit, minmax(MIN, 1fr))` for a **dense** directory with enough groups to
|
|
71
|
+
fill the row.
|
|
72
|
+
|
|
73
|
+
```tsx
|
|
74
|
+
<Layout display="flex" flexWrap="wrap" gap="var(--uxl-breakpoints-gutter)">
|
|
75
|
+
{/* brand block leads */}
|
|
76
|
+
<Layout display="flex" flexDirection="column" gap="var(--uxl-theme-layout-spacing-small-4)">
|
|
77
|
+
<a href="/" aria-label="Home">
|
|
78
|
+
<Logo name="generic" width={40} interactive aria-hidden />
|
|
79
|
+
</a>
|
|
80
|
+
<Text type="Body Small" width={false}>One-line tagline or mission.</Text>
|
|
81
|
+
{/* optional CTA on marketing */}
|
|
82
|
+
<Button emphasis="high"><Text type="Button" width>Book a demo</Text></Button>
|
|
83
|
+
</Layout>
|
|
84
|
+
|
|
85
|
+
{/* each category is a content-sized column — repeat per group */}
|
|
86
|
+
<Layout display="flex" flexDirection="column" gap="var(--uxl-theme-layout-spacing-micro-2)">
|
|
87
|
+
<Text type="Body Medium Alt" width={false}>Product</Text>
|
|
88
|
+
<Link href="/how-it-works">How it works</Link>
|
|
89
|
+
<Link href="/pricing">Pricing</Link>
|
|
90
|
+
<Link href="/compare">Compare</Link>
|
|
91
|
+
</Layout>
|
|
92
|
+
</Layout>
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
- **Utility bar (bottom band).** Keep copyright and legal links in a **left-aligned
|
|
96
|
+
wrapping row** (or stack), separated by the spacing ramp — prefer this over
|
|
97
|
+
`justifyContent="space-between"`, which pins content to both edges and hollows the
|
|
98
|
+
center (worse when it wraps on mobile). A two-end split (copyright left, social
|
|
99
|
+
right) is fine only when **both** sides carry enough to avoid that void.
|
|
100
|
+
|
|
101
|
+
## Responsive
|
|
102
|
+
|
|
103
|
+
Few groups wrap from a single row to stacked as width narrows (no JS); a dense
|
|
104
|
+
directory uses `repeat(auto-fit, minmax(MIN, 1fr))` to shed columns. The brand block
|
|
105
|
+
leads. The legal bar wraps its links. See
|
|
106
|
+
[how-to-page-layout.md](how-to-page-layout.md#responsiveness).
|
|
107
|
+
|
|
108
|
+
## Spacing
|
|
109
|
+
|
|
110
|
+
`--uxl-breakpoints-gutter` between columns; `micro-2` within a link list; `small-4`
|
|
111
|
+
inside the brand block and the legal bar. Band padding is component-owned — a single
|
|
112
|
+
remaining band is compact (`small-4`), two bands use the large step; do not wrap
|
|
113
|
+
`Footer` in extra `p` / `gap`.
|
|
114
|
+
|
|
115
|
+
## A11y
|
|
116
|
+
|
|
117
|
+
`Footer` is the page `contentinfo` landmark (one per page). Head each link group so
|
|
118
|
+
its purpose is clear; wrap a substantial directory (or each column) in a named `nav`.
|
|
119
|
+
Social/app links carry an accessible name; a `Logo` beside a visible brand name is
|
|
120
|
+
decorative (`aria-hidden`). See [how-to-accessibility.md](how-to-accessibility.md).
|
|
121
|
+
|
|
122
|
+
## Color
|
|
123
|
+
|
|
124
|
+
`Footer` sets each band's palette itself (top vs bottom) — style content with role
|
|
125
|
+
tokens and let the component own the surface; don't fight it with hardcoded color
|
|
126
|
+
([how-to-color.md](how-to-color.md)).
|
|
127
|
+
|
|
128
|
+
## React
|
|
129
|
+
|
|
130
|
+
Assemble the two bands into `Footer`. A legal-only footer (`topMainContent={null}`) is
|
|
131
|
+
the product default; add the directory band as the surface grows.
|
|
132
|
+
|
|
133
|
+
```tsx
|
|
134
|
+
<Footer
|
|
135
|
+
topMainContent={/* directory cluster from above, or null for legal-only */ null}
|
|
136
|
+
bottomMainContent={
|
|
137
|
+
<Layout display="flex" flexWrap="wrap" alignItems="center" gap="var(--uxl-theme-layout-spacing-small-4)">
|
|
138
|
+
<Text type="Body Small" width={false}>© 2026 Organization. All rights reserved.</Text>
|
|
139
|
+
<Layout as="nav" aria-label="Legal" display="flex" flexWrap="wrap" gap="var(--uxl-theme-layout-spacing-small-4)">
|
|
140
|
+
<Link href="/privacy">Privacy</Link>
|
|
141
|
+
<Link href="/terms">Terms of Use</Link>
|
|
142
|
+
<Link href="/cookies">Cookie Settings</Link>
|
|
143
|
+
</Layout>
|
|
144
|
+
</Layout>
|
|
145
|
+
}
|
|
146
|
+
/>
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
`DefaultTopMainContent` / `DefaultBottomMainContent` from `@uxelle/components` give a
|
|
150
|
+
quick start; generated UI passes real nodes.
|
|
151
|
+
|
|
152
|
+
## A2UI
|
|
153
|
+
|
|
154
|
+
No `getUxelleRecipe("footer")` — compose `A2uiFooter` with the same two slots, links
|
|
155
|
+
as `A2uiLink`, headings/copyright as `A2uiText` (no `Lockup`), layout-spacing only.
|
|
156
|
+
See [a2ui.md](a2ui.md).
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# Recipe: form section
|
|
2
|
+
|
|
3
|
+
## When
|
|
4
|
+
|
|
5
|
+
A labeled **group of fields** under a heading — stacking rule, not a new form component. Preference toggles and choice lists: [recipe-settings-page.md](recipe-settings-page.md) (`ListControls`).
|
|
6
|
+
|
|
7
|
+
## When not
|
|
8
|
+
|
|
9
|
+
- Do not wrap fields that already ship Label + FieldMessage (`Textfield`, `Textarea`, `Select`, `MultiSelect`, `FileUpload`).
|
|
10
|
+
- Do not use `Textfield` for comments, notes, bios, or other multi-line copy — that is [`Textarea`](../uxelle-components/Textarea.md).
|
|
11
|
+
- Do not fake exclusive choice with `ListItem` clicks.
|
|
12
|
+
- Do not flatten `RadioGroup` / `CheckboxGroup` (they are already a `fieldset`).
|
|
13
|
+
- Do not collapse given name + family name (or city/state/ZIP, month/day/year) into one field.
|
|
14
|
+
- `Layout` has no `as="form"`. Host wraps a native `<form>` when submitting.
|
|
15
|
+
|
|
16
|
+
## Allowed children
|
|
17
|
+
|
|
18
|
+
Any catalog field: [`Textfield`](../uxelle-components/Textfield.md), [`Textarea`](../uxelle-components/Textarea.md), [`Select`](../uxelle-components/Select.md), [`MultiSelect`](../uxelle-components/MultiSelect.md), [`FileUpload`](../uxelle-components/FileUpload.md), [`CheckboxGroup`](../uxelle-components/CheckboxGroup.md), [`RadioGroup`](../uxelle-components/RadioGroup.md), [`Switch`](../uxelle-components/Switch.md), [`ListControls`](../uxelle-components/ListControls.md), [`Quantity`](../uxelle-components/Quantity.md) when present.
|
|
19
|
+
|
|
20
|
+
## Multi-line
|
|
21
|
+
|
|
22
|
+
`Textarea` for free-form copy (notes, comments, bio). `autoGrow` sizes the field to its content — `rows` (or `height`) is the minimum, `maxHeight` caps it, and the resize grip is hidden. `counter` + `maxLength` is a hard stop; add `softOverflow` to let the user type past the limit. Past the limit, the counter uses `data-color-switcher="warning"` (numbers still show `n/max`; do not set `aria-invalid` from overflow). Pass `softOverflowMessage` for the warning `FieldMessage` — do not rely on catalog copy. Search, email, and other single-line values stay on `Textfield`.
|
|
23
|
+
|
|
24
|
+
## Spacing
|
|
25
|
+
|
|
26
|
+
`gap="var(--uxl-theme-layout-spacing-medium-8)"` between fields (or field **rows**). Header stack `var(--uxl-theme-layout-spacing-micro-2)`. Actions in `ButtonGroup` (not ad-hoc `Layout` for two buttons) — row from tablet up, column below tablet ([how-to-page-layout.md](how-to-page-layout.md#action-clusters)). Parts of one value on one row: `small-4` inside the grid.
|
|
27
|
+
|
|
28
|
+
**Separate the actions from the fields.** The submit / action cluster is a decision point, not one more field — give it `medium-12` from the last field (the page-section gap), not the `medium-8` field gap. At the field gap the primary button reads as grouped with the control above it (e.g. a submit sitting right under the last `Select`). Group the heading + fields in one stack and make the actions a sibling at `medium-12` (see the React example).
|
|
29
|
+
|
|
30
|
+
## Measure
|
|
31
|
+
|
|
32
|
+
A **standalone** form section on a wider page (a marketing demo request, a contact form) caps the whole section — heading, fields, and actions — at the `maxWidth="600px"` reading measure and centers it with `mh="auto"`, so the heading sits directly above the fields on one left edge:
|
|
33
|
+
|
|
34
|
+
```tsx
|
|
35
|
+
<Layout
|
|
36
|
+
as="section"
|
|
37
|
+
display="flex"
|
|
38
|
+
flexDirection="column"
|
|
39
|
+
gap="var(--uxl-theme-layout-spacing-medium-12)"
|
|
40
|
+
width="100%"
|
|
41
|
+
maxWidth="600px"
|
|
42
|
+
mh="auto"
|
|
43
|
+
>
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Cap the **section**, not just the inner field stack — capping only the fields leaves the heading full-width and the form stranded left of center. Match this measure to sibling reading sections (an FAQ above it) so they read as one column ([how-to-page-layout.md](how-to-page-layout.md#reading-measure)). A settings page is already one `600px` column — do not nest another.
|
|
47
|
+
|
|
48
|
+
## Related fields
|
|
49
|
+
|
|
50
|
+
Parts of **one value** sit on one row. Separate fields — not a single “Full name” / “Address” / “Date” string. Use `display="grid"` (`Textfield` / `Select` are `width: 100%` of the parent). Do not `flexWrap="wrap"` them. Account pages: [recipe-settings-page.md](recipe-settings-page.md).
|
|
51
|
+
|
|
52
|
+
```tsx
|
|
53
|
+
<Layout
|
|
54
|
+
display="grid"
|
|
55
|
+
gridTemplateColumns="1fr 1fr"
|
|
56
|
+
gap="var(--uxl-theme-layout-spacing-small-4)"
|
|
57
|
+
>
|
|
58
|
+
<Textfield labelText="First name" name="givenName" autoComplete="given-name" />
|
|
59
|
+
<Textfield labelText="Last name" name="familyName" autoComplete="family-name" />
|
|
60
|
+
</Layout>
|
|
61
|
+
|
|
62
|
+
<Layout
|
|
63
|
+
display="grid"
|
|
64
|
+
gridTemplateColumns="2fr 1fr 1fr"
|
|
65
|
+
gap="var(--uxl-theme-layout-spacing-small-4)"
|
|
66
|
+
>
|
|
67
|
+
<Textfield labelText="City" name="city" autoComplete="address-level2" />
|
|
68
|
+
<Select labelText="State" name="state" options={stateOptions} />
|
|
69
|
+
<Textfield labelText="ZIP" name="postalCode" autoComplete="postal-code" />
|
|
70
|
+
</Layout>
|
|
71
|
+
|
|
72
|
+
<Layout
|
|
73
|
+
display="grid"
|
|
74
|
+
gridTemplateColumns="1fr 1fr 1fr"
|
|
75
|
+
gap="var(--uxl-theme-layout-spacing-small-4)"
|
|
76
|
+
>
|
|
77
|
+
<Select labelText="Month" name="month" options={monthOptions} />
|
|
78
|
+
<Select labelText="Day" name="day" options={dayOptions} />
|
|
79
|
+
<Select labelText="Year" name="year" options={yearOptions} />
|
|
80
|
+
</Layout>
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
A2UI: `A2uiLayout` has no `gridTemplateColumns` — stack these fields. Do not put unrelated fields on the same row.
|
|
84
|
+
|
|
85
|
+
## Dialog
|
|
86
|
+
|
|
87
|
+
When this section is `Dialog` `centerSlotContent`, `Text` in `topSlotContent` and `centerSlotContent` uses `width={false}` (100% of the slot). `width` / `width={true}` shrinks to the string. Button labels stay `width` (fit). A2UI: `"width": false` on `A2uiText` in those slots.
|
|
88
|
+
|
|
89
|
+
## A11y
|
|
90
|
+
|
|
91
|
+
[how-to-accessibility.md](how-to-accessibility.md). Section heading (typically `h2` under a page `h1`). Groups use the group component’s legend. Actions in a labeled `ButtonGroup` (`aria-label`). Action `direction`: [how-to-page-layout.md](how-to-page-layout.md#action-clusters).
|
|
92
|
+
|
|
93
|
+
## Responsive
|
|
94
|
+
|
|
95
|
+
Related-field grids collapse to one column below tablet, and the action `ButtonGroup` stacks `fullWidth` (`useBreakpointUp("tablet")`, [how-to-page-layout.md](how-to-page-layout.md#action-clusters)). Standalone fields are already full-width, so they need nothing.
|
|
96
|
+
|
|
97
|
+
## React
|
|
98
|
+
|
|
99
|
+
v1 example: heading + supporting copy + name fields, select, radios, notes, and actions. `tabletUp` comes from `useBreakpointUp("tablet")` ([how-to-page-layout.md](how-to-page-layout.md#action-clusters)).
|
|
100
|
+
|
|
101
|
+
```tsx
|
|
102
|
+
// Section gap is medium-12 so the actions sit a decision-boundary apart from the
|
|
103
|
+
// fields. Heading + fields are grouped in one medium-8 stack.
|
|
104
|
+
<Layout as="section" display="flex" flexDirection="column" gap="var(--uxl-theme-layout-spacing-medium-12)">
|
|
105
|
+
<Layout display="flex" flexDirection="column" gap="var(--uxl-theme-layout-spacing-medium-8)">
|
|
106
|
+
<Layout display="flex" flexDirection="column" gap="var(--uxl-theme-layout-spacing-micro-2)">
|
|
107
|
+
<Text type="Display Extra Small" as="h2" width={false}>
|
|
108
|
+
Workspace details
|
|
109
|
+
</Text>
|
|
110
|
+
<Text type="Body Medium" width={false}>
|
|
111
|
+
Used on invoices and shared reports. Required fields are marked.
|
|
112
|
+
</Text>
|
|
113
|
+
</Layout>
|
|
114
|
+
<Layout
|
|
115
|
+
display="grid"
|
|
116
|
+
gridTemplateColumns="1fr 1fr"
|
|
117
|
+
gap="var(--uxl-theme-layout-spacing-small-4)"
|
|
118
|
+
>
|
|
119
|
+
<Textfield labelText="First name" name="givenName" autoComplete="given-name" />
|
|
120
|
+
<Textfield labelText="Last name" name="familyName" autoComplete="family-name" />
|
|
121
|
+
</Layout>
|
|
122
|
+
<Select
|
|
123
|
+
labelText="Department"
|
|
124
|
+
name="department"
|
|
125
|
+
options={[
|
|
126
|
+
{ value: "engineering", label: "Engineering" },
|
|
127
|
+
{ value: "design", label: "Design" },
|
|
128
|
+
{ value: "product", label: "Product" },
|
|
129
|
+
]}
|
|
130
|
+
/>
|
|
131
|
+
<RadioGroup name="priority" label labelText="Priority" defaultValue="normal">
|
|
132
|
+
<Radio value="low">
|
|
133
|
+
<Text type="Body Medium">Low</Text>
|
|
134
|
+
</Radio>
|
|
135
|
+
<Radio value="normal">
|
|
136
|
+
<Text type="Body Medium">Normal</Text>
|
|
137
|
+
</Radio>
|
|
138
|
+
<Radio value="high">
|
|
139
|
+
<Text type="Body Medium">High</Text>
|
|
140
|
+
</Radio>
|
|
141
|
+
</RadioGroup>
|
|
142
|
+
<Textarea
|
|
143
|
+
labelText="Notes"
|
|
144
|
+
fieldDescription="Optional context for this workspace."
|
|
145
|
+
name="notes"
|
|
146
|
+
autoGrow
|
|
147
|
+
rows={3}
|
|
148
|
+
/>
|
|
149
|
+
</Layout>
|
|
150
|
+
<ButtonGroup
|
|
151
|
+
direction={tabletUp ? "row" : "column"}
|
|
152
|
+
fullWidth={!tabletUp}
|
|
153
|
+
aria-label="Form actions"
|
|
154
|
+
>
|
|
155
|
+
<Button emphasis="medium">Cancel</Button>
|
|
156
|
+
<Button emphasis="high">Save</Button>
|
|
157
|
+
</ButtonGroup>
|
|
158
|
+
</Layout>
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## A2UI
|
|
162
|
+
|
|
163
|
+
Same stacking and theme spacing vars. Bind values with `{ path: "/form/…" }`. Runtime: `getUxelleRecipe("form-section")` from `@uxelle/a2ui` ([a2ui.md](a2ui.md)). Nested in `A2uiDialog`: `A2uiText` in `topSlotContent` and `centerSlotContent` uses `"width": false`. Given-name and family-name are separate `A2uiTextfield`s, stacked (`A2uiLayout` has no `gridTemplateColumns`). Notes are `A2uiTextarea` with `"autoGrow": true`. Form actions: `"direction": "column"` on `A2uiButtonGroup` (no matchMedia).
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Recipe: hero
|
|
2
|
+
|
|
3
|
+
A marketing **piece**: the opening band of a public page — value proposition,
|
|
4
|
+
primary/secondary action, and a product visual. Usually the one loud brand band on
|
|
5
|
+
the page.
|
|
6
|
+
|
|
7
|
+
Composes [`Hero`](../uxelle-components/Hero.md),
|
|
8
|
+
[`Lockup`](../uxelle-components/Lockup.md),
|
|
9
|
+
[`ButtonGroup`](../uxelle-components/ButtonGroup.md),
|
|
10
|
+
[`Image`](../uxelle-components/Image.md), and optionally
|
|
11
|
+
[`DynamicAngle*`](../uxelle-components/DynamicAngleShape.md) accents elsewhere on the page.
|
|
12
|
+
|
|
13
|
+
## When
|
|
14
|
+
|
|
15
|
+
The first impression: one sentence of value, one clear action, and supporting media.
|
|
16
|
+
|
|
17
|
+
## When not
|
|
18
|
+
|
|
19
|
+
- One `h1` for the whole page lives here — later sections are `h2`.
|
|
20
|
+
- Keep the media non-interactive; actions go in `contentSlot`.
|
|
21
|
+
- Pass media as a **direct** `Image` / `img` / `picture` / `video` — no wrappers
|
|
22
|
+
(Hero owns the crop; a nested `Image.aspectRatio` has no effect).
|
|
23
|
+
|
|
24
|
+
## Regions
|
|
25
|
+
|
|
26
|
+
`Hero` -> `image` (direct `Image`) + `contentSlot` (`Lockup` `h1` + `ButtonGroup`)
|
|
27
|
+
|
|
28
|
+
## Responsive
|
|
29
|
+
|
|
30
|
+
`Hero` owns it: `contentDirection="Left" | "Right"` pins a side panel on tablet and
|
|
31
|
+
up and drops to a full-width bottom panel below; `Bottom` is full-width at every
|
|
32
|
+
size. The crop adapts automatically. If the two actions feel tight on mobile, switch
|
|
33
|
+
the `ButtonGroup` `direction` to `column` (`useBreakpointUp("tablet")`).
|
|
34
|
+
|
|
35
|
+
## Spacing
|
|
36
|
+
|
|
37
|
+
Content-area spacing is component-owned. The hero is a full-bleed band, so it sits
|
|
38
|
+
outside the capped content column — following sections resume the column
|
|
39
|
+
([how-to-page-layout.md](how-to-page-layout.md)).
|
|
40
|
+
|
|
41
|
+
## A11y
|
|
42
|
+
|
|
43
|
+
The `h1` is here. `Hero` has no landmark by default — add `role="region"` with
|
|
44
|
+
`aria-label` / `aria-labelledby` if you need a named region. Meaningful media gets
|
|
45
|
+
real `alt`; decorative media uses `alt=""`. See
|
|
46
|
+
[how-to-accessibility.md](how-to-accessibility.md).
|
|
47
|
+
|
|
48
|
+
## Color
|
|
49
|
+
|
|
50
|
+
`Hero` defaults to `data-color-switcher="brand-1"`. Choose the brand palette by the
|
|
51
|
+
look you want and override with the attribute — this is typically the single
|
|
52
|
+
dominant brand band; keep later sections calmer ([how-to-color.md](how-to-color.md)).
|
|
53
|
+
|
|
54
|
+
## React
|
|
55
|
+
|
|
56
|
+
```tsx
|
|
57
|
+
<Hero
|
|
58
|
+
contentDirection="Left"
|
|
59
|
+
data-color-switcher="brand-1"
|
|
60
|
+
image={<Image src="/hero.jpg" alt="Team collaborating around a screen" />}
|
|
61
|
+
contentSlot={
|
|
62
|
+
<>
|
|
63
|
+
<Lockup
|
|
64
|
+
overline={<Text type="Overline" width={false}>New</Text>}
|
|
65
|
+
title={
|
|
66
|
+
<Text type="Display Medium" as="h1" width={false}>
|
|
67
|
+
One workspace for every team
|
|
68
|
+
</Text>
|
|
69
|
+
}
|
|
70
|
+
body={
|
|
71
|
+
<Text type="Body Medium" width={false}>
|
|
72
|
+
Plan, build, and ship together — without the tool sprawl.
|
|
73
|
+
</Text>
|
|
74
|
+
}
|
|
75
|
+
/>
|
|
76
|
+
<ButtonGroup aria-label="Get started">
|
|
77
|
+
<Button emphasis="high"><Text type="Button" width={false}>Start free</Text></Button>
|
|
78
|
+
<Button emphasis="medium"><Text type="Button" width={false}>Book a demo</Text></Button>
|
|
79
|
+
</ButtonGroup>
|
|
80
|
+
</>
|
|
81
|
+
}
|
|
82
|
+
/>
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## A2UI
|
|
86
|
+
|
|
87
|
+
`A2uiHero` with a nested `A2uiImage` and a `contentSlot` of stacked `A2uiText`
|
|
88
|
+
(no `Lockup` adapter) plus `A2uiButtonGroup`. Set the palette via the adapter's
|
|
89
|
+
`data-color-switcher` ([a2ui.md](a2ui.md)).
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# Recipe: landing page
|
|
2
|
+
|
|
3
|
+
A marketing **experience**: a public page that builds trust and converts. It
|
|
4
|
+
assembles the marketing pieces in a dependable rhythm on one grid, with a single
|
|
5
|
+
dominant brand band.
|
|
6
|
+
|
|
7
|
+
Assembles [app-chrome](recipe-app-chrome.md) (marketing mode),
|
|
8
|
+
[hero](recipe-hero.md), [logo-wall](recipe-logo-wall.md),
|
|
9
|
+
[feature-section](recipe-feature-section.md), [stat-callouts](recipe-stat-callouts.md),
|
|
10
|
+
[testimonial](recipe-testimonial.md), [pricing](recipe-pricing.md),
|
|
11
|
+
[cta-band](recipe-cta-band.md), and [footer](recipe-footer.md).
|
|
12
|
+
|
|
13
|
+
## When
|
|
14
|
+
|
|
15
|
+
A public page whose job is to explain value and drive one action.
|
|
16
|
+
|
|
17
|
+
## When not
|
|
18
|
+
|
|
19
|
+
- Signed-in product screens are [dashboard-overview](recipe-dashboard-overview.md) /
|
|
20
|
+
[data-table-page](recipe-data-table-page.md).
|
|
21
|
+
- Do not stack multiple loud brand bands — pick one dominant (usually the hero).
|
|
22
|
+
- One `h1` (in the hero); every section below is `h2`.
|
|
23
|
+
|
|
24
|
+
## Regions
|
|
25
|
+
|
|
26
|
+
[app-chrome](recipe-app-chrome.md) (marketing nav) -> `main`: full-bleed
|
|
27
|
+
[hero](recipe-hero.md) -> capped column ([logo-wall](recipe-logo-wall.md),
|
|
28
|
+
[feature-section](recipe-feature-section.md)s, [stat-callouts](recipe-stat-callouts.md),
|
|
29
|
+
[testimonial](recipe-testimonial.md), [pricing](recipe-pricing.md)) ->
|
|
30
|
+
full-bleed [cta-band](recipe-cta-band.md) -> [footer](recipe-footer.md)
|
|
31
|
+
|
|
32
|
+
Full-bleed bands (hero, CTA, and any brand band) span the viewport; in-column
|
|
33
|
+
sections cap with `--uxl-breakpoints-max-container-width`. Any section can become a
|
|
34
|
+
full-bleed brand band — keep to one dominant.
|
|
35
|
+
|
|
36
|
+
## Responsive
|
|
37
|
+
|
|
38
|
+
Every piece carries its own responsiveness (hero panel, `auto-fit` grids, stacking
|
|
39
|
+
clusters) — no page-level breakpoint logic. See
|
|
40
|
+
[how-to-page-layout.md](how-to-page-layout.md#responsiveness).
|
|
41
|
+
|
|
42
|
+
## Spacing
|
|
43
|
+
|
|
44
|
+
`medium-12` between in-column sections; `large-13`+ block padding for full-bleed
|
|
45
|
+
bands; `--uxl-breakpoints-gutter` inside grids ([spacing-steps.md](spacing-steps.md)).
|
|
46
|
+
|
|
47
|
+
## A11y
|
|
48
|
+
|
|
49
|
+
One `main`, one `h1` (hero), section headings `h2` in order; skip link and landmarks
|
|
50
|
+
from [app-chrome](recipe-app-chrome.md). Name each section by its heading. See
|
|
51
|
+
[how-to-accessibility.md](how-to-accessibility.md).
|
|
52
|
+
|
|
53
|
+
## Color
|
|
54
|
+
|
|
55
|
+
One dominant brand band (usually the hero). Apply palettes per section with
|
|
56
|
+
`data-color-switcher`, style with role tokens, keep the calmer sections on `default`
|
|
57
|
+
/ `default-subtle` ([how-to-color.md](how-to-color.md)).
|
|
58
|
+
|
|
59
|
+
## React
|
|
60
|
+
|
|
61
|
+
```tsx
|
|
62
|
+
<main id="main-content" tabIndex={-1} style={{ flexGrow: 1, minHeight: 0, overflow: "auto" }}>
|
|
63
|
+
{/* Full-bleed hero — recipe-hero.md (contains the h1) */}
|
|
64
|
+
<MarketingHero />
|
|
65
|
+
|
|
66
|
+
{/* Capped content column for the mid-page sections */}
|
|
67
|
+
<Layout
|
|
68
|
+
display="flex"
|
|
69
|
+
flexDirection="column"
|
|
70
|
+
width="100%"
|
|
71
|
+
maxWidth="var(--uxl-breakpoints-max-container-width)"
|
|
72
|
+
mh="auto"
|
|
73
|
+
p="var(--uxl-breakpoints-margin)"
|
|
74
|
+
gap="var(--uxl-theme-layout-spacing-medium-12)"
|
|
75
|
+
>
|
|
76
|
+
<LogoWall /> {/* recipe-logo-wall.md */}
|
|
77
|
+
<FeatureSections /> {/* recipe-feature-section.md (alternating) */}
|
|
78
|
+
<StatCallouts /> {/* recipe-stat-callouts.md */}
|
|
79
|
+
<Testimonials /> {/* recipe-testimonial.md */}
|
|
80
|
+
<Pricing /> {/* recipe-pricing.md */}
|
|
81
|
+
</Layout>
|
|
82
|
+
|
|
83
|
+
{/* Full-bleed closing CTA — recipe-cta-band.md */}
|
|
84
|
+
<CtaBand />
|
|
85
|
+
|
|
86
|
+
{/* Footer — recipe-footer.md (marketing: directory band + legal bar) */}
|
|
87
|
+
<MarketingFooter />
|
|
88
|
+
</main>
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## A2UI
|
|
92
|
+
|
|
93
|
+
No `getUxelleRecipe("landing-page")` — compose the piece adapters in one narrow
|
|
94
|
+
column, in the same order, dropping full-bleed layout (host-owned) ([a2ui.md](a2ui.md)).
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Recipe: logo wall
|
|
2
|
+
|
|
3
|
+
A shared **piece**: a row of customer, partner, or integration logos that builds
|
|
4
|
+
trust. Common just under a hero or between marketing sections; also used for an
|
|
5
|
+
"integrates with" band.
|
|
6
|
+
|
|
7
|
+
Composes [`Logo`](../uxelle-components/Logo.md) (brand marks in the catalog),
|
|
8
|
+
[`Image`](../uxelle-components/Image.md) (logos not in the catalog), and
|
|
9
|
+
[`Text`](../uxelle-components/Text.md) for the eyebrow.
|
|
10
|
+
|
|
11
|
+
## When
|
|
12
|
+
|
|
13
|
+
A set of recognizable marks shown as social proof or integration coverage.
|
|
14
|
+
|
|
15
|
+
## When not
|
|
16
|
+
|
|
17
|
+
- Not navigation and not a `Footer` — this is a proof band.
|
|
18
|
+
- Do not stretch or distort marks; keep a consistent height and let them wrap.
|
|
19
|
+
- Make a logo interactive only if it links somewhere, and name that control.
|
|
20
|
+
|
|
21
|
+
## Regions
|
|
22
|
+
|
|
23
|
+
section -> optional eyebrow `Text` -> wrapping, centered row of `Logo` / `Image`
|
|
24
|
+
|
|
25
|
+
## Responsive
|
|
26
|
+
|
|
27
|
+
`flexWrap="wrap"` with `justifyContent="center"` reflows to as many marks per row as
|
|
28
|
+
fit — no JS, no breakpoint literals. Bound each mark's `width` so rows stay even.
|
|
29
|
+
|
|
30
|
+
## Spacing
|
|
31
|
+
|
|
32
|
+
`gap="var(--uxl-theme-layout-spacing-medium-8)"` between marks; `small-4` under the
|
|
33
|
+
eyebrow. As a band, add section breathing with `large-13`+ block padding
|
|
34
|
+
([spacing-steps.md](spacing-steps.md)).
|
|
35
|
+
|
|
36
|
+
## A11y
|
|
37
|
+
|
|
38
|
+
Name the region (`aria-label="Trusted by leading teams"`). If the mark is meaningful
|
|
39
|
+
and there is no adjacent text, give it an accessible name (`aria-label` on `Logo`,
|
|
40
|
+
`alt` on `Image`); if company names appear as text elsewhere, mark the logos
|
|
41
|
+
`aria-hidden`. See [how-to-accessibility.md](how-to-accessibility.md).
|
|
42
|
+
|
|
43
|
+
## Color
|
|
44
|
+
|
|
45
|
+
On a brand band, set `data-color-switcher` on the **section** and tint monochrome
|
|
46
|
+
marks with `Logo` `color="colorSwitcherIcon"` (or `background="onDark"` for
|
|
47
|
+
full-color marks on dark). Keep one dominant brand band per view
|
|
48
|
+
([how-to-color.md](how-to-color.md)).
|
|
49
|
+
|
|
50
|
+
## React
|
|
51
|
+
|
|
52
|
+
```tsx
|
|
53
|
+
<Layout
|
|
54
|
+
as="section"
|
|
55
|
+
display="flex"
|
|
56
|
+
flexDirection="column"
|
|
57
|
+
alignItems="center"
|
|
58
|
+
gap="var(--uxl-theme-layout-spacing-small-4)"
|
|
59
|
+
aria-label="Trusted by leading teams"
|
|
60
|
+
>
|
|
61
|
+
<Text type="Overline" width={false}>Trusted by teams at</Text>
|
|
62
|
+
<Layout
|
|
63
|
+
display="flex"
|
|
64
|
+
flexWrap="wrap"
|
|
65
|
+
justifyContent="center"
|
|
66
|
+
alignItems="center"
|
|
67
|
+
gap="var(--uxl-theme-layout-spacing-medium-8)"
|
|
68
|
+
>
|
|
69
|
+
<Logo name="generic" width={112} color="colorSwitcherIcon" aria-label="Northwind" />
|
|
70
|
+
<Logo name="generic" width={112} color="colorSwitcherIcon" aria-label="Acme" />
|
|
71
|
+
{/* Logos not in the catalog use Image */}
|
|
72
|
+
<Image src="/logos/contoso.svg" alt="Contoso" />
|
|
73
|
+
</Layout>
|
|
74
|
+
</Layout>
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## A2UI
|
|
78
|
+
|
|
79
|
+
No `getUxelleRecipe("logo-wall")` — compose `A2uiLogo` / `A2uiImage` inside an
|
|
80
|
+
`A2uiLayout` (single wrapping row, layout-spacing only) ([a2ui.md](a2ui.md)).
|