@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,150 @@
|
|
|
1
|
+
# StatTile
|
|
2
|
+
|
|
3
|
+
Import: `import { StatTile } from "@uxelle/components";`
|
|
4
|
+
|
|
5
|
+
Card-styled readout for a headline figure with a label, optional qualifier, info control, and trailing region.
|
|
6
|
+
|
|
7
|
+
## Props
|
|
8
|
+
|
|
9
|
+
<!-- prettier-ignore-start -->
|
|
10
|
+
|
|
11
|
+
| Prop | Type | Default | Description |
|
|
12
|
+
|------|------|---------|-------------|
|
|
13
|
+
| order | `"labelFirst" \| "valueFirst"` | `"labelFirst" as StatTileOrder` | Vertical order of the label row and the value row. |
|
|
14
|
+
| label | `ReactNode` | `—` | Label in the label row. Occupies remaining width beside the info control. Pass a `string` (or `number`) to render with the default label typography (`Text type="Body Medium"`), or pass a `ReactNode` for full control. When you pass a `ReactNode` you own its accessibility: use meaningful text (not color or icon alone) and keep it non-interactive so it can name the group. |
|
|
15
|
+
| value | `ReactNode` | `—` | Headline figure in the value row. Pass a `string` (or `number`) to render with the default value typography (`Text type="Display Medium"` as `<p>`), or pass a `ReactNode` for full control. A library `Text` node is rendered as `<p>` so a grid of StatTiles does not become a heading outline. When you pass any other `ReactNode`, make sure the figure reads as text and is not conveyed through color or an icon alone (WCAG 1.4.1). |
|
|
16
|
+
| qualifier | `boolean` | `true` | When true, allows the qualifier to render when content is present. |
|
|
17
|
+
| qualifierContent | `ReactNode` | `—` | Qualifier beside the value. Pass a `string` (or `number`) to render with the default qualifier typography (`Text type="Body Medium"`), or a `ReactNode` for full control. Only rendered when `qualifier` is true and content is present. When you pass a `ReactNode`, ensure it reads as text; do not rely on color alone to distinguish comparison periods. |
|
|
18
|
+
| tooltipSlot | `boolean` | `true` | When true, allows the tooltip slot to render when content is present. |
|
|
19
|
+
| tooltipContent | `ReactNode` | `—` | Info control at the end of the label row (typically `Tooltip` wrapping `IconButton`). Only rendered when `tooltipSlot` is true and content is present. Icon-only triggers within this slot must carry an accessible name via `aria-label` on the interactive element (e.g. `<IconButton aria-label="More information" />`). |
|
|
20
|
+
| trailingSlot | `boolean` | `false` | When true, allows the trailing region to render when content is present. The slot accepts any content. |
|
|
21
|
+
| trailingSlotContent | `ReactNode` | `—` | Trailing region. Open slot — any content is allowed (a delta `LabelBadge`, sparkline, action, or custom composition). Only rendered when `trailingSlot` is true and content is present. Because it is an open slot the component cannot guarantee its accessibility: this slot commonly holds a trend/delta indicator, so do not convey direction with color or an icon alone (WCAG 1.4.1) — include text (e.g. `"+25%"`) or an accessible name on the indicator, and give any interactive content a clear accessible name. |
|
|
22
|
+
| fullWidth | `boolean` | `true` | When true, the root fills the parent width. When false, the root hugs its contents. |
|
|
23
|
+
|
|
24
|
+
<!-- prettier-ignore-end -->
|
|
25
|
+
|
|
26
|
+
## Usage
|
|
27
|
+
|
|
28
|
+
```tsx
|
|
29
|
+
// Label First (default) — string shorthand renders default typography
|
|
30
|
+
<StatTile
|
|
31
|
+
label="Median first response time"
|
|
32
|
+
value="1d 12h"
|
|
33
|
+
qualifierContent="Last 7 days"
|
|
34
|
+
tooltipContent={
|
|
35
|
+
<Tooltip
|
|
36
|
+
direction="Top Right"
|
|
37
|
+
content={<Text type="Body Small">How this figure is calculated.</Text>}
|
|
38
|
+
>
|
|
39
|
+
<IconButton
|
|
40
|
+
emphasis="low"
|
|
41
|
+
size="extraSmall"
|
|
42
|
+
iconName="info"
|
|
43
|
+
aria-label="More information"
|
|
44
|
+
/>
|
|
45
|
+
</Tooltip>
|
|
46
|
+
}
|
|
47
|
+
/>
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
```tsx
|
|
51
|
+
// Value First — named automatically from the label
|
|
52
|
+
<StatTile
|
|
53
|
+
order="valueFirst"
|
|
54
|
+
trailingSlot
|
|
55
|
+
trailingSlotContent={
|
|
56
|
+
// Direction is carried by text ("25%"), not color/icon alone.
|
|
57
|
+
<LabelBadge label="25%" leadingIcon leadingIconName="arrow_upward_alt" data-color-switcher="success" />
|
|
58
|
+
}
|
|
59
|
+
label="Median first response time"
|
|
60
|
+
value="1d 12h"
|
|
61
|
+
/>
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
```tsx
|
|
65
|
+
// Value First without a label — supply an explicit accessible name
|
|
66
|
+
<StatTile order="valueFirst" aria-label="Uptime, 99.9 percent" value="99.9%" />
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
```tsx
|
|
70
|
+
// ReactNode override — caller owns accessibility (semantic element, text)
|
|
71
|
+
<StatTile
|
|
72
|
+
label={<Text type="Body Medium" width={false}>Fill rate</Text>}
|
|
73
|
+
value={<Text type="Display Medium" as="p">94%</Text>}
|
|
74
|
+
/>
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
```tsx
|
|
78
|
+
// Polling dashboard — aria-live on the value announces only the change
|
|
79
|
+
<StatTile
|
|
80
|
+
label="Active sessions"
|
|
81
|
+
value={<Text type="Display Medium" as="p" aria-live="polite">{count}</Text>}
|
|
82
|
+
/>
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
```tsx
|
|
86
|
+
// Hug contents instead of filling the parent
|
|
87
|
+
<StatTile fullWidth={false} label="Uptime" value="99.9%" />
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
```tsx
|
|
91
|
+
// Loading — replace the tile with a Skeleton that fills the same cell
|
|
92
|
+
<SkeletonGroup loading={isLoading} aria-label="Loading median first response time">
|
|
93
|
+
{isLoading ? (
|
|
94
|
+
<Skeleton type="rectangle" size="large" />
|
|
95
|
+
) : (
|
|
96
|
+
<StatTile label="Median first response time" value="1d 12h" />
|
|
97
|
+
)}
|
|
98
|
+
</SkeletonGroup>
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
```tsx
|
|
102
|
+
// Drill-through — destination lives in the trailing slot, not on the root
|
|
103
|
+
<StatTile
|
|
104
|
+
label="Monthly revenue"
|
|
105
|
+
value="$1.24M"
|
|
106
|
+
trailingSlot
|
|
107
|
+
trailingSlotContent={<Link href="/reports/revenue">View report</Link>}
|
|
108
|
+
/>
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
## Notes
|
|
113
|
+
|
|
114
|
+
- Pass a `string` (or `number`) to `label`, `value`, and `qualifierContent` to
|
|
115
|
+
- render with each region's default typography — no `Text` wrapper needed. Pass
|
|
116
|
+
- a `ReactNode` instead when you need full control (e.g. truncation, a custom
|
|
117
|
+
- element); in that case you own its accessibility (see each prop). A library
|
|
118
|
+
- `Text` passed as `value` is rendered as `<p>`.
|
|
119
|
+
- Pass a library `Tooltip` (typically wrapping `IconButton`) through
|
|
120
|
+
- `tooltipContent`. Toggle regions with `qualifier`, `tooltipSlot`, and
|
|
121
|
+
- `trailingSlot`; empty content is not rendered. `trailingSlotContent` is an
|
|
122
|
+
- open slot — any content is allowed. Surface, elevation, radius, and
|
|
123
|
+
- padding come from `Card`. StatTile is a standalone tile: place it next to
|
|
124
|
+
- other StatTiles in a grid. Do not wrap it in `Card`. A figure inside a chart
|
|
125
|
+
- card or table cell is a different surface — compose `Text` there.
|
|
126
|
+
- **Accessibility.** The root is a `role="group"` region. It is named
|
|
127
|
+
- automatically from `label` (via `aria-labelledby`), so the group is announced
|
|
128
|
+
- with context in both orders — including `"valueFirst"`, where the value
|
|
129
|
+
- precedes the label in the DOM. Supply your own `aria-label` or
|
|
130
|
+
- `aria-labelledby` to override the derived name; this is **required** when
|
|
131
|
+
- there is no `label` (e.g. `aria-label="Median first response time, 1 day 12
|
|
132
|
+
- hours"`) so the figure is not announced without context. In development,
|
|
133
|
+
- StatTile warns when content is present but the group has no name. The trailing
|
|
134
|
+
- slot is open, so its accessibility is the caller's responsibility — see
|
|
135
|
+
- `trailingSlotContent`.
|
|
136
|
+
- **Navigation.** Do not put `onClick` on StatTile — the root is a named group,
|
|
137
|
+
- not a control, and a wrapped link would nest interactives when a tooltip is
|
|
138
|
+
- present. Put the destination in `trailingSlotContent` as a `Link` or
|
|
139
|
+
- `IconButton`.
|
|
140
|
+
- Set `order` to `"valueFirst"` to put the value row above the label row.
|
|
141
|
+
- The root fills its parent by default (`fullWidth`). Set `fullWidth={false}`
|
|
142
|
+
- to hug contents. Long label, value, and qualifier copy wrap inside the tile.
|
|
143
|
+
- While data is loading, replace the tile with a `Skeleton` sized to the card
|
|
144
|
+
- (full-width rectangle) inside `SkeletonGroup`. Do not pass a skeleton as
|
|
145
|
+
- `value`.
|
|
146
|
+
- For polling dashboards where the value updates in place, prefer putting
|
|
147
|
+
- `aria-live="polite"` on the **value** node so only the changing figure is
|
|
148
|
+
- announced (a root-level `aria-live` also works but re-announces the whole
|
|
149
|
+
- group). When using a `string` `value`, wrap it yourself to place the live
|
|
150
|
+
- region precisely.
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Stepper
|
|
2
|
+
|
|
3
|
+
Import: `import { Stepper, StepperItem, STEPPER_AMOUNT_MIN } from "@uxelle/components";`
|
|
4
|
+
|
|
5
|
+
Ordered step list for multi-step flows. Use **`steps`** with **`currentStep`**, or compose **`StepperItem`** children (children take precedence). **`amount`** generates placeholder ordinal copy for demos only.
|
|
6
|
+
|
|
7
|
+
## Props — Stepper
|
|
8
|
+
|
|
9
|
+
<!-- prettier-ignore-start -->
|
|
10
|
+
|
|
11
|
+
| Prop | Type | Default | Description |
|
|
12
|
+
|------|------|---------|-------------|
|
|
13
|
+
| children | `ReactNode` | `—` | Composed `StepperItem` children, which take precedence over `steps` and `amount`. Each item owns its own `status`, so `currentStep` is not applied to children. |
|
|
14
|
+
| horizontal | `boolean` | `true` | When true, steps run in a row; when false, they stack against a vertical rail with the labels beside it. |
|
|
15
|
+
| amount | `number` | `3` | Number of placeholder steps generated when neither `children` nor `steps` is set; the value is floored and clamped to at least `STEPPER_AMOUNT_MIN` (2). The generated ordinal copy is for demos — pass `steps` or `StepperItem` children in product UI. |
|
|
16
|
+
| currentStep | `number` | `1` | 1-based index of the active step, which drives complete / current / upcoming status for `steps` and `amount`. Floored and clamped into range, and ignored once `StepperItem` children are supplied. |
|
|
17
|
+
| showLabel | `boolean` | `true` | When false, only the indicator row renders — no titles or descriptions. Inherited by every `StepperItem` through context and overridable per item. |
|
|
18
|
+
| interactive | `boolean` | `true` | When true, each indicator is a focusable `button` (or `a` when the step sets `href`) with hover and focus styling; when false, indicators are static progress markers outside the tab order. |
|
|
19
|
+
| onStepClick | `(stepIndex: number) => void` | `—` | Called with the activated step's 1-based index when an interactive indicator is clicked or keyboard-activated. Skipped for disabled steps and when a per-item `onClick` calls `preventDefault()`. |
|
|
20
|
+
| required | `boolean` | `false` | Default asterisk state for steps that do not set their own `required`, including composed `StepperItem` children. A per-step `required` always wins. |
|
|
21
|
+
| steps | `StepperStepDefinition[]` | `—` | Declarative step definitions (**StepperStepDefinition**) — label, description, and optional `required` / `disabled` / `href`. Its length sets the step count and `currentStep` drives status; ignored when `children` is provided. |
|
|
22
|
+
| statusLabels | `Partial<StepperStatusLabels>` | `—` | Localized fragments (**StepperStatusLabels**) appended to each step's accessible name for the completed, current, and required states. Merged over the English defaults, so partial objects are fine. |
|
|
23
|
+
| className | `string` | `""` | Extra class names appended to the root `<ol>` after the built-in `uxl-stepper` layout classes. |
|
|
24
|
+
|
|
25
|
+
<!-- prettier-ignore-end -->
|
|
26
|
+
|
|
27
|
+
Extends `OlHTMLAttributes` — supports standard HTML attributes.
|
|
28
|
+
|
|
29
|
+
## Props — StepperItem
|
|
30
|
+
|
|
31
|
+
<!-- prettier-ignore-start -->
|
|
32
|
+
|
|
33
|
+
| Prop | Type | Default | Description |
|
|
34
|
+
|------|------|---------|-------------|
|
|
35
|
+
| label (required) | `string` | `"Step"` | Step title, rendered as Body Medium Alt while labels are visible and always part of the indicator's accessible name. |
|
|
36
|
+
| description | `string` | `""` | Supporting line under the title in Condensed type, also appended to the step's accessible name; omit it or pass an empty string to hide it. |
|
|
37
|
+
| status | `"complete" \| "current" \| "upcoming"` | `"upcoming"` | Indicator state: `complete` shows a filled check and a high-emphasis connector, `current` shows a filled dot and sets `aria-current`, `upcoming` shows `stepIndex`. |
|
|
38
|
+
| stepIndex | `number` | `1` | 1-based position: displayed inside an `upcoming` indicator and passed to the parent `Stepper` `onStepClick`. Derived from child order inside a `Stepper`, so set it only for standalone items. |
|
|
39
|
+
| lastStep | `boolean` | `false` | When true, the connector after the indicator is omitted. Derived from child order inside a `Stepper`. |
|
|
40
|
+
| layout | `StepperLayout` | `—` | Orientation for this step only, overriding the row / rail layout inherited from the parent `Stepper`. |
|
|
41
|
+
| showLabel | `boolean` | `true` | Shows or hides this step's title and description, overriding the parent `Stepper` `showLabel`. |
|
|
42
|
+
| required | `boolean` | `false` | When true, an asterisk precedes the title and “required” joins the accessible name; overrides the parent `Stepper` `required` default. |
|
|
43
|
+
| disabled | `boolean` | `false` | Blocks activation of this step's indicator while the parent `Stepper` is interactive, so `onStepClick` does not fire; a link step also drops its `href` and tab stop. |
|
|
44
|
+
| href | `string` | `""` | Renders the interactive indicator as an `a` pointing here instead of a `button`. Applies only while the parent `Stepper` is interactive; an empty string keeps the button. |
|
|
45
|
+
| onClick | `MouseEventHandler<HTMLButtonElement \| HTMLAnchorElement>` | `—` | Fired when the interactive indicator is activated, before the parent `Stepper` `onStepClick`. Call `preventDefault()` to stop `onStepClick` from running. |
|
|
46
|
+
| anchorProps | `Omit< AnchorHTMLAttributes<HTMLAnchorElement>, "href" \| "onClick" \| "children" >` | `—` | Extra anchor attributes (e.g. `target`, `rel`) merged onto the link rendered for `href`; ignored on button steps. |
|
|
47
|
+
| className | `string` | `""` | Extra class names appended to the `<li>` after the built-in `uxl-stepper-item` status classes. |
|
|
48
|
+
|
|
49
|
+
<!-- prettier-ignore-end -->
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
## Usage
|
|
53
|
+
|
|
54
|
+
```tsx
|
|
55
|
+
<Stepper
|
|
56
|
+
steps={[
|
|
57
|
+
{ label: "Shipping", description: "Address" },
|
|
58
|
+
{ label: "Payment", description: "Card" },
|
|
59
|
+
{ label: "Review", description: "Confirm" },
|
|
60
|
+
]}
|
|
61
|
+
currentStep={2}
|
|
62
|
+
aria-label="Checkout"
|
|
63
|
+
onStepClick={(step) => setStep(step)}
|
|
64
|
+
/>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
## Notes
|
|
69
|
+
|
|
70
|
+
- Renders as an ordered list (`<ol>`). Prefer wrapping in `<nav aria-label="…">` when the stepper is a page landmark. Pass **`aria-label`** / **`aria-labelledby`** on the root when it is not inside a labeled `nav`.
|
|
71
|
+
- **Interactive** (default): each indicator is a native `<button>` (or `<a>` when **`href`** is set) in the tab order with a `:focus-visible` ring. Use **`onStepClick`** (1-based) or per-item **`onClick`**. Set **`interactive={false}`** for progress-only UI.
|
|
72
|
+
- **`statusLabels`**: localize “completed” / “current” / “required” in accessible names.
|
|
73
|
+
- **`stepIndex`** / **`lastStep`** are derived from child order when omitted.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Switch
|
|
2
|
+
|
|
3
|
+
Import: `import { Switch } from "@uxelle/components";`
|
|
4
|
+
|
|
5
|
+
Toggle switch using design tokens. Renders a native checkbox with `role="switch"` for forms and assistive technologies.
|
|
6
|
+
|
|
7
|
+
## Props
|
|
8
|
+
|
|
9
|
+
<!-- prettier-ignore-start -->
|
|
10
|
+
|
|
11
|
+
| Prop | Type | Default | Description |
|
|
12
|
+
|------|------|---------|-------------|
|
|
13
|
+
| checked | `boolean` | `—` | On/off state when controlled. Pass it with `onChange` to drive the switch from app state; omit it and the switch tracks its own state from `defaultChecked`. |
|
|
14
|
+
| defaultChecked | `boolean` | `false` | Starting on/off state when `checked` is omitted. Unusually for a `default*` prop, changing it later re-syncs the uncontrolled state; it is ignored entirely while `checked` is set. |
|
|
15
|
+
| disabled | `boolean` | `false` | Blocks toggling and applies disabled styling. The underlying input is disabled, so its value is left out of form submission. |
|
|
16
|
+
| embedded | `boolean` | `false` | Turns the switch into a display-only indicator: `pointer-events: none`, `tabIndex={-1}`, and no hover/active/focus ring, so an enclosing row or cell owns pointer and focus. That parent must then supply the accessible name. |
|
|
17
|
+
| label | `boolean` | `true` | Renders `children` beside the track as the switch's visible name. With `false` there is no visible name, so set `aria-label` or `aria-labelledby` on the component or the switch is left unnamed. |
|
|
18
|
+
| onChange | `ChangeEventHandler<HTMLInputElement>` | `—` | Fires on every toggle with the native change event; read the new state from `event.target.checked`. Required in controlled mode to keep `checked` in sync. |
|
|
19
|
+
| name | `string` | `—` | Form field name on the underlying checkbox input. Without it the switch submits no value. |
|
|
20
|
+
| value | `string` | `—` | Value submitted alongside `name` while the switch is on. Browsers fall back to `on` when omitted. |
|
|
21
|
+
| form | `string` | `—` | `id` of the form this switch belongs to, for when it renders outside that form's markup. |
|
|
22
|
+
| required | `boolean` | `—` | Marks the underlying input required, so native validation blocks submission while the switch is off. |
|
|
23
|
+
| children | `ReactNode` | `—` | Label content to the right of the track, usually a `Text` node; the wrapping `label` element makes it the switch's accessible name. Not rendered when `label` is `false` — set `aria-label` instead. |
|
|
24
|
+
|
|
25
|
+
<!-- prettier-ignore-end -->
|
|
26
|
+
|
|
27
|
+
## Usage
|
|
28
|
+
|
|
29
|
+
```tsx
|
|
30
|
+
<Switch defaultChecked>
|
|
31
|
+
<Text type="Body Medium" width>Notifications</Text>
|
|
32
|
+
</Switch>
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
## Notes
|
|
37
|
+
|
|
38
|
+
- Optional `children` render as label text to the right of the track.
|
|
39
|
+
- **Accessibility**: Provide a visible label (`children`) or an accessible name with `aria-label` / `aria-labelledby`. Control ARIA (`aria-invalid`, `aria-errormessage`, `aria-describedby`, `aria-required`) is applied to the `role="switch"` input, not the wrapping label. Keyboard: Space toggles when the input is focused. **`embedded`** uses **`tabIndex={-1}`**.
|
|
40
|
+
- **Embedded**: When `embedded` is set, the switch is removed from the tab order (`tabIndex={-1}`), does not draw its own focus ring, and uses **`pointer-events: none`** so the parent row or cell owns pointer and focus; that parent should expose a single clear `:focus-visible` indicator.
|
|
41
|
+
- **Controlled**: Pass `checked` and `onChange`; omit `defaultChecked`.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Tab
|
|
2
|
+
|
|
3
|
+
Import: `import { Tab } from "@uxelle/components";`
|
|
4
|
+
|
|
5
|
+
Selectable control that switches between panels in a tabbed interface.
|
|
6
|
+
|
|
7
|
+
## Props
|
|
8
|
+
|
|
9
|
+
<!-- prettier-ignore-start -->
|
|
10
|
+
|
|
11
|
+
| Prop | Type | Default | Description |
|
|
12
|
+
|------|------|---------|-------------|
|
|
13
|
+
| label (required) | `string` | `"Tab 1"` | Visible text naming the panel this tab switches to. Rendered with `Text`, so keep it to a couple of words; add `aria-label` when the panel needs a fuller name. |
|
|
14
|
+
| activated | `boolean` | `false` | Marks this tab as the selected one: sets `aria-selected`, applies the activated styling, and makes it the single tab-order stop. `TabsCompact` / `TabsStretch` set it from their own selection state, so pass it only on a standalone `Tab`. |
|
|
15
|
+
| fullWidth | `boolean` | `false` | Switches to full-width spacing tokens and lets the tab grow to fill its share of the row. `TabsStretch` sets it for every tab. |
|
|
16
|
+
| leadingSlot | `boolean` | `false` | Reveals the region before the label. With no `leading` content, the `leadingIconName` icon fills it. |
|
|
17
|
+
| trailingSlot | `boolean` | `false` | Reveals the region after the label. With no `trailing` content, the `trailingIconName` icon fills it. |
|
|
18
|
+
| leading | `ReactNode` | `—` | Custom content for the region before the label, replacing the default icon. Rendered only when `leadingSlot` is true. |
|
|
19
|
+
| trailing | `ReactNode` | `—` | Custom content for the region after the label, replacing the default icon. Rendered only when `trailingSlot` is true. |
|
|
20
|
+
| leadingIconName | `string` | `"add"` | Material Symbol drawn in the leading region when `leadingSlot` is on and no `leading` content is supplied. |
|
|
21
|
+
| trailingIconName | `string` | `"add"` | Material Symbol drawn in the trailing region when `trailingSlot` is on and no `trailing` content is supplied. |
|
|
22
|
+
| leadingIconVariant | `"sharpUnfilled" \| "sharpFilled"` | `"sharpUnfilled"` | Fill style for the default leading icon: `sharpFilled` solid, `sharpUnfilled` outlined. |
|
|
23
|
+
| trailingIconVariant | `"sharpUnfilled" \| "sharpFilled"` | `"sharpUnfilled"` | Fill style for the default trailing icon: `sharpFilled` solid, `sharpUnfilled` outlined. |
|
|
24
|
+
| textWidth | `boolean` | `true` | When true the label shrinks to its text; when false it fills the tab so the text centres. `TabsStretch` sets it to false for its equal-width tabs. |
|
|
25
|
+
| "aria-label" | `string` | `""` | Accessible name that replaces `label` for assistive technology; use it when the visible label is too terse to identify the panel. Blank strings are dropped. |
|
|
26
|
+
| "aria-controls" | `string` | `""` | Id of the `tabpanel` this tab controls. Set it only when you render panels yourself — `TabsCompact` / `TabsStretch` wire their own `panel` / `panels` content automatically. |
|
|
27
|
+
|
|
28
|
+
<!-- prettier-ignore-end -->
|
|
29
|
+
|
|
30
|
+
## Usage
|
|
31
|
+
|
|
32
|
+
```tsx
|
|
33
|
+
<Tab label="…">
|
|
34
|
+
{/* Content */}
|
|
35
|
+
</Tab>
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Notes
|
|
39
|
+
|
|
40
|
+
- The visible label uses `Text` `type="Body Medium"` when inactive and `type="Body Medium Alt"` when activated.
|
|
41
|
+
- **Slots**: `leading` / `trailing` accept any composable content; `leadingSlot` / `trailingSlot` show the side regions. When a slot is on and no custom content is passed, the default Material Symbol icon (`add`) is shown — same pattern as ListItem and Textfield.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Table
|
|
2
|
+
|
|
3
|
+
Import: `import { Table } from "@uxelle/components";`
|
|
4
|
+
|
|
5
|
+
Composable table shell and semantic grid for tabular data. Columns support resize, reorder, and sort; horizontal scroll activates when widths exceed the container.
|
|
6
|
+
|
|
7
|
+
## Props
|
|
8
|
+
|
|
9
|
+
<!-- prettier-ignore-start -->
|
|
10
|
+
|
|
11
|
+
| Prop | Type | Default | Description |
|
|
12
|
+
|------|------|---------|-------------|
|
|
13
|
+
| topSlot | `ReactNode` | `—` | Band rendered above the table and outside its horizontal scrollport — typically a title, toolbar, filters, or search. Omitted entirely when unset. |
|
|
14
|
+
| bottomSlot | `ReactNode` | `—` | Band rendered below the table and outside its horizontal scrollport — typically pagination or bulk actions. Omitted entirely when unset. |
|
|
15
|
+
| tableSlot | `ReactNode` | `—` | The table itself: one **TableGrid** with its head, body, rows, and cells, placed in the scroll region that drives the horizontal scroll hint. Same slot as `children`, and it wins when both are passed. |
|
|
16
|
+
| children | `ReactNode` | `—` | Alias for `tableSlot`, ignored when `tableSlot` is also passed. Compose one `TableGrid` (give it `aria-label` and `gridTemplateColumns`) holding a `TableHead` with a `TableRow` of `TableHeaderCell` children, then a `TableBody` with one `TableRow` of `TableCell` children per record — all exported from `@uxelle/components`. |
|
|
17
|
+
|
|
18
|
+
<!-- prettier-ignore-end -->
|
|
19
|
+
|
|
20
|
+
Extends `HTMLAttributes` — supports standard HTML attributes.
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
```tsx
|
|
25
|
+
const columns = [
|
|
26
|
+
{ flex: 0.5, minWidth: 48 },
|
|
27
|
+
{ flex: 1, minWidth: 120 },
|
|
28
|
+
] as const;
|
|
29
|
+
|
|
30
|
+
function OrdersGrid() {
|
|
31
|
+
const columnOrder = useColumnOrder() ?? [0, 1];
|
|
32
|
+
const gridTemplateColumns =
|
|
33
|
+
useColumnGridTemplate(columnOrder) ?? tableColumnTracksTemplate(columns);
|
|
34
|
+
return (
|
|
35
|
+
<TableGrid aria-label="Orders" gridTemplateColumns={gridTemplateColumns}>
|
|
36
|
+
…
|
|
37
|
+
</TableGrid>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
<Table
|
|
42
|
+
tableSlot={
|
|
43
|
+
<TableColumnSizingProvider columns={columns}>
|
|
44
|
+
<OrdersGrid />
|
|
45
|
+
</TableColumnSizingProvider>
|
|
46
|
+
}
|
|
47
|
+
/>
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
## Notes
|
|
52
|
+
|
|
53
|
+
- Optional top and bottom slots frame the table region; a horizontal scroll hint appears when the grid is wider than its container. Fixed columns are implemented as sticky cells inside one `TableGrid`—not as a separate grid—so each row stays a single unit for assistive technology. Use **TableColumnPinProvider** or `pinned="start"` on **TableHeaderCell** and **TableCell** with **usePinnedStartOffsets** to keep leading columns visible during horizontal scroll.
|
|
54
|
+
- Horizontal scroll and the trailing-edge hint appear automatically when content overflows the container. Define column proportions with **TableColumnSizingProvider** and **useColumnGridTemplate**.
|
|
55
|
+
- Layout is left-to-right only; right-to-left document direction is not supported yet.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# TabsCompact
|
|
2
|
+
|
|
3
|
+
Import: `import { TabsCompact, Tab } from "@uxelle/components";`
|
|
4
|
+
|
|
5
|
+
Tabs sized to their content width, with optional scroll controls when they overflow.
|
|
6
|
+
|
|
7
|
+
## Props
|
|
8
|
+
|
|
9
|
+
<!-- prettier-ignore-start -->
|
|
10
|
+
|
|
11
|
+
| Prop | Type | Default | Description |
|
|
12
|
+
|------|------|---------|-------------|
|
|
13
|
+
| children | `ReactNode` | `—` | `Tab` elements in display order — the normal way to build a tab list. Selection, ids, and `aria-controls` are injected into each one. Omit them only to fall back to the placeholder tabs generated from `amount`. |
|
|
14
|
+
| amount | `number` | `7` | Tab count, clamped to 2–8. Without `children` it generates that many placeholder tabs labelled from `tabLabels` (or `Tab 1`…`Tab N`) for previews; with real `Tab` children — preferred in product code — it only caps how many of them render. |
|
|
15
|
+
| tabLabels | `string[]` | `—` | Labels for the tabs generated by `amount`, indexed in order; missing entries fall back to `Tab 1`, `Tab 2`, … Ignored once `children` are supplied. |
|
|
16
|
+
| panel | `ReactNode` | `—` | One body rendered below the tab list and reused for every tab. Passing this (or `panels`) is what creates the `tabpanel` and its `aria-controls` wiring. |
|
|
17
|
+
| panels | `ReactNode[]` | `—` | Body content per tab, indexed in tab order. Takes precedence over `panel`; inactive panels stay mounted with `hidden`. |
|
|
18
|
+
| activeTab | `number` | `undefined` | Selected tab as a 1-based index, clamped to the tab count. Pass it with `onChange` to control selection; omit it to let the group track its own from `defaultActiveTab`. |
|
|
19
|
+
| defaultActiveTab | `number` | `1` | Selected tab as a 1-based index on first render only; later changes are ignored. Use it for uncontrolled selection — pair `activeTab` with `onChange` to drive it. |
|
|
20
|
+
| onChange | `(activeTab: number) => void` | `—` | Called with the newly selected tab's 1-based index on click or arrow-key navigation. Required to react to selection when `activeTab` is controlled. |
|
|
21
|
+
| scrollLeft | `boolean` | `false` | Shows a chevron control before the tablist that scrolls it left by roughly three-quarters of its visible width. `compact` layouts only. |
|
|
22
|
+
| scrollRight | `boolean` | `false` | Shows a chevron control after the tablist that scrolls it right by roughly three-quarters of its visible width. `compact` layouts only. |
|
|
23
|
+
| "aria-label" | `string` | `—` | Accessible name for the tablist. Set this or `aria-labelledby` — development builds warn when neither is present, and an unnamed tablist is ambiguous once a page has more than one. |
|
|
24
|
+
|
|
25
|
+
<!-- prettier-ignore-end -->
|
|
26
|
+
|
|
27
|
+
## Usage
|
|
28
|
+
|
|
29
|
+
```tsx
|
|
30
|
+
<TabsCompact>
|
|
31
|
+
{/* Content */}
|
|
32
|
+
</TabsCompact>
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Notes
|
|
36
|
+
|
|
37
|
+
- Renders `TabGroup` with `variant="compact"`. Scroll controls appear when tabs exceed the container width. Use **TabsStretch** when tabs should share width equally. Tab and panel ids are unique per instance. Pass `aria-label` or `aria-labelledby` so the tablist has an accessible name, especially when more than one tablist is on the page.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# TabsStretch
|
|
2
|
+
|
|
3
|
+
Import: `import { TabsStretch, Tab } from "@uxelle/components";`
|
|
4
|
+
|
|
5
|
+
Equal-width tabs that stretch to fill the available space; no scroll controls.
|
|
6
|
+
|
|
7
|
+
## Props
|
|
8
|
+
|
|
9
|
+
<!-- prettier-ignore-start -->
|
|
10
|
+
|
|
11
|
+
| Prop | Type | Default | Description |
|
|
12
|
+
|------|------|---------|-------------|
|
|
13
|
+
| children | `ReactNode` | `—` | `Tab` elements in display order — the normal way to build a tab list. Selection, ids, and `aria-controls` are injected into each one. Omit them only to fall back to the placeholder tabs generated from `amount`. |
|
|
14
|
+
| amount | `number` | `7` | Tab count, clamped to 2–8. Without `children` it generates that many placeholder tabs labelled from `tabLabels` (or `Tab 1`…`Tab N`) for previews; with real `Tab` children — preferred in product code — it only caps how many of them render. |
|
|
15
|
+
| tabLabels | `string[]` | `—` | Labels for the tabs generated by `amount`, indexed in order; missing entries fall back to `Tab 1`, `Tab 2`, … Ignored once `children` are supplied. |
|
|
16
|
+
| panel | `ReactNode` | `—` | One body rendered below the tab list and reused for every tab. Passing this (or `panels`) is what creates the `tabpanel` and its `aria-controls` wiring. |
|
|
17
|
+
| panels | `ReactNode[]` | `—` | Body content per tab, indexed in tab order. Takes precedence over `panel`; inactive panels stay mounted with `hidden`. |
|
|
18
|
+
| activeTab | `number` | `undefined` | Selected tab as a 1-based index, clamped to the tab count. Pass it with `onChange` to control selection; omit it to let the group track its own from `defaultActiveTab`. |
|
|
19
|
+
| defaultActiveTab | `number` | `1` | Selected tab as a 1-based index on first render only; later changes are ignored. Use it for uncontrolled selection — pair `activeTab` with `onChange` to drive it. |
|
|
20
|
+
| onChange | `(activeTab: number) => void` | `—` | Called with the newly selected tab's 1-based index on click or arrow-key navigation. Required to react to selection when `activeTab` is controlled. |
|
|
21
|
+
| "aria-label" | `string` | `—` | Accessible name for the tablist. Set this or `aria-labelledby` — development builds warn when neither is present, and an unnamed tablist is ambiguous once a page has more than one. |
|
|
22
|
+
|
|
23
|
+
<!-- prettier-ignore-end -->
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
|
|
27
|
+
```tsx
|
|
28
|
+
<TabsStretch>
|
|
29
|
+
{/* Content */}
|
|
30
|
+
</TabsStretch>
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Notes
|
|
34
|
+
|
|
35
|
+
- Renders `TabGroup` with `variant="stretch"` so each tab shares width equally. Use **TabsCompact** when tabs should size to their content. Tab and panel ids are unique per instance. Pass `aria-label` or `aria-labelledby` so the tablist has an accessible name, especially when more than one tablist is on the page.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Text
|
|
2
|
+
|
|
3
|
+
Import: `import { Text } from "@uxelle/components";`
|
|
4
|
+
|
|
5
|
+
Typographic primitive for rendering text with variant styles and responsive sizing.
|
|
6
|
+
|
|
7
|
+
## Props
|
|
8
|
+
|
|
9
|
+
<!-- prettier-ignore-start -->
|
|
10
|
+
|
|
11
|
+
| Prop | Type | Default | Description |
|
|
12
|
+
|------|------|---------|-------------|
|
|
13
|
+
| type (required) | `TextType` | `"Body Medium"` | Type ramp entry applied to the text: it sets the font family, size, weight, line height, and letter spacing, and picks the default element when `as` is omitted. The full set of values is exported as **TEXT_TYPES** — Display, Component, Body, Condensed, Overline, Button, Link, and Navigation Link entries. |
|
|
14
|
+
| width | `boolean` | `true` | When true the text box hugs its content; when false it spans the full width of its container, which is what lets long copy wrap or `truncation` clip to a stable width. |
|
|
15
|
+
| children (required) | `ReactNode` | `—` | Text to render inside the styled element — a string, or inline nodes such as a `Link` or emphasis markup. Omit it when `as` is `\"input\"`, which renders no children. |
|
|
16
|
+
| as | `TextAsTag \| "input"` | `—` | Element tag to render. Omit it for a semantic default derived from `type` — `h1`, `h2`, `h3` for Display Large, Medium, and Small, `p` for Body and Component types, and `span` for everything else. Pass `"input"` to switch to input mode, which renders a native input styled with the same type ramp. |
|
|
17
|
+
| role | `string` | `—` | ARIA role placed on the rendered text element itself, not the wrapper. Set it only to override the implicit role of the tag `as` produces, for example `heading` on a `span`. |
|
|
18
|
+
| "aria-label" | `string` | `—` | Accessible name for the rendered text element, for when the visible characters are not meaningful on their own. `Overline` and `Overline Alt` fall back to their string content as a name when neither this nor `aria-hidden` is set. |
|
|
19
|
+
| "aria-describedby" | `string` | `—` | Space-separated ids of elements that describe this text; applied to the rendered text element rather than the wrapper. |
|
|
20
|
+
| "aria-hidden" | `boolean` | `—` | When true, removes the text from the accessibility tree. Use it for decorative copy, or copy a labelled parent already announces, so it is not read twice. |
|
|
21
|
+
| truncation | `boolean` | `false` | Clips overflow to a single line ending in an ellipsis instead of wrapping. Needs a bounded width, so pair it with `width={false}` or a sized parent. |
|
|
22
|
+
| uppercase | `boolean` | `—` | Forces `text-transform`: true uppercases the text, false cancels an inherited uppercase style. Omit it to leave the transform to the type ramp (Button applies the theme's uppercase prop-control to its own label only). |
|
|
23
|
+
| inline | `boolean` | `false` | Renders the wrapper inline instead of as its own block, so the text can sit inside a sentence or in another component's row (for example `FieldMessage`). |
|
|
24
|
+
| className | `string` | `—` | Classes merged onto the outer wrapper element; the inner text element keeps its own type ramp classes. |
|
|
25
|
+
| inputClassName | `string` | `—` | Classes merged onto the `input` element alongside its type ramp classes. Applies only in input mode. |
|
|
26
|
+
| inputHtmlType | `HTMLInputElement["type"]` | `"text"` | Value for the native `type` attribute on the input, such as `email` or `password`. `type` stays the type ramp entry. |
|
|
27
|
+
|
|
28
|
+
<!-- prettier-ignore-end -->
|
|
29
|
+
|
|
30
|
+
## TextType values
|
|
31
|
+
|
|
32
|
+
- **Display**: `"Display Large"`, `"Display Medium"`, `"Display Small"`, `"Display Extra Small"`
|
|
33
|
+
- **Component**: `"Component Medium"`, `"Component Medium Alt"`
|
|
34
|
+
- **Body**: `"Body Medium"`, `"Body Medium Alt"`, `"Body Small"`, `"Body Small Alt"`
|
|
35
|
+
- **Condensed**: `"Condensed"`, `"Condensed Alt"`
|
|
36
|
+
- **Overline**: `"Overline"`, `"Overline Alt"`
|
|
37
|
+
- **Button**: `"Button"`
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
## Usage
|
|
41
|
+
|
|
42
|
+
```tsx
|
|
43
|
+
// Display heading
|
|
44
|
+
<Text type="Display Large">Main Heading</Text>
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
```tsx
|
|
48
|
+
// Body text with full width
|
|
49
|
+
<Text type="Body Medium" width={false}>This is body text that wraps to full width</Text>
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
```tsx
|
|
53
|
+
// Native input with Body Small (e.g. Textfield — long values scroll in the field wrapper, not ellipsis)
|
|
54
|
+
<Text type="Body Small" as="input" width inputHtmlType="email" />
|
|
55
|
+
```
|
|
56
|
+
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Textarea
|
|
2
|
+
|
|
3
|
+
Import: `import { Textarea } from "@uxelle/components";`
|
|
4
|
+
|
|
5
|
+
Multi-line text input with a label, optional helper copy, validation message, character counter, and CSS-driven height.
|
|
6
|
+
|
|
7
|
+
## Props
|
|
8
|
+
|
|
9
|
+
<!-- prettier-ignore-start -->
|
|
10
|
+
|
|
11
|
+
| Prop | Type | Default | Description |
|
|
12
|
+
|------|------|---------|-------------|
|
|
13
|
+
| label | `boolean` | `true` | Show label row (Label + Text) |
|
|
14
|
+
| labelText | `string` | `"Label"` | Label text (Body Medium Alt); ignored if labelSlot is set |
|
|
15
|
+
| labelSlot | `ReactNode` | `—` | Composable label: replaces the default `Text` inside `Label` while keeping `htmlFor` / `required` / `fieldDescription` on `Label`. Pass `id` on `Textarea` when you need a stable association. |
|
|
16
|
+
| fieldDescription | `string` | `""` | Helper text below the label; associated with the textarea via aria-describedby |
|
|
17
|
+
| required | `boolean` | `false` | Required field (asterisk on Label, native required on the textarea) |
|
|
18
|
+
| fieldMessage | `boolean` | `false` | Show field message row (also shown automatically when validation is success or error, or when soft overflow is over the limit and softOverflowMessage is set) |
|
|
19
|
+
| fieldMessageContent | `string` | `""` | Message when fieldMessage is true (optional; else defaults by validation) |
|
|
20
|
+
| validation | `"none" \| "success" \| "error"` | `"none" as TextareaValidation` | Field message and validation state |
|
|
21
|
+
| height | `string` | `""` | Explicit field height (px number or CSS length). Overrides rows when set |
|
|
22
|
+
| maxHeight | `string` | `""` | Maximum height while resizing or auto-growing (px number or CSS length) |
|
|
23
|
+
| resize | `"vertical" \| "none"` | `"vertical" as TextareaResize` | Height-only resize; width never changes. Forced to none when disabled, read-only, or autoGrow |
|
|
24
|
+
| counter | `boolean` | `false` | Show character count (requires maxLength) |
|
|
25
|
+
| autoGrow | `boolean` | `false` | Grow height with content; rows/height are the minimum, maxHeight caps. Forces resize none |
|
|
26
|
+
| softOverflow | `boolean` | `false` | Allow typing past maxLength; warning color-switcher on the counter when over the limit. Pass softOverflowMessage for warning FieldMessage copy |
|
|
27
|
+
| softOverflowMessage | `string` | `""` | Warning FieldMessage when softOverflow content exceeds maxLength; omit for chrome only |
|
|
28
|
+
| placeholder | `string` | `""` | Native textarea placeholder |
|
|
29
|
+
| disabled | `boolean` | `false` | Disabled state |
|
|
30
|
+
| readOnly | `boolean` | `false` | Read-only state (token background, no hover, no resize; focus uses the focus stroke) |
|
|
31
|
+
| rows | `number` | `4` | Visible line count used for min-height (overridden by height; minimum when autoGrow) |
|
|
32
|
+
|
|
33
|
+
<!-- prettier-ignore-end -->
|
|
34
|
+
|
|
35
|
+
## Usage
|
|
36
|
+
|
|
37
|
+
```tsx
|
|
38
|
+
<Textarea labelText="Comments" placeholder="Enter details" rows={4} />
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
```tsx
|
|
42
|
+
<Textarea
|
|
43
|
+
labelText="Bio"
|
|
44
|
+
fieldDescription="Shown on your public profile."
|
|
45
|
+
required
|
|
46
|
+
autoGrow
|
|
47
|
+
counter
|
|
48
|
+
softOverflow
|
|
49
|
+
softOverflowMessage="Stay under the recommended limit."
|
|
50
|
+
maxLength={100}
|
|
51
|
+
/>
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
## Notes
|
|
56
|
+
|
|
57
|
+
- Associates label, field description, field message, and character count with
|
|
58
|
+
- the native textarea for accessibility. Height comes from `rows` (line-height
|
|
59
|
+
- calc) or an explicit `height`. `autoGrow` sizes the field to its content
|
|
60
|
+
- (capped by `maxHeight`). Native attributes (`id`, `name`, `maxLength`,
|
|
61
|
+
- `aria-*`) apply to the textarea; `className` applies to the root.
|
|
62
|
+
- **Simple API** — `labelText`, `fieldDescription`, `placeholder`, `rows`,
|
|
63
|
+
- `counter` + `maxLength`, optional `autoGrow` / `softOverflow` /
|
|
64
|
+
- `softOverflowMessage`. Use `Textfield` for single-line values (email, search,
|
|
65
|
+
- password).
|
|
66
|
+
- **Label and message** — `label` and `fieldMessage` are visibility flags with
|
|
67
|
+
- `labelText` and `fieldMessageContent` (same pattern as Textfield). Default
|
|
68
|
+
- `labelText` is `"Label"` so the control has an accessible name. Set
|
|
69
|
+
- `label={false}` and pass `aria-label` (or `aria-labelledby`) to hide the row.
|
|
70
|
+
- **Composable API** — `labelSlot` replaces the default `Text` inside `Label`.
|
|
71
|
+
- **Accessibility** — Pass `id` for stable `label` association. `fieldDescription`
|
|
72
|
+
- is linked with `aria-describedby` and stays available during errors.
|
|
73
|
+
- `required` shows the Label asterisk and native textarea `required`. Pass
|
|
74
|
+
- `aria-label` when `label={false}` (placeholder is not a name). Keyboard users
|
|
75
|
+
- scroll overflowing text; resize is pointer-only. Soft overflow paints the
|
|
76
|
+
- counter and field stroke as warning; pass `softOverflowMessage` for a warning
|
|
77
|
+
- `FieldMessage`. Overflow is not `aria-invalid`.
|