@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,108 @@
|
|
|
1
|
+
# Recipe: multi-step flow
|
|
2
|
+
|
|
3
|
+
A product **experience**: a task split into ordered steps — onboarding, checkout,
|
|
4
|
+
guided setup. A stepper shows progress, each step is a focused form, and a final
|
|
5
|
+
review confirms before commit.
|
|
6
|
+
|
|
7
|
+
Assembles [`Stepper`](../uxelle-components/Stepper.md) (with `StepperItem`),
|
|
8
|
+
[form-section](recipe-form-section.md), [summary-list](recipe-summary-list.md) (the
|
|
9
|
+
review), [`ButtonGroup`](../uxelle-components/ButtonGroup.md), and
|
|
10
|
+
[states](recipe-states.md) (submit feedback).
|
|
11
|
+
|
|
12
|
+
## When
|
|
13
|
+
|
|
14
|
+
A multi-step task with a clear order and a commit at the end.
|
|
15
|
+
|
|
16
|
+
## When not
|
|
17
|
+
|
|
18
|
+
- A single form is a [form-section](recipe-form-section.md), not a flow.
|
|
19
|
+
- The `Stepper` is **progress**, not page navigation — do not wire it to routes as
|
|
20
|
+
primary nav.
|
|
21
|
+
- Do not skip the review step before an irreversible commit.
|
|
22
|
+
|
|
23
|
+
## Regions
|
|
24
|
+
|
|
25
|
+
page column -> flow title (`h1`) -> `<nav>` `Stepper` (progress) -> current step
|
|
26
|
+
(step `h2` + [form-section](recipe-form-section.md)) -> footer action row
|
|
27
|
+
(Back / Next, Next becomes Confirm on the review step)
|
|
28
|
+
|
|
29
|
+
## Responsive
|
|
30
|
+
|
|
31
|
+
`Stepper` is `horizontal` from tablet up and vertical below
|
|
32
|
+
(`useBreakpointUp("tablet")`); the footer `ButtonGroup` stacks `fullWidth` when
|
|
33
|
+
narrow. See [how-to-page-layout.md](how-to-page-layout.md#action-clusters).
|
|
34
|
+
|
|
35
|
+
## Spacing
|
|
36
|
+
|
|
37
|
+
`medium-12` between the stepper, the step body, and the footer; the step form uses
|
|
38
|
+
its own `medium-8` field stack ([spacing-steps.md](spacing-steps.md)).
|
|
39
|
+
|
|
40
|
+
## A11y
|
|
41
|
+
|
|
42
|
+
One `main`, one `h1` (the flow); each step title is `h2`. Wrap the `Stepper` in
|
|
43
|
+
`<nav aria-label="Progress">`. Move focus to the step heading on change and keep the
|
|
44
|
+
`currentStep` in sync. The review uses a [summary-list](recipe-summary-list.md);
|
|
45
|
+
submit feedback follows [states](recipe-states.md). See
|
|
46
|
+
[how-to-accessibility.md](how-to-accessibility.md).
|
|
47
|
+
|
|
48
|
+
## React
|
|
49
|
+
|
|
50
|
+
```tsx
|
|
51
|
+
const tabletUp = useBreakpointUp("tablet");
|
|
52
|
+
const steps = [
|
|
53
|
+
{ label: "Details", description: "Your workspace" },
|
|
54
|
+
{ label: "Members", description: "Invite people" },
|
|
55
|
+
{ label: "Review", description: "Confirm" },
|
|
56
|
+
];
|
|
57
|
+
const isLast = step === steps.length;
|
|
58
|
+
|
|
59
|
+
<Layout
|
|
60
|
+
display="flex"
|
|
61
|
+
flexDirection="column"
|
|
62
|
+
width="100%"
|
|
63
|
+
maxWidth="600px"
|
|
64
|
+
mh="auto"
|
|
65
|
+
p="var(--uxl-breakpoints-margin)"
|
|
66
|
+
gap="var(--uxl-theme-layout-spacing-medium-12)"
|
|
67
|
+
>
|
|
68
|
+
<Text type="Display Extra Small" as="h1" width={false}>Create a workspace</Text>
|
|
69
|
+
|
|
70
|
+
<nav aria-label="Progress">
|
|
71
|
+
<Stepper
|
|
72
|
+
steps={steps}
|
|
73
|
+
currentStep={step}
|
|
74
|
+
horizontal={tabletUp}
|
|
75
|
+
onStepClick={(s) => s < step && setStep(s)}
|
|
76
|
+
/>
|
|
77
|
+
</nav>
|
|
78
|
+
|
|
79
|
+
<Layout as="section" display="flex" flexDirection="column" gap="var(--uxl-theme-layout-spacing-medium-8)">
|
|
80
|
+
<Text type="Display Extra Small" as="h2" width={false}>
|
|
81
|
+
{steps[step - 1].label}
|
|
82
|
+
</Text>
|
|
83
|
+
{/* Steps 1-2: a form-section. Final step: a summary-list review. */}
|
|
84
|
+
{isLast ? <ReviewSummary /> : <StepForm step={step} />}
|
|
85
|
+
</Layout>
|
|
86
|
+
|
|
87
|
+
<ButtonGroup
|
|
88
|
+
direction={tabletUp ? "row" : "column"}
|
|
89
|
+
fullWidth={!tabletUp}
|
|
90
|
+
aria-label="Flow navigation"
|
|
91
|
+
>
|
|
92
|
+
<Button emphasis="medium" disabled={step === 1} onClick={() => setStep(step - 1)}>
|
|
93
|
+
<Text type="Button" width>Back</Text>
|
|
94
|
+
</Button>
|
|
95
|
+
<Button emphasis="high" onClick={isLast ? onConfirm : () => setStep(step + 1)}>
|
|
96
|
+
<Text type="Button" width>{isLast ? "Confirm" : "Next"}</Text>
|
|
97
|
+
</Button>
|
|
98
|
+
</ButtonGroup>
|
|
99
|
+
</Layout>
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
The flow is one column, so the `600px` cap applies ([how-to-page-layout.md](how-to-page-layout.md)).
|
|
103
|
+
|
|
104
|
+
## A2UI
|
|
105
|
+
|
|
106
|
+
No `getUxelleRecipe("multi-step-flow")` — compose `A2uiStepper` + `A2uiStepperItem`,
|
|
107
|
+
the step form adapters, and an `A2uiButtonGroup` footer (`"direction": "column"`).
|
|
108
|
+
Bind `currentStep` and field values to the host model ([a2ui.md](a2ui.md)).
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# Recipe: page header
|
|
2
|
+
|
|
3
|
+
A shared **piece**: the top band of a page — title, optional supporting line,
|
|
4
|
+
optional status, and page-level actions. Used by
|
|
5
|
+
[data-table-page](recipe-data-table-page.md), [record-detail](recipe-record-detail.md),
|
|
6
|
+
[dashboard-overview](recipe-dashboard-overview.md), and most product pages.
|
|
7
|
+
|
|
8
|
+
Composes [`Lockup`](../uxelle-components/Lockup.md),
|
|
9
|
+
[`LabelBadge`](../uxelle-components/LabelBadge.md),
|
|
10
|
+
[`ButtonGroup`](../uxelle-components/ButtonGroup.md), and
|
|
11
|
+
[`Text`](../uxelle-components/Text.md). Optional [`Breadcrumbs`](../uxelle-components/Breadcrumbs.md)
|
|
12
|
+
sit above it, in the page column.
|
|
13
|
+
|
|
14
|
+
## When
|
|
15
|
+
|
|
16
|
+
The first section of a page body: one `h1`, optional supporting copy, optional
|
|
17
|
+
status, and the actions that operate on the whole page.
|
|
18
|
+
|
|
19
|
+
## When not
|
|
20
|
+
|
|
21
|
+
- Not a `PageHeader` component — it is a `Layout` composition.
|
|
22
|
+
- Page actions only (create, import, export, delete). Controls that filter a table
|
|
23
|
+
belong to the [query-bar](recipe-query-bar.md); tabs that swap a table's rows
|
|
24
|
+
belong on the `Table` ([data-table-page](recipe-data-table-page.md)).
|
|
25
|
+
- Do not wrap `Lockup` in a nested `Layout` to share the row — let it be the flex
|
|
26
|
+
sibling that shrinks and put `flexShrink={0}` on the action cluster.
|
|
27
|
+
|
|
28
|
+
## Regions
|
|
29
|
+
|
|
30
|
+
optional `Breadcrumbs` (in the column) -> header row: left `Lockup`
|
|
31
|
+
(`h1` + optional body, optional status `LabelBadge` in `overline`) · right action
|
|
32
|
+
`ButtonGroup`
|
|
33
|
+
|
|
34
|
+
## Responsive
|
|
35
|
+
|
|
36
|
+
Row from tablet up, column below (`useBreakpointUp("tablet")`,
|
|
37
|
+
[how-to-page-layout.md](how-to-page-layout.md#action-clusters)); the stacked actions
|
|
38
|
+
go `fullWidth`. Keep `flexWrap="nowrap"` — never `wrap` (the full-width `Lockup`
|
|
39
|
+
would claim the row and drop the actions).
|
|
40
|
+
|
|
41
|
+
## Spacing
|
|
42
|
+
|
|
43
|
+
`small-4` gap between title and actions. Lockup internals are `micro-2`
|
|
44
|
+
(component-owned). The header sits in the page column at the `medium-12` section gap
|
|
45
|
+
([spacing-steps.md](spacing-steps.md)).
|
|
46
|
+
|
|
47
|
+
## A11y
|
|
48
|
+
|
|
49
|
+
One `h1` per page, here. Give the action `ButtonGroup` an `aria-label`. A status
|
|
50
|
+
`LabelBadge` conveys meaning by its visible text; keep `dataColorSwitcher` paired
|
|
51
|
+
with that text ([how-to-color.md](how-to-color.md)).
|
|
52
|
+
|
|
53
|
+
## React
|
|
54
|
+
|
|
55
|
+
```tsx
|
|
56
|
+
const tabletUp = useBreakpointUp("tablet");
|
|
57
|
+
|
|
58
|
+
<Layout
|
|
59
|
+
display="flex"
|
|
60
|
+
flexDirection={tabletUp ? "row" : "column"}
|
|
61
|
+
flexWrap="nowrap"
|
|
62
|
+
justifyContent="space-between"
|
|
63
|
+
alignItems={tabletUp ? "flex-end" : "stretch"}
|
|
64
|
+
gap="var(--uxl-theme-layout-spacing-small-4)"
|
|
65
|
+
>
|
|
66
|
+
<Lockup
|
|
67
|
+
overline={<LabelBadge label="Active" dataColorSwitcher="success" emphasis="low" />}
|
|
68
|
+
title={
|
|
69
|
+
<Text type="Display Small" as="h1" width={false}>
|
|
70
|
+
Invoices
|
|
71
|
+
</Text>
|
|
72
|
+
}
|
|
73
|
+
body={
|
|
74
|
+
<Text type="Body Medium" width={false}>
|
|
75
|
+
Every invoice across your workspace.
|
|
76
|
+
</Text>
|
|
77
|
+
}
|
|
78
|
+
/>
|
|
79
|
+
<Layout flexShrink={0}>
|
|
80
|
+
<ButtonGroup
|
|
81
|
+
direction={tabletUp ? "row" : "column"}
|
|
82
|
+
fullWidth={!tabletUp}
|
|
83
|
+
aria-label="Invoice actions"
|
|
84
|
+
>
|
|
85
|
+
<Button emphasis="medium">
|
|
86
|
+
<Text type="Button" width>Export</Text>
|
|
87
|
+
</Button>
|
|
88
|
+
<Button emphasis="high">
|
|
89
|
+
<Text type="Button" width>New invoice</Text>
|
|
90
|
+
</Button>
|
|
91
|
+
</ButtonGroup>
|
|
92
|
+
</Layout>
|
|
93
|
+
</Layout>
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Omit `overline` when there is no status; omit `body` when the title stands alone.
|
|
97
|
+
|
|
98
|
+
## A2UI
|
|
99
|
+
|
|
100
|
+
No `getUxelleRecipe("page-header")` — compose adapters ([a2ui.md](a2ui.md)).
|
|
101
|
+
`Lockup` has no adapter: stack `A2uiText` (optional `A2uiLabelBadge`, then the `h1`,
|
|
102
|
+
then the body). Actions use `A2uiButtonGroup` with `"direction": "column"`.
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Recipe: page shell
|
|
2
|
+
|
|
3
|
+
## When
|
|
4
|
+
|
|
5
|
+
Themed page column with stacked sections. Records: [recipe-data-table-page.md](recipe-data-table-page.md). Preferences: [recipe-settings-page.md](recipe-settings-page.md).
|
|
6
|
+
|
|
7
|
+
## When not
|
|
8
|
+
|
|
9
|
+
- Not a layout framework; not a `PageShell` component.
|
|
10
|
+
- Agents do not set theme attributes (`data-*` / `A2uiThemeScope` are host-owned).
|
|
11
|
+
- Do not hardcode `1120px` / `1600px`. React uses `--uxl-breakpoints-max-container-width`, except a **one-column** settings/form page may use `600px` — [recipe-settings-page.md](recipe-settings-page.md).
|
|
12
|
+
- A2UI chat surfaces do not use page chrome tokens — keep `var(--uxl-theme-layout-spacing-*)` only.
|
|
13
|
+
|
|
14
|
+
## Regions
|
|
15
|
+
|
|
16
|
+
host canvas → content column (`max-container-width`, `mh="auto"`, `p` = breakpoints-margin) → sections
|
|
17
|
+
|
|
18
|
+
`medium-12` between **sections**. Controls that belong to a section (table tabs, query bar, form actions) stay inside that section at `small-4`. Do not lift data tabs up to sit under the page `h1` as if they were app navigation — that is [recipe-app-chrome.md](recipe-app-chrome.md) (`NavLink`). Table datasets: [recipe-data-table-page.md](recipe-data-table-page.md).
|
|
19
|
+
|
|
20
|
+
## Spacing
|
|
21
|
+
|
|
22
|
+
React page column: [how-to-page-layout.md](how-to-page-layout.md). Section stacks: medium-8 or small-4. Intro lockup internals: micro-2. Form/dialog action groups: [how-to-page-layout.md](how-to-page-layout.md#action-clusters). A2UI: layout-spacing only (`p` large-13, `gap` medium-12).
|
|
23
|
+
|
|
24
|
+
## A11y
|
|
25
|
+
|
|
26
|
+
[how-to-accessibility.md](how-to-accessibility.md). One `main` per page (host `<main>` or `as="main"` on this column). Heading rank starts at `h1` once; section titles `h2`.
|
|
27
|
+
|
|
28
|
+
## React
|
|
29
|
+
|
|
30
|
+
[`Lockup`](../uxelle-components/Lockup.md) for section intros; [`Layout`](../uxelle-components/Layout.md) + [`Text`](../uxelle-components/Text.md) build the column. Import from `@uxelle/components`.
|
|
31
|
+
|
|
32
|
+
```tsx
|
|
33
|
+
<Layout
|
|
34
|
+
as="main"
|
|
35
|
+
display="flex"
|
|
36
|
+
flexDirection="column"
|
|
37
|
+
width="100%"
|
|
38
|
+
maxWidth="var(--uxl-breakpoints-max-container-width)"
|
|
39
|
+
mh="auto"
|
|
40
|
+
p="var(--uxl-breakpoints-margin)"
|
|
41
|
+
gap="var(--uxl-theme-layout-spacing-medium-12)"
|
|
42
|
+
>
|
|
43
|
+
<Lockup
|
|
44
|
+
topSlot={false}
|
|
45
|
+
title={
|
|
46
|
+
<Text type="Display Extra Small" as="h1" width={false}>
|
|
47
|
+
Account settings
|
|
48
|
+
</Text>
|
|
49
|
+
}
|
|
50
|
+
body={
|
|
51
|
+
<Text type="Body Medium" width={false}>
|
|
52
|
+
Manage how your team appears across products.
|
|
53
|
+
</Text>
|
|
54
|
+
}
|
|
55
|
+
/>
|
|
56
|
+
<Layout as="section" display="flex" flexDirection="column" gap="var(--uxl-theme-layout-spacing-small-4)">
|
|
57
|
+
<Lockup
|
|
58
|
+
overline={<Text type="Overline" width={false}>Workspace</Text>}
|
|
59
|
+
title={
|
|
60
|
+
<Text type="Display Extra Small" as="h2" width={false}>
|
|
61
|
+
Profile
|
|
62
|
+
</Text>
|
|
63
|
+
}
|
|
64
|
+
body={
|
|
65
|
+
<Text type="Body Medium" width={false}>
|
|
66
|
+
Shown on invoices and shared reports.
|
|
67
|
+
</Text>
|
|
68
|
+
}
|
|
69
|
+
/>
|
|
70
|
+
</Layout>
|
|
71
|
+
</Layout>
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
If the host already renders `<main>` (skip-link target), omit `as="main"` on this column.
|
|
75
|
+
|
|
76
|
+
## Responsive
|
|
77
|
+
|
|
78
|
+
The capped content column reflows on its own — `--uxl-breakpoints-max-container-width`, `-margin`, and section gaps remap per breakpoint, so sections stack and tighten on mobile with nothing to set. Grids inside a section reflow with `auto-fit` / `minmax` ([how-to-page-layout.md](how-to-page-layout.md#responsiveness)).
|
|
79
|
+
|
|
80
|
+
## A2UI
|
|
81
|
+
|
|
82
|
+
Same `var(--uxl-theme-layout-spacing-*)` strings on `A2uiLayout` — no gutter / margin / max-container-width. Headings as `A2uiText` (`Lockup` has no adapter). Runtime: `getUxelleRecipe("page-shell")` from `@uxelle/a2ui`. See [a2ui.md](a2ui.md).
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Recipe: pricing
|
|
2
|
+
|
|
3
|
+
A marketing **piece**: two to four comparable plans, one marked recommended, each
|
|
4
|
+
with a price, a feature list, and a call to action.
|
|
5
|
+
|
|
6
|
+
Composes [`ProductCard`](../uxelle-components/ProductCard.md) (or
|
|
7
|
+
[`Card`](../uxelle-components/Card.md)),
|
|
8
|
+
[`LabelBadge`](../uxelle-components/LabelBadge.md) (the recommended flag),
|
|
9
|
+
[`List`](../uxelle-components/List.md) + [`ListItem`](../uxelle-components/ListItem.md)
|
|
10
|
+
(features), [`Text`](../uxelle-components/Text.md), and
|
|
11
|
+
[`ButtonGroup`](../uxelle-components/ButtonGroup.md).
|
|
12
|
+
|
|
13
|
+
## When
|
|
14
|
+
|
|
15
|
+
Comparable plans the visitor chooses between.
|
|
16
|
+
|
|
17
|
+
## When not
|
|
18
|
+
|
|
19
|
+
- Five-plus plans overwhelm — group or simplify.
|
|
20
|
+
- One recommended tier at most; do not badge them all.
|
|
21
|
+
- Section heading is `h2`; each plan name is `h3`.
|
|
22
|
+
|
|
23
|
+
## Regions
|
|
24
|
+
|
|
25
|
+
section -> grid of tier `ProductCard`s: top (recommended `LabelBadge` + plan `h3` +
|
|
26
|
+
price) · center (feature `List`) · bottom (CTA `Button`)
|
|
27
|
+
|
|
28
|
+
## Responsive
|
|
29
|
+
|
|
30
|
+
Tiers reflow with `repeat(auto-fit, minmax(MIN, 1fr))` — side by side on wide
|
|
31
|
+
viewports, stacked on mobile, no JS. Pick `MIN` (e.g. `16rem`) for the narrowest
|
|
32
|
+
comfortable tier. See [how-to-page-layout.md](how-to-page-layout.md#responsiveness).
|
|
33
|
+
|
|
34
|
+
## Spacing
|
|
35
|
+
|
|
36
|
+
`--uxl-breakpoints-gutter` between tiers; feature rows are the `List` default;
|
|
37
|
+
`large-13`+ block padding as a band ([spacing-steps.md](spacing-steps.md)).
|
|
38
|
+
|
|
39
|
+
## A11y
|
|
40
|
+
|
|
41
|
+
Section `h2`; plan names `h3`. Each CTA names its plan (`aria-label="Choose Team"`)
|
|
42
|
+
so buttons are distinguishable out of context. The recommended `LabelBadge` states
|
|
43
|
+
"Recommended" in text. See [how-to-accessibility.md](how-to-accessibility.md).
|
|
44
|
+
|
|
45
|
+
## React
|
|
46
|
+
|
|
47
|
+
```tsx
|
|
48
|
+
<Layout
|
|
49
|
+
as="section"
|
|
50
|
+
display="grid"
|
|
51
|
+
gridTemplateColumns="repeat(auto-fit, minmax(16rem, 1fr))"
|
|
52
|
+
gap="var(--uxl-breakpoints-gutter)"
|
|
53
|
+
aria-label="Plans"
|
|
54
|
+
>
|
|
55
|
+
<ProductCard
|
|
56
|
+
topSlotContent={
|
|
57
|
+
<>
|
|
58
|
+
<LabelBadge label="Recommended" emphasis="high" />
|
|
59
|
+
<Text type="Display Extra Small" as="h3" width={false}>Team</Text>
|
|
60
|
+
<Text type="Display Small" as="p" width={false}>$24<Text type="Body Small" as="span" inline> / user / mo</Text></Text>
|
|
61
|
+
</>
|
|
62
|
+
}
|
|
63
|
+
centerSlotContent={
|
|
64
|
+
<List>
|
|
65
|
+
<ListItem leadingSlot leading={<Icon iconName="check" variant="sharpUnfilled" aria-hidden />} centerText={<Text type="Body Small Alt">Unlimited projects</Text>} />
|
|
66
|
+
<ListItem leadingSlot leading={<Icon iconName="check" variant="sharpUnfilled" aria-hidden />} centerText={<Text type="Body Small Alt">Advanced roles</Text>} />
|
|
67
|
+
<ListItem leadingSlot leading={<Icon iconName="check" variant="sharpUnfilled" aria-hidden />} centerText={<Text type="Body Small Alt">Priority support</Text>} />
|
|
68
|
+
</List>
|
|
69
|
+
}
|
|
70
|
+
bottomSlotContent={
|
|
71
|
+
<Button fullWidth emphasis="high" aria-label="Choose Team">
|
|
72
|
+
<Text type="Button" width>Choose Team</Text>
|
|
73
|
+
</Button>
|
|
74
|
+
}
|
|
75
|
+
/>
|
|
76
|
+
{/* repeat tiers; only one carries the Recommended badge */}
|
|
77
|
+
</Layout>
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## A2UI
|
|
81
|
+
|
|
82
|
+
No `getUxelleRecipe("pricing")` and no `fr` grid — stack `A2uiProductCard` tiers in a
|
|
83
|
+
column; features via `A2uiList` + `A2uiListItem`; CTA `A2uiButton` ([a2ui.md](a2ui.md)).
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# Recipe: query bar
|
|
2
|
+
|
|
3
|
+
A shared **piece**: the toolbar that narrows the current view — filter, facets,
|
|
4
|
+
search, and export. It rides on the surface it controls (a `Table` `topSlot`, or a
|
|
5
|
+
card grid's header), not under the page title.
|
|
6
|
+
|
|
7
|
+
Composes [`Button`](../uxelle-components/Button.md),
|
|
8
|
+
[`Sheet`](../uxelle-components/Sheet.md),
|
|
9
|
+
[`Textfield`](../uxelle-components/Textfield.md),
|
|
10
|
+
[`ChoiceChip`](../uxelle-components/ChoiceChip.md), and
|
|
11
|
+
[`FilterChipGroup`](../uxelle-components/FilterChipGroup.md). Full table context:
|
|
12
|
+
[data-table-page](recipe-data-table-page.md).
|
|
13
|
+
|
|
14
|
+
## When
|
|
15
|
+
|
|
16
|
+
Controls that filter, facet, search, or export the rows/cards currently in view.
|
|
17
|
+
|
|
18
|
+
## When not
|
|
19
|
+
|
|
20
|
+
- Page-level actions (create, import, delete) belong to the [page-header](recipe-page-header.md).
|
|
21
|
+
- Data tabs that swap which rows are shown sit above the query bar, first in the
|
|
22
|
+
`Table` `topSlot` — not on this row.
|
|
23
|
+
- No filter *fields* on the bar: a filter *trigger* opens a right `Sheet`.
|
|
24
|
+
- No pagination here — that goes in the `Table` `bottomSlot`.
|
|
25
|
+
|
|
26
|
+
## Regions
|
|
27
|
+
|
|
28
|
+
one row, `justifyContent="space-between"`: **left** = constrain (Filter `Button`
|
|
29
|
+
-> `Sheet`, optional `ChoiceChip` facet) · **right** = find (search `Textfield`
|
|
30
|
+
bounded + `Export` `Button`, `nowrap`). Applied filters: `FilterChipGroup` on the
|
|
31
|
+
row **below**.
|
|
32
|
+
|
|
33
|
+
## Responsive
|
|
34
|
+
|
|
35
|
+
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.
|
|
37
|
+
Keep the filter and export buttons together (`flexShrink={0}`).
|
|
38
|
+
|
|
39
|
+
## Spacing
|
|
40
|
+
|
|
41
|
+
`small-4` gap within and between the two clusters; the applied-chips row is
|
|
42
|
+
`small-4` below. This whole stack lives in the table's `topSlot` at `small-4`, not
|
|
43
|
+
the page-section gap ([spacing-steps.md](spacing-steps.md)).
|
|
44
|
+
|
|
45
|
+
## A11y
|
|
46
|
+
|
|
47
|
+
Name the bar `aria-label="Table filters and search"`. The compact search
|
|
48
|
+
`Textfield` uses `aria-label` (no visible label). The filter `Sheet` has a `title`.
|
|
49
|
+
See [how-to-accessibility.md](how-to-accessibility.md).
|
|
50
|
+
|
|
51
|
+
## React
|
|
52
|
+
|
|
53
|
+
```tsx
|
|
54
|
+
<Layout display="flex" flexDirection="column" gap="var(--uxl-theme-layout-spacing-small-4)">
|
|
55
|
+
<Layout
|
|
56
|
+
display="flex"
|
|
57
|
+
flexDirection="row"
|
|
58
|
+
flexWrap="nowrap"
|
|
59
|
+
justifyContent="space-between"
|
|
60
|
+
alignItems="center"
|
|
61
|
+
gap="var(--uxl-theme-layout-spacing-small-4)"
|
|
62
|
+
aria-label="Table filters and search"
|
|
63
|
+
>
|
|
64
|
+
<Layout display="flex" flexDirection="row" flexWrap="nowrap" alignItems="center" gap="var(--uxl-theme-layout-spacing-small-4)">
|
|
65
|
+
<Sheet
|
|
66
|
+
direction="Right"
|
|
67
|
+
title="Filters"
|
|
68
|
+
closeLabel="Close filters"
|
|
69
|
+
trigger={
|
|
70
|
+
<Button emphasis="medium" leadingIcon leadingIconName="filter_list">
|
|
71
|
+
<Text type="Button" width>Status</Text>
|
|
72
|
+
</Button>
|
|
73
|
+
}
|
|
74
|
+
>
|
|
75
|
+
<CheckboxGroup name="status" label labelText="Status">
|
|
76
|
+
<Checkbox value="open"><Text type="Body Medium">Open</Text></Checkbox>
|
|
77
|
+
<Checkbox value="matched"><Text type="Body Medium">Matched</Text></Checkbox>
|
|
78
|
+
</CheckboxGroup>
|
|
79
|
+
</Sheet>
|
|
80
|
+
<ChoiceChip label="Top match" onChange={() => {}} />
|
|
81
|
+
</Layout>
|
|
82
|
+
<Layout display="flex" flexDirection="row" flexWrap="nowrap" alignItems="center" gap="var(--uxl-theme-layout-spacing-small-4)" flexShrink={0}>
|
|
83
|
+
<Layout width="16rem" flexShrink={0}>
|
|
84
|
+
<Textfield
|
|
85
|
+
label={false}
|
|
86
|
+
aria-label="Search by name"
|
|
87
|
+
name="table-search"
|
|
88
|
+
autoComplete="off"
|
|
89
|
+
placeholder="Search by name"
|
|
90
|
+
leadingIcon
|
|
91
|
+
leadingIconName="search"
|
|
92
|
+
/>
|
|
93
|
+
</Layout>
|
|
94
|
+
<Layout flexShrink={0}>
|
|
95
|
+
<Button emphasis="low"><Text type="Button" width>Export all</Text></Button>
|
|
96
|
+
</Layout>
|
|
97
|
+
</Layout>
|
|
98
|
+
</Layout>
|
|
99
|
+
{/* Applied filters */}
|
|
100
|
+
{/* <FilterChipGroup> … </FilterChipGroup> */}
|
|
101
|
+
</Layout>
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## A2UI
|
|
105
|
+
|
|
106
|
+
No `getUxelleRecipe("query-bar")` — compose `A2uiSheet` / `A2uiTextfield` /
|
|
107
|
+
`A2uiButton` / `A2uiChoiceChip` / `A2uiFilterChipGroup` ([a2ui.md](a2ui.md)). One
|
|
108
|
+
column; bind filter and search state to the host model.
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# Recipe: record detail
|
|
2
|
+
|
|
3
|
+
A product **experience**: one record (customer, invoice, project). A header with
|
|
4
|
+
title, status, and record actions; the key facts at a glance; then deeper sections,
|
|
5
|
+
optionally tabbed.
|
|
6
|
+
|
|
7
|
+
Assembles the [page-header](recipe-page-header.md),
|
|
8
|
+
[summary-list](recipe-summary-list.md),
|
|
9
|
+
[`TabsCompact`](../uxelle-components/TabsCompact.md), and section pieces
|
|
10
|
+
([form-section](recipe-form-section.md), [data-table-page](recipe-data-table-page.md),
|
|
11
|
+
[states](recipe-states.md)). Lives inside the [app-chrome](recipe-app-chrome.md).
|
|
12
|
+
|
|
13
|
+
## When
|
|
14
|
+
|
|
15
|
+
The drill-in for a single row: read its state, then act on it or explore related
|
|
16
|
+
data.
|
|
17
|
+
|
|
18
|
+
## When not
|
|
19
|
+
|
|
20
|
+
- Not a list — a collection is a [data-table-page](recipe-data-table-page.md).
|
|
21
|
+
- Tabs here switch **sections of this record** (Overview / Activity / Files), not
|
|
22
|
+
pages — page routes stay in `Navigation`.
|
|
23
|
+
- One `h1` (the record name); section headings are `h2`.
|
|
24
|
+
|
|
25
|
+
## Regions
|
|
26
|
+
|
|
27
|
+
optional `Breadcrumbs` -> [page-header](recipe-page-header.md) (`h1` + status
|
|
28
|
+
`LabelBadge` + record actions) -> body: main column + aside
|
|
29
|
+
[summary-list](recipe-summary-list.md) -> section content, optionally under
|
|
30
|
+
`TabsCompact`
|
|
31
|
+
|
|
32
|
+
## Responsive
|
|
33
|
+
|
|
34
|
+
The body is a two-column grid (main + aside) that collapses to one column below
|
|
35
|
+
tablet (`useBreakpointUp("desktop")` or `auto-fit`); the header stacks; `TabsCompact`
|
|
36
|
+
scrolls when the labels overflow. See [how-to-page-layout.md](how-to-page-layout.md#responsiveness).
|
|
37
|
+
|
|
38
|
+
## Spacing
|
|
39
|
+
|
|
40
|
+
`medium-12` between page sections; `--uxl-breakpoints-gutter` between the main and
|
|
41
|
+
aside columns; summary rows `small-4` ([spacing-steps.md](spacing-steps.md)).
|
|
42
|
+
|
|
43
|
+
## A11y
|
|
44
|
+
|
|
45
|
+
One `main`, one `h1`. The status `LabelBadge` states meaning in text. `TabsCompact`
|
|
46
|
+
gets an `aria-label` when the tablist needs a name. Related sections use `h2`. See
|
|
47
|
+
[how-to-accessibility.md](how-to-accessibility.md).
|
|
48
|
+
|
|
49
|
+
## React
|
|
50
|
+
|
|
51
|
+
```tsx
|
|
52
|
+
const desktopUp = useBreakpointUp("desktop");
|
|
53
|
+
|
|
54
|
+
<Layout
|
|
55
|
+
display="flex"
|
|
56
|
+
flexDirection="column"
|
|
57
|
+
width="100%"
|
|
58
|
+
maxWidth="var(--uxl-breakpoints-max-container-width)"
|
|
59
|
+
mh="auto"
|
|
60
|
+
p="var(--uxl-breakpoints-margin)"
|
|
61
|
+
gap="var(--uxl-theme-layout-spacing-medium-12)"
|
|
62
|
+
>
|
|
63
|
+
{/* Header with status + actions — recipe-page-header.md */}
|
|
64
|
+
<RecordHeader />
|
|
65
|
+
|
|
66
|
+
<Layout
|
|
67
|
+
display="grid"
|
|
68
|
+
gridTemplateColumns={desktopUp ? "2fr 1fr" : "1fr"}
|
|
69
|
+
gap="var(--uxl-breakpoints-gutter)"
|
|
70
|
+
>
|
|
71
|
+
{/* Main: tabbed sections of THIS record */}
|
|
72
|
+
<Layout as="section" display="flex" flexDirection="column" gap="var(--uxl-theme-layout-spacing-small-4)">
|
|
73
|
+
<TabsCompact
|
|
74
|
+
amount={3}
|
|
75
|
+
tabLabels={["Overview", "Activity", "Files"]}
|
|
76
|
+
activeTab={tab}
|
|
77
|
+
onChange={setTab}
|
|
78
|
+
aria-label="Record sections"
|
|
79
|
+
/>
|
|
80
|
+
{/* render the active section: form-section / table / states */}
|
|
81
|
+
<SectionForActiveTab tab={tab} />
|
|
82
|
+
</Layout>
|
|
83
|
+
|
|
84
|
+
{/* Aside: key facts — recipe-summary-list.md */}
|
|
85
|
+
<Layout as="aside" aria-label="Summary">
|
|
86
|
+
<RecordSummaryList />
|
|
87
|
+
</Layout>
|
|
88
|
+
</Layout>
|
|
89
|
+
</Layout>
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## A2UI
|
|
93
|
+
|
|
94
|
+
No `getUxelleRecipe("record-detail")` — one narrow column: stacked `A2uiText` header
|
|
95
|
+
(no `Lockup` adapter), `A2uiLabelBadge` status, `A2uiTabGroup` + `A2uiTab` for
|
|
96
|
+
sections, and stacked label/value `A2uiText` for the summary ([a2ui.md](a2ui.md)).
|