@uxelle/skills 0.2.1-beta.0 → 0.2.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/README.md +3 -5
- package/dist/index.js +96 -86
- package/index.json +96 -86
- package/package.json +1 -1
- package/skills/uxelle-components/ChoiceChip.md +1 -1
- package/skills/uxelle-components/ChoiceChipGroup.md +3 -3
- package/skills/uxelle-components/FilterChip.md +2 -2
- package/skills/uxelle-components/FilterChipGroup.md +1 -1
- package/skills/uxelle-components/Hero.md +2 -2
- package/skills/uxelle-components/Image.md +10 -3
- package/skills/uxelle-components/MultiSelect.md +3 -2
- package/skills/uxelle-components/SKILL.md +3 -1
- package/skills/uxelle-components/Select.md +2 -1
- package/skills/uxelle-components/Table.md +7 -4
- package/skills/uxelle-components/getting-started.md +54 -0
- package/skills/uxelle-design-harness/SKILL.md +44 -20
- package/skills/uxelle-design-harness/a2ui.md +22 -2
- package/skills/uxelle-design-harness/density.md +138 -0
- package/skills/uxelle-design-harness/how-to-accessibility.md +2 -0
- package/skills/uxelle-design-harness/how-to-color.md +6 -3
- package/skills/uxelle-design-harness/how-to-host.md +5 -22
- package/skills/uxelle-design-harness/how-to-page-layout.md +51 -6
- package/skills/uxelle-design-harness/principles.md +10 -6
- package/skills/uxelle-design-harness/recipe-app-chrome.md +20 -16
- package/skills/uxelle-design-harness/recipe-card-grid.md +14 -5
- package/skills/uxelle-design-harness/recipe-cta-band.md +16 -6
- package/skills/uxelle-design-harness/recipe-dashboard-overview.md +17 -5
- package/skills/uxelle-design-harness/recipe-data-table-page.md +30 -137
- package/skills/uxelle-design-harness/recipe-feature-section.md +9 -7
- package/skills/uxelle-design-harness/recipe-footer.md +7 -4
- package/skills/uxelle-design-harness/recipe-form-section.md +2 -2
- package/skills/uxelle-design-harness/recipe-hero.md +35 -15
- package/skills/uxelle-design-harness/recipe-landing-page.md +27 -4
- package/skills/uxelle-design-harness/recipe-logo-wall.md +11 -9
- package/skills/uxelle-design-harness/recipe-multi-step-flow.md +5 -3
- package/skills/uxelle-design-harness/recipe-page-header.md +14 -4
- package/skills/uxelle-design-harness/recipe-page-shell.md +2 -2
- package/skills/uxelle-design-harness/recipe-pricing.md +5 -2
- package/skills/uxelle-design-harness/recipe-query-bar.md +21 -5
- package/skills/uxelle-design-harness/recipe-record-detail.md +5 -2
- package/skills/uxelle-design-harness/recipe-settings-page.md +3 -3
- package/skills/uxelle-design-harness/recipe-stat-callouts.md +8 -3
- package/skills/uxelle-design-harness/recipe-states.md +9 -4
- package/skills/uxelle-design-harness/recipe-summary-list.md +5 -3
- package/skills/uxelle-design-harness/recipe-template.md +6 -0
- package/skills/uxelle-design-harness/recipe-testimonial.md +5 -3
- package/skills/uxelle-design-harness/spacing-steps.md +17 -8
- package/skills/uxelle-design-harness/tokens.md +45 -4
|
@@ -19,7 +19,8 @@ The first impression: one sentence of value, one clear action, and supporting me
|
|
|
19
19
|
- One `h1` for the whole page lives here — later sections are `h2`.
|
|
20
20
|
- Keep the media non-interactive; actions go in `contentSlot`.
|
|
21
21
|
- Pass media as a **direct** `Image` / `img` / `picture` / `video` — no wrappers
|
|
22
|
-
(Hero owns the crop;
|
|
22
|
+
(Hero owns the crop; nested `Image.aspectRatio` and box size — `width`,
|
|
23
|
+
`height`, `minWidth`, `maxWidth`, `minHeight`, `maxHeight` — have no effect).
|
|
23
24
|
|
|
24
25
|
## Regions
|
|
25
26
|
|
|
@@ -27,16 +28,24 @@ The first impression: one sentence of value, one clear action, and supporting me
|
|
|
27
28
|
|
|
28
29
|
## Responsive
|
|
29
30
|
|
|
30
|
-
`Hero` owns
|
|
31
|
-
up and drops to a full-width bottom panel below; `Bottom` is
|
|
32
|
-
size. The crop adapts automatically.
|
|
33
|
-
|
|
31
|
+
`Hero` owns the panel and crop: `contentDirection="Left" | "Right"` pins a side
|
|
32
|
+
panel on tablet and up and drops to a full-width bottom panel below; `Bottom` is
|
|
33
|
+
full-width at every size. The crop adapts automatically.
|
|
34
|
+
|
|
35
|
+
The actions are **not** component-owned. Two text `Button`s take the tablet switch
|
|
36
|
+
here exactly as anywhere else — `direction={tabletUp ? "row" : "column"}` with
|
|
37
|
+
`fullWidth={!tabletUp}`, not a judgement call about whether they "feel tight"
|
|
38
|
+
([how-to-page-layout.md](how-to-page-layout.md#action-clusters)). A hero with a
|
|
39
|
+
single action passes no `direction`.
|
|
34
40
|
|
|
35
41
|
## Spacing
|
|
36
42
|
|
|
37
|
-
Content-area spacing is component-owned
|
|
38
|
-
|
|
39
|
-
|
|
43
|
+
Content-area spacing is component-owned and **density-invariant** — `Hero` owns its
|
|
44
|
+
own band padding, so do not wrap it in compensating `p` to reach a mode. The hero is
|
|
45
|
+
a full-bleed band, so it sits outside the capped content column — following sections
|
|
46
|
+
resume the column at the page's **Section** gap (`large-14` on the `spacious`
|
|
47
|
+
marketing page where a hero lives) ([how-to-page-layout.md](how-to-page-layout.md),
|
|
48
|
+
[density.md](density.md)).
|
|
40
49
|
|
|
41
50
|
## A11y
|
|
42
51
|
|
|
@@ -53,7 +62,13 @@ dominant brand band; keep later sections calmer ([how-to-color.md](how-to-color.
|
|
|
53
62
|
|
|
54
63
|
## React
|
|
55
64
|
|
|
65
|
+
`tabletUp` is the shared `useBreakpointUp("tablet")` helper — copy it from
|
|
66
|
+
[how-to-page-layout.md](how-to-page-layout.md#3-token-driven-js-narrow-fallback),
|
|
67
|
+
do not reimplement it or inline a px literal.
|
|
68
|
+
|
|
56
69
|
```tsx
|
|
70
|
+
const tabletUp = useBreakpointUp("tablet");
|
|
71
|
+
|
|
57
72
|
<Hero
|
|
58
73
|
contentDirection="Left"
|
|
59
74
|
data-color-switcher="brand-1"
|
|
@@ -63,7 +78,7 @@ dominant brand band; keep later sections calmer ([how-to-color.md](how-to-color.
|
|
|
63
78
|
<Lockup
|
|
64
79
|
overline={<Text type="Overline" width={false}>New</Text>}
|
|
65
80
|
title={
|
|
66
|
-
<Text type="Display
|
|
81
|
+
<Text type="Display Large" as="h1" width={false}>
|
|
67
82
|
One workspace for every team
|
|
68
83
|
</Text>
|
|
69
84
|
}
|
|
@@ -73,9 +88,13 @@ dominant brand band; keep later sections calmer ([how-to-color.md](how-to-color.
|
|
|
73
88
|
</Text>
|
|
74
89
|
}
|
|
75
90
|
/>
|
|
76
|
-
<ButtonGroup
|
|
77
|
-
|
|
78
|
-
|
|
91
|
+
<ButtonGroup
|
|
92
|
+
direction={tabletUp ? "row" : "column"}
|
|
93
|
+
fullWidth={!tabletUp}
|
|
94
|
+
aria-label="Get started"
|
|
95
|
+
>
|
|
96
|
+
<Button emphasis="high"><Text type="Button" width>Start free</Text></Button>
|
|
97
|
+
<Button emphasis="medium"><Text type="Button" width>Book a demo</Text></Button>
|
|
79
98
|
</ButtonGroup>
|
|
80
99
|
</>
|
|
81
100
|
}
|
|
@@ -84,6 +103,7 @@ dominant brand band; keep later sections calmer ([how-to-color.md](how-to-color.
|
|
|
84
103
|
|
|
85
104
|
## A2UI
|
|
86
105
|
|
|
87
|
-
`A2uiHero` with a nested `A2uiImage`
|
|
88
|
-
|
|
89
|
-
`data-color-switcher`
|
|
106
|
+
`A2uiHero` with a nested `A2uiImage` (`src` + `alt` only — omit `aspectRatio` and
|
|
107
|
+
box size) and a `contentSlot` of stacked `A2uiText` (no `Lockup` adapter) plus
|
|
108
|
+
`A2uiButtonGroup`. Set the palette via the adapter's `data-color-switcher`
|
|
109
|
+
([a2ui.md](a2ui.md)).
|
|
@@ -41,8 +41,16 @@ clusters) — no page-level breakpoint logic. See
|
|
|
41
41
|
|
|
42
42
|
## Spacing
|
|
43
43
|
|
|
44
|
-
`
|
|
45
|
-
|
|
44
|
+
**Density: `spacious`** — generous whitespace is the argument on a public page, and
|
|
45
|
+
one idea should land per band ([density.md](density.md)). **Section** gap between
|
|
46
|
+
in-column sections is `large-14`; full-bleed bands take the **Band** step
|
|
47
|
+
(`large-15`, or `large-16` for a deliberate full-viewport hero statement);
|
|
48
|
+
`--uxl-breakpoints-gutter` stays the grid gap in every mode
|
|
49
|
+
([spacing-steps.md](spacing-steps.md)).
|
|
50
|
+
|
|
51
|
+
Hold one mode for the whole page. A dense piece inside it (a pricing comparison, an
|
|
52
|
+
embedded table) keeps its own internals tight but still sits at the page's
|
|
53
|
+
`large-14` **Section** gap — density describes the experience, not each component.
|
|
46
54
|
|
|
47
55
|
## A11y
|
|
48
56
|
|
|
@@ -56,9 +64,23 @@ One dominant brand band (usually the hero). Apply palettes per section with
|
|
|
56
64
|
`data-color-switcher`, style with role tokens, keep the calmer sections on `default`
|
|
57
65
|
/ `default-subtle` ([how-to-color.md](how-to-color.md)).
|
|
58
66
|
|
|
67
|
+
This page ships **two** full-bleed bands, so make the choice explicit rather than
|
|
68
|
+
letting both default to `brand-1`: the hero takes the brand palette and the closing
|
|
69
|
+
[cta-band](recipe-cta-band.md) drops to `default-subtle`. Flip it if the CTA is the
|
|
70
|
+
louder moment. What you must not do is set both to a brand palette — that is the
|
|
71
|
+
"stacked loud bands" this recipe's **when not** rules out, and it costs the hero its
|
|
72
|
+
dominance.
|
|
73
|
+
|
|
59
74
|
## React
|
|
60
75
|
|
|
76
|
+
`<main>` comes from [app-chrome](recipe-app-chrome.md) and is shown here only for
|
|
77
|
+
context — this recipe owns what goes *inside* it
|
|
78
|
+
([how-to-page-layout.md](how-to-page-layout.md#who-owns-the-content-column)). Note
|
|
79
|
+
that a marketing page caps only its mid-page sections: the full-bleed bands are
|
|
80
|
+
deliberately outside any column.
|
|
81
|
+
|
|
61
82
|
```tsx
|
|
83
|
+
// Density: spacious (public marketing page) — see density.md
|
|
62
84
|
<main id="main-content" tabIndex={-1} style={{ flexGrow: 1, minHeight: 0, overflow: "auto" }}>
|
|
63
85
|
{/* Full-bleed hero — recipe-hero.md (contains the h1) */}
|
|
64
86
|
<MarketingHero />
|
|
@@ -71,7 +93,7 @@ One dominant brand band (usually the hero). Apply palettes per section with
|
|
|
71
93
|
maxWidth="var(--uxl-breakpoints-max-container-width)"
|
|
72
94
|
mh="auto"
|
|
73
95
|
p="var(--uxl-breakpoints-margin)"
|
|
74
|
-
gap="var(--uxl-theme-layout-spacing-
|
|
96
|
+
gap="var(--uxl-theme-layout-spacing-large-14)"
|
|
75
97
|
>
|
|
76
98
|
<LogoWall /> {/* recipe-logo-wall.md */}
|
|
77
99
|
<FeatureSections /> {/* recipe-feature-section.md (alternating) */}
|
|
@@ -80,7 +102,8 @@ One dominant brand band (usually the hero). Apply palettes per section with
|
|
|
80
102
|
<Pricing /> {/* recipe-pricing.md */}
|
|
81
103
|
</Layout>
|
|
82
104
|
|
|
83
|
-
{/* Full-bleed closing CTA — recipe-cta-band.md
|
|
105
|
+
{/* Full-bleed closing CTA — recipe-cta-band.md.
|
|
106
|
+
Hero above is the brand band, so this one runs default-subtle. */}
|
|
84
107
|
<CtaBand />
|
|
85
108
|
|
|
86
109
|
{/* Footer — recipe-footer.md (marketing: directory band + legal bar) */}
|
|
@@ -29,9 +29,10 @@ fit — no JS, no breakpoint literals. Bound each mark's `width` so rows stay ev
|
|
|
29
29
|
|
|
30
30
|
## Spacing
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
**Group** step between marks and **Control** under the eyebrow — `medium-10` and
|
|
33
|
+
`small-6` on the `spacious` marketing page where a trust bar lives (`medium-8` /
|
|
34
|
+
`small-4` under `standard`). As a band, add breathing with the **Band** step
|
|
35
|
+
(`large-15`) ([density.md](density.md), [spacing-steps.md](spacing-steps.md)).
|
|
35
36
|
|
|
36
37
|
## A11y
|
|
37
38
|
|
|
@@ -55,7 +56,7 @@ full-color marks on dark). Keep one dominant brand band per view
|
|
|
55
56
|
display="flex"
|
|
56
57
|
flexDirection="column"
|
|
57
58
|
alignItems="center"
|
|
58
|
-
gap="var(--uxl-theme-layout-spacing-small-
|
|
59
|
+
gap="var(--uxl-theme-layout-spacing-small-6)"
|
|
59
60
|
aria-label="Trusted by leading teams"
|
|
60
61
|
>
|
|
61
62
|
<Text type="Overline" width={false}>Trusted by teams at</Text>
|
|
@@ -64,17 +65,18 @@ full-color marks on dark). Keep one dominant brand band per view
|
|
|
64
65
|
flexWrap="wrap"
|
|
65
66
|
justifyContent="center"
|
|
66
67
|
alignItems="center"
|
|
67
|
-
gap="var(--uxl-theme-layout-spacing-medium-
|
|
68
|
+
gap="var(--uxl-theme-layout-spacing-medium-10)"
|
|
68
69
|
>
|
|
69
70
|
<Logo name="generic" width={112} color="colorSwitcherIcon" aria-label="Northwind" />
|
|
70
71
|
<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" />
|
|
72
|
+
{/* Logos not in the catalog use Image; bound height to match the row */}
|
|
73
|
+
<Image src="/logos/contoso.svg" alt="Contoso" height={40} />
|
|
73
74
|
</Layout>
|
|
74
75
|
</Layout>
|
|
75
76
|
```
|
|
76
77
|
|
|
77
78
|
## A2UI
|
|
78
79
|
|
|
79
|
-
No `getUxelleRecipe("logo-wall")` — compose `A2uiLogo` / `A2uiImage`
|
|
80
|
-
`
|
|
80
|
+
No `getUxelleRecipe("logo-wall")` — compose `A2uiLogo` (`width`) / `A2uiImage`
|
|
81
|
+
(`height` or `width` so marks stay even) inside an `A2uiLayout` (single wrapping
|
|
82
|
+
row, layout-spacing only) ([a2ui.md](a2ui.md)).
|
|
@@ -34,8 +34,10 @@ narrow. See [how-to-page-layout.md](how-to-page-layout.md#action-clusters).
|
|
|
34
34
|
|
|
35
35
|
## Spacing
|
|
36
36
|
|
|
37
|
-
`
|
|
38
|
-
|
|
37
|
+
**Density: `standard`** — a flow asks for one decision at a time, so keep it calm
|
|
38
|
+
even inside a `compact` app ([density.md](density.md)). **Section** gap between the
|
|
39
|
+
stepper, the step body, and the footer (`medium-12`); the step form uses its own
|
|
40
|
+
**Group** field stack (`medium-8`) ([spacing-steps.md](spacing-steps.md)).
|
|
39
41
|
|
|
40
42
|
## A11y
|
|
41
43
|
|
|
@@ -65,7 +67,7 @@ const isLast = step === steps.length;
|
|
|
65
67
|
p="var(--uxl-breakpoints-margin)"
|
|
66
68
|
gap="var(--uxl-theme-layout-spacing-medium-12)"
|
|
67
69
|
>
|
|
68
|
-
<Text type="Display
|
|
70
|
+
<Text type="Display Small" as="h1" width={false}>Create a workspace</Text>
|
|
69
71
|
|
|
70
72
|
<nav aria-label="Progress">
|
|
71
73
|
<Stepper
|
|
@@ -19,9 +19,16 @@ status, and the actions that operate on the whole page.
|
|
|
19
19
|
## When not
|
|
20
20
|
|
|
21
21
|
- Not a `PageHeader` component — it is a `Layout` composition.
|
|
22
|
-
- Page actions only (create, import,
|
|
22
|
+
- Page actions only (create, import, delete). Controls that filter a table
|
|
23
23
|
belong to the [query-bar](recipe-query-bar.md); tabs that swap a table's rows
|
|
24
24
|
belong on the `Table` ([data-table-page](recipe-data-table-page.md)).
|
|
25
|
+
- **Export splits by scope.** Export of the *whole dataset* is a page action and
|
|
26
|
+
belongs here — label it for that scope ("Export all"). Export of the *rows
|
|
27
|
+
currently in view* (after filters and search) belongs on the
|
|
28
|
+
[query-bar](recipe-query-bar.md), labelled for the view ("Export view"). Never
|
|
29
|
+
put both on one page: pick the scope the user actually needs. A
|
|
30
|
+
[data-table-page](recipe-data-table-page.md) defaults to "Export view" — omit
|
|
31
|
+
export from this header when that query bar is present.
|
|
25
32
|
- Do not wrap `Lockup` in a nested `Layout` to share the row — let it be the flex
|
|
26
33
|
sibling that shrinks and put `flexShrink={0}` on the action cluster.
|
|
27
34
|
|
|
@@ -40,9 +47,12 @@ would claim the row and drop the actions).
|
|
|
40
47
|
|
|
41
48
|
## Spacing
|
|
42
49
|
|
|
43
|
-
|
|
44
|
-
(
|
|
45
|
-
([spacing-steps.md](spacing-steps.md)).
|
|
50
|
+
**Control** step between title and actions. Lockup internals are component-owned
|
|
51
|
+
(`micro-2`) and do not shift with the mode. The header sits in the page column at
|
|
52
|
+
the **Section** gap ([spacing-steps.md](spacing-steps.md)).
|
|
53
|
+
|
|
54
|
+
Inherits the host experience's mode — `micro-3` / `medium-9` under `compact`,
|
|
55
|
+
`small-4` / `medium-12` under `standard` ([density.md](density.md)).
|
|
46
56
|
|
|
47
57
|
## A11y
|
|
48
58
|
|
|
@@ -19,7 +19,7 @@ host canvas → content column (`max-container-width`, `mh="auto"`, `p` = breakp
|
|
|
19
19
|
|
|
20
20
|
## Spacing
|
|
21
21
|
|
|
22
|
-
React page column: [how-to-page-layout.md](how-to-page-layout.md). Section stacks
|
|
22
|
+
**Density: `standard`** unless the body is data-dense (then `compact`) or public marketing (then `spacious`) — [density.md](density.md). React page column: [how-to-page-layout.md](how-to-page-layout.md). **Section** gap between sections (`medium-12`); section stacks use **Group** (`medium-8`) or **Control** (`small-4`); intro lockup internals **Tight** (`micro-2`). Form/dialog action groups: [how-to-page-layout.md](how-to-page-layout.md#action-clusters). A2UI: layout-spacing only, `spacious` never applies (`p` **Band**-equivalent `large-13`, `gap` **Section** `medium-12`).
|
|
23
23
|
|
|
24
24
|
## A11y
|
|
25
25
|
|
|
@@ -43,7 +43,7 @@ React page column: [how-to-page-layout.md](how-to-page-layout.md). Section stack
|
|
|
43
43
|
<Lockup
|
|
44
44
|
topSlot={false}
|
|
45
45
|
title={
|
|
46
|
-
<Text type="Display
|
|
46
|
+
<Text type="Display Small" as="h1" width={false}>
|
|
47
47
|
Account settings
|
|
48
48
|
</Text>
|
|
49
49
|
}
|
|
@@ -33,8 +33,11 @@ comfortable tier. See [how-to-page-layout.md](how-to-page-layout.md#responsivene
|
|
|
33
33
|
|
|
34
34
|
## Spacing
|
|
35
35
|
|
|
36
|
-
`--uxl-breakpoints-gutter` between tiers; feature rows
|
|
37
|
-
`
|
|
36
|
+
`--uxl-breakpoints-gutter` between tiers in every mode; feature rows keep the
|
|
37
|
+
`List` default (component-owned, density-invariant); the **Band** step as a band —
|
|
38
|
+
`large-15` on the `spacious` marketing page. A tier comparison is the one dense
|
|
39
|
+
piece on an airy page: let its feature rows stay tight while the section still sits
|
|
40
|
+
at the page's **Section** gap ([density.md](density.md), [spacing-steps.md](spacing-steps.md)).
|
|
38
41
|
|
|
39
42
|
## A11y
|
|
40
43
|
|
|
@@ -18,6 +18,11 @@ Controls that filter, facet, search, or export the rows/cards currently in view.
|
|
|
18
18
|
## When not
|
|
19
19
|
|
|
20
20
|
- Page-level actions (create, import, delete) belong to the [page-header](recipe-page-header.md).
|
|
21
|
+
- **Export here is view-scoped only.** This bar exports what the filters and search
|
|
22
|
+
currently leave on screen, so label it for that ("Export view"). Exporting the
|
|
23
|
+
*whole dataset* ignores this bar's controls and is a page action —
|
|
24
|
+
[page-header](recipe-page-header.md). Never put both on one page: pick the
|
|
25
|
+
scope the user actually needs.
|
|
21
26
|
- Data tabs that swap which rows are shown sit above the query bar, first in the
|
|
22
27
|
`Table` `topSlot` — not on this row.
|
|
23
28
|
- No filter *fields* on the bar: a filter *trigger* opens a right `Sheet`.
|
|
@@ -33,14 +38,25 @@ row **below**.
|
|
|
33
38
|
## Responsive
|
|
34
39
|
|
|
35
40
|
The bar stays a row of controls. When very tight, let the search field take the
|
|
36
|
-
full line above the filter/export cluster; the applied `FilterChipGroup` wraps
|
|
41
|
+
full line above the filter/export cluster; the applied `FilterChipGroup` wraps
|
|
42
|
+
whole chips. A chip wider than the row ellipsizes so dismiss stays visible.
|
|
37
43
|
Keep the filter and export buttons together (`flexShrink={0}`).
|
|
38
44
|
|
|
45
|
+
Chip labels are component-owned: pass `label` on `ChoiceChip` / `FilterChip` — do
|
|
46
|
+
not wrap `Text`. Unchecked choice chips use Condensed; checked use Condensed Alt.
|
|
47
|
+
Filter chips use Condensed. Exclusive `ChoiceChip` selection is one checked value
|
|
48
|
+
in app state (`role="group"`); use `RadioGroup` or `SegmentedControl` for radio
|
|
49
|
+
semantics.
|
|
50
|
+
|
|
39
51
|
## Spacing
|
|
40
52
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
the
|
|
53
|
+
The **Control** step throughout — within and between the two clusters, and for the
|
|
54
|
+
applied-chips row below. This whole stack lives in the table's `topSlot` at the
|
|
55
|
+
**Control** step, never the **Section** gap ([spacing-steps.md](spacing-steps.md)).
|
|
56
|
+
|
|
57
|
+
As a piece, it inherits the host experience's density mode: `micro-3` on a
|
|
58
|
+
`compact` data-table page (its usual home), `small-4` under `standard`
|
|
59
|
+
([density.md](density.md)).
|
|
44
60
|
|
|
45
61
|
## A11y
|
|
46
62
|
|
|
@@ -92,7 +108,7 @@ See [how-to-accessibility.md](how-to-accessibility.md).
|
|
|
92
108
|
/>
|
|
93
109
|
</Layout>
|
|
94
110
|
<Layout flexShrink={0}>
|
|
95
|
-
<Button emphasis="low"><Text type="Button" width>Export
|
|
111
|
+
<Button emphasis="low"><Text type="Button" width>Export view</Text></Button>
|
|
96
112
|
</Layout>
|
|
97
113
|
</Layout>
|
|
98
114
|
</Layout>
|
|
@@ -37,8 +37,11 @@ scrolls when the labels overflow. See [how-to-page-layout.md](how-to-page-layout
|
|
|
37
37
|
|
|
38
38
|
## Spacing
|
|
39
39
|
|
|
40
|
-
`
|
|
41
|
-
|
|
40
|
+
**Density: `standard`**; step to `compact` when the record is a dense diagnostic
|
|
41
|
+
view (many fields scanned side by side) rather than a read-and-act page
|
|
42
|
+
([density.md](density.md)). **Section** gap between page sections (`medium-12`);
|
|
43
|
+
**Control** for summary rows (`small-4`); `--uxl-breakpoints-gutter` between the
|
|
44
|
+
main and aside columns in every mode ([spacing-steps.md](spacing-steps.md)).
|
|
42
45
|
|
|
43
46
|
## A11y
|
|
44
47
|
|
|
@@ -38,7 +38,7 @@ Standalone fields (email, language, phone, about) stay in the section stack at `
|
|
|
38
38
|
|
|
39
39
|
## Spacing
|
|
40
40
|
|
|
41
|
-
Page column: [how-to-page-layout.md](how-to-page-layout.md). Section gap
|
|
41
|
+
**Density: `standard`** — settings are read and decided on, so comprehension beats volume; do not compact a preferences page ([density.md](density.md)). Page column: [how-to-page-layout.md](how-to-page-layout.md). **Section** gap `medium-12`. **Tight** lockup internals `micro-2`. **Group** form field stack `medium-8`. **Control** inline related fields `small-4`. Do not add extra padding around `ListControls` — its row rhythm is component-owned. Form actions: [how-to-page-layout.md](how-to-page-layout.md#action-clusters).
|
|
42
42
|
|
|
43
43
|
## A11y
|
|
44
44
|
|
|
@@ -65,7 +65,7 @@ Import from `@uxelle/components`. Pass `bottomText` (or `""`) so the catalog dem
|
|
|
65
65
|
<Lockup
|
|
66
66
|
topSlot={false}
|
|
67
67
|
title={
|
|
68
|
-
<Text type="Display
|
|
68
|
+
<Text type="Display Small" as="h1" width={false}>
|
|
69
69
|
Settings
|
|
70
70
|
</Text>
|
|
71
71
|
}
|
|
@@ -163,7 +163,7 @@ Import from `@uxelle/components`. Pass `bottomText` (or `""`) so the catalog dem
|
|
|
163
163
|
</Layout>
|
|
164
164
|
```
|
|
165
165
|
|
|
166
|
-
If
|
|
166
|
+
If chrome already renders `<main>` (the skip-link target), omit `as="main"` — but still render this column yourself, `p` and `maxWidth="600px"` included. Chrome supplies only the scrollport, never a column, so there is nothing to avoid duplicating ([how-to-page-layout.md](how-to-page-layout.md#who-owns-the-content-column)).
|
|
167
167
|
|
|
168
168
|
## A2UI
|
|
169
169
|
|
|
@@ -38,8 +38,13 @@ its track. A row variant can use vertical `Divider`s between cells that simply w
|
|
|
38
38
|
|
|
39
39
|
## Spacing
|
|
40
40
|
|
|
41
|
-
`gap="var(--uxl-breakpoints-gutter)"` between cells
|
|
42
|
-
|
|
41
|
+
`gap="var(--uxl-breakpoints-gutter)"` between cells — the grid gap is
|
|
42
|
+
density-invariant, so KPI tiles align identically in every mode.
|
|
43
|
+
|
|
44
|
+
This piece serves both ends of the density range and inherits the host's mode
|
|
45
|
+
([density.md](density.md)). As dashboard KPIs on a `compact` page it takes no band
|
|
46
|
+
padding at all and sits at the page's `medium-9` **Section** gap. As a marketing
|
|
47
|
+
band on a `spacious` page it adds breathing with the **Band** step (`large-15`)
|
|
43
48
|
([spacing-steps.md](spacing-steps.md)).
|
|
44
49
|
|
|
45
50
|
## A11y
|
|
@@ -69,7 +74,7 @@ Plain figures (marketing proof band):
|
|
|
69
74
|
gap="var(--uxl-breakpoints-gutter)"
|
|
70
75
|
aria-label="By the numbers"
|
|
71
76
|
>
|
|
72
|
-
<Layout display="flex" flexDirection="column" gap="var(--uxl-theme-layout-spacing-micro-
|
|
77
|
+
<Layout display="flex" flexDirection="column" gap="var(--uxl-theme-layout-spacing-micro-3)">
|
|
73
78
|
<Text type="Display Medium" as="p" width={false}>10k+</Text>
|
|
74
79
|
<Text type="Body Small" width={false}>Teams onboarded</Text>
|
|
75
80
|
</Layout>
|
|
@@ -19,8 +19,10 @@ Any region backed by data or an async action: lists, tables, cards, forms, panel
|
|
|
19
19
|
- Do not blank the whole page for a partial load — scope the state to the region.
|
|
20
20
|
- Do not signal status with color alone; pair with an icon or words
|
|
21
21
|
([how-to-color.md](how-to-color.md)).
|
|
22
|
-
- Do not swap a `SkeletonGroup` out of the tree while loading
|
|
23
|
-
toggle `loading
|
|
22
|
+
- Do not swap a `SkeletonGroup` out of the tree while loading. Keep **the group**
|
|
23
|
+
mounted across the whole load and toggle its `loading` prop; swapping its
|
|
24
|
+
*children* between shapes and real content is expected (see the example).
|
|
25
|
+
Unmounting the group loses the load/complete announcement.
|
|
24
26
|
|
|
25
27
|
## Loading
|
|
26
28
|
|
|
@@ -68,7 +70,10 @@ where the data would be, plus the action that creates the first item.
|
|
|
68
70
|
## Error
|
|
69
71
|
|
|
70
72
|
A recoverable, in-flow failure uses a non-dismissible `danger` `Banner` with a retry
|
|
71
|
-
action.
|
|
73
|
+
action. The single action passes no `direction` — row is the `ButtonGroup` default
|
|
74
|
+
and one child looks the same either way. Add a second text button (a Dismiss beside
|
|
75
|
+
Try again) and the cluster takes the tablet switch like any other
|
|
76
|
+
([how-to-page-layout.md](how-to-page-layout.md#action-clusters)).
|
|
72
77
|
|
|
73
78
|
```tsx
|
|
74
79
|
<Banner
|
|
@@ -77,7 +82,7 @@ action.
|
|
|
77
82
|
alert
|
|
78
83
|
leadingSlotContent={<Icon iconName="error" variant="sharpUnfilled" aria-hidden />}
|
|
79
84
|
actionsSlotContent={
|
|
80
|
-
<ButtonGroup
|
|
85
|
+
<ButtonGroup aria-label="Error actions">
|
|
81
86
|
<Button emphasis="medium" onClick={retry}>
|
|
82
87
|
<Text type="Button" width>Try again</Text>
|
|
83
88
|
</Button>
|
|
@@ -33,9 +33,11 @@ long, stack each pair (label above value) below tablet by switching
|
|
|
33
33
|
|
|
34
34
|
## Spacing
|
|
35
35
|
|
|
36
|
-
Row/column `gap` =
|
|
37
|
-
`
|
|
38
|
-
|
|
36
|
+
Row/column `gap` = the **Control** step
|
|
37
|
+
(`var(--uxl-theme-layout-spacing-small-4)` under `standard`, `micro-3` under
|
|
38
|
+
`compact` — a key/value summary is a common dense surface). Reset the browser `dd`
|
|
39
|
+
indent with `margin: 0`. Group inside a section at the page rhythm
|
|
40
|
+
([spacing-steps.md](spacing-steps.md), [density.md](density.md)).
|
|
39
41
|
|
|
40
42
|
## A11y
|
|
41
43
|
|
|
@@ -32,6 +32,12 @@ narration.
|
|
|
32
32
|
([how-to-page-layout.md](how-to-page-layout.md#responsiveness)).
|
|
33
33
|
5. **Spacing** — stacks use `var(--uxl-theme-layout-spacing-*)` (or `0`); page
|
|
34
34
|
chrome uses the breakpoint tokens ([spacing-steps.md](spacing-steps.md)).
|
|
35
|
+
Name spacing by **role** (Flush / Tight / Control / Group / Section / Band), not
|
|
36
|
+
by a bare step. An **experience** declares its default **density mode**
|
|
37
|
+
(`compact` | `standard` | `spacious`) and why; a **piece** says it inherits the
|
|
38
|
+
host's mode and gives the resolution for the modes it realistically appears in.
|
|
39
|
+
Call out anything density-invariant (component-owned padding, grid gutters)
|
|
40
|
+
([density.md](density.md)).
|
|
35
41
|
6. **A11y** — landmarks, heading ranks, names, live regions
|
|
36
42
|
([how-to-accessibility.md](how-to-accessibility.md)).
|
|
37
43
|
7. **Color** — include when the recipe uses palettes or brand bands: set
|
|
@@ -32,8 +32,10 @@ A single testimonial is a centered, measure-capped block. A set reflows with
|
|
|
32
32
|
|
|
33
33
|
## Spacing
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
**Control** step between the quote and its attribution (`small-6` on the `spacious`
|
|
36
|
+
marketing page, `small-4` under `standard`); `--uxl-breakpoints-gutter` between
|
|
37
|
+
cards in a grid in every mode; the **Band** step (`large-15`) as a band
|
|
38
|
+
([density.md](density.md), [spacing-steps.md](spacing-steps.md)).
|
|
37
39
|
|
|
38
40
|
## A11y
|
|
39
41
|
|
|
@@ -54,7 +56,7 @@ associated. A customer `Logo` is decorative when the name is in text
|
|
|
54
56
|
</Text>
|
|
55
57
|
</blockquote>
|
|
56
58
|
<figcaption>
|
|
57
|
-
<Layout display="flex" flexDirection="column" gap="var(--uxl-theme-layout-spacing-micro-
|
|
59
|
+
<Layout display="flex" flexDirection="column" gap="var(--uxl-theme-layout-spacing-micro-3)">
|
|
58
60
|
<Text type="Body Small Alt" width={false}>Dana Lee</Text>
|
|
59
61
|
<Text type="Condensed" width={false}>VP Operations, Northwind</Text>
|
|
60
62
|
</Layout>
|
|
@@ -18,16 +18,24 @@ number"). The size band must match the index, so `medium-10` exists but
|
|
|
18
18
|
|
|
19
19
|
## Choosing a step
|
|
20
20
|
|
|
21
|
+
Do not pick a step directly. Pick the **role** the space plays, then resolve it
|
|
22
|
+
through the experience's **density mode** — [density.md](density.md).
|
|
23
|
+
|
|
21
24
|
- **Flush** (line boxes already separate lines, e.g. name over email) -> `0`.
|
|
22
|
-
- **Tight stacks / chips
|
|
23
|
-
- **
|
|
25
|
+
- **Tight** stacks / chips / lockup internals.
|
|
26
|
+
- **Control** — related controls and field stacks. Same-row clusters (search +
|
|
24
27
|
export, filter + facet) use this gap and stay `nowrap`.
|
|
25
|
-
- **
|
|
26
|
-
- **
|
|
27
|
-
- **
|
|
28
|
+
- **Group** — bespoke section padding and subgroups (also the default A2UI gap).
|
|
29
|
+
- **Section** — between page sections (lockup vs table vs a later block).
|
|
30
|
+
- **Band** — block padding on a full-bleed band (hero, CTA).
|
|
31
|
+
|
|
32
|
+
The `standard` mode resolves these to `0` / `micro-2` / `small-4` / `medium-8` /
|
|
33
|
+
`medium-12` / `large-13` — the harness baseline. `compact` shifts the column down
|
|
34
|
+
for data-dense product surfaces, `spacious` up for marketing. Resolve the whole
|
|
35
|
+
column from one mode; never tighten a single role in isolation.
|
|
28
36
|
|
|
29
|
-
Do not use the
|
|
30
|
-
|
|
37
|
+
Do not use the **Section** gap between a table's tabs and its grid — those belong
|
|
38
|
+
together at the **Control** gap.
|
|
31
39
|
|
|
32
40
|
## Usage
|
|
33
41
|
|
|
@@ -50,7 +58,8 @@ emit them in new generated UI** — use the indexed tokens.
|
|
|
50
58
|
## Not part of this ramp
|
|
51
59
|
|
|
52
60
|
Page inset, column cap, and column/`fr` gaps are **page chrome**, not ramp steps:
|
|
53
|
-
`--uxl-breakpoints-margin` / `-max-container-width` / `-gutter`.
|
|
61
|
+
`--uxl-breakpoints-margin` / `-max-container-width` / `-gutter`. They are also
|
|
62
|
+
**density-invariant** — they do not change with the mode ([density.md](density.md)). See
|
|
54
63
|
[how-to-page-layout.md](how-to-page-layout.md) and [tokens.md](tokens.md). One-column
|
|
55
64
|
reading content (a settings/form page, or a form / FAQ / prose section on a wider
|
|
56
65
|
page) may cap at the `maxWidth="600px"` reading measure — centered — instead of the
|
|
@@ -38,8 +38,10 @@ Never hardcode hex/rgb for themeable UI. Palette selection (`default`,
|
|
|
38
38
|
## Spacing (layout ramp)
|
|
39
39
|
|
|
40
40
|
Stacks on `Layout` / `A2uiLayout` pass `0` or `var(--uxl-theme-layout-spacing-*)`
|
|
41
|
-
on `gap` / padding / margin. The full 0-16 ramp and
|
|
42
|
-
[spacing-steps.md](spacing-steps.md)
|
|
41
|
+
on `gap` / padding / margin. The full 0-16 ramp and the spacing **roles** are in
|
|
42
|
+
[spacing-steps.md](spacing-steps.md); which step each role resolves to depends on
|
|
43
|
+
the experience's **density mode** ([density.md](density.md)). Set `display="flex"`
|
|
44
|
+
(or `grid`) when using `gap`.
|
|
43
45
|
|
|
44
46
|
## Page chrome (React app/page body)
|
|
45
47
|
|
|
@@ -50,6 +52,13 @@ Usage in [how-to-page-layout.md](how-to-page-layout.md).
|
|
|
50
52
|
- `--uxl-breakpoints-margin` — padding around the body
|
|
51
53
|
- `--uxl-breakpoints-gutter` — gap between columns / `fr` tracks
|
|
52
54
|
|
|
55
|
+
`--uxl-breakpoints-padding-<band>-<index>` is the per-breakpoint source the spacing
|
|
56
|
+
ramp aliases (`--uxl-theme-layout-spacing-medium-8` resolves to
|
|
57
|
+
`--uxl-breakpoints-padding-medium-8`). Use the `--uxl-theme-layout-spacing-*` name
|
|
58
|
+
in app and recipe CSS; the `padding` family appears only where a catalog component
|
|
59
|
+
documents its own band padding (e.g. `Footer` —
|
|
60
|
+
[recipe-app-chrome.md](recipe-app-chrome.md)). Same index rule applies.
|
|
61
|
+
|
|
53
62
|
Breakpoint bounds (read-only, for the rare JS switch) are exposed as
|
|
54
63
|
`--uxl-theme-layout-{mobile,tablet,desktop,desktop-plus}-screen-width-{min,max}`,
|
|
55
64
|
and the active band as `--uxl-breakpoints-screen-width-{min,max}`. Reference these
|
|
@@ -71,12 +80,44 @@ must, compose a `box-shadow` from levels `1`-`3` at depths `1`-`4`:
|
|
|
71
80
|
## Typography
|
|
72
81
|
|
|
73
82
|
Render copy with the `Text` component (`type="Display Small"`, `Body Medium`,
|
|
74
|
-
`Button`, …) and its `as` prop — never set font-size in app CSS.
|
|
75
|
-
|
|
83
|
+
`Button`, …) and its `as` prop — never set font-size in app CSS. Which `type` a
|
|
84
|
+
heading rank gets depends on the density mode ([density.md](density.md)).
|
|
85
|
+
Font-family variables exist for custom chrome only:
|
|
76
86
|
|
|
77
87
|
- `--uxl-component-text-font-family-display`
|
|
78
88
|
- `--uxl-component-text-font-family-body`
|
|
79
89
|
|
|
90
|
+
### `width` on `Text`
|
|
91
|
+
|
|
92
|
+
`width` defaults to `true` and makes the text box **hug its content**;
|
|
93
|
+
`width={false}` makes it **span its container**, which is what lets long copy wrap
|
|
94
|
+
and `truncation` clip to a stable width.
|
|
95
|
+
|
|
96
|
+
- **`width={false}`** for anything that wraps or truncates — headings, body copy,
|
|
97
|
+
descriptions, `Lockup` slots, `Dialog` titles and bodies.
|
|
98
|
+
- **`width`** (the default) for a short label inside a control — `type="Button"`
|
|
99
|
+
labels, chips, badges — so the control sizes to its text.
|
|
100
|
+
|
|
101
|
+
Chip labels are **component-owned**. Pass the `label` string on `ChoiceChip` /
|
|
102
|
+
`FilterChip` (and their A2UI adapters) — do not wrap a `Text` node.
|
|
103
|
+
|
|
104
|
+
- **ChoiceChip** — `Condensed` when unchecked, `Condensed Alt` when checked.
|
|
105
|
+
Exclusive single-select is one checked value in app/host state. Keep
|
|
106
|
+
`role="group"`; chips stay `aria-pressed` toggles. Use `RadioGroup` or
|
|
107
|
+
`SegmentedControl` for radio semantics.
|
|
108
|
+
- **FilterChip** — `Condensed`. Labels hug content so `FilterChipGroup` wraps
|
|
109
|
+
whole chips. A chip wider than its container ellipsizes so the dismiss
|
|
110
|
+
control stays visible.
|
|
111
|
+
|
|
112
|
+
## Inline `style` on the React track
|
|
113
|
+
|
|
114
|
+
React recipes may use inline `style` for what `Layout` props do not express —
|
|
115
|
+
a scrollport (`overflow`, `minHeight`), a `backgroundColor` on a palette region, or
|
|
116
|
+
native `dl` grid tracks — **provided every value is a `--uxl-*` variable**. Inline
|
|
117
|
+
`style` is not a licence to hardcode: no hex, no px, no font-size. The "no
|
|
118
|
+
`className` / `style`" rule is **A2UI-only**, where styling must come from adapter
|
|
119
|
+
props ([a2ui.md](a2ui.md)).
|
|
120
|
+
|
|
80
121
|
## Component tokens
|
|
81
122
|
|
|
82
123
|
`--uxl-component-<component>-*` tokens style a specific component from inside that
|