@viraui/react 2026.6.5 → 2026.7.0-next.0
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/dist/catalog.json +12 -0
- package/dist/components/autocomplete/autocomplete.js +1 -1
- package/dist/components/basic-input/basic-input.d.ts +2 -1
- package/dist/components/basic-input/basic-input.js +7 -0
- package/dist/components/beam/beam-pulse-driver.d.ts +1 -0
- package/dist/components/beam/beam-pulse-driver.js +2 -2
- package/dist/components/beam/beam.css +1 -1
- package/dist/components/beam/beam.d.ts +5 -2
- package/dist/components/beam/beam.js +31 -26
- package/dist/components/dialog/dialog-sheet.js +1 -1
- package/dist/components/index.d.ts +2 -0
- package/dist/components/menu/menu-popup.js +1 -1
- package/dist/components/meter/meter.css +1 -1
- package/dist/components/meter/meter.d.ts +11 -1
- package/dist/components/meter/meter.js +2 -1
- package/dist/components/otp-field/index.d.ts +4 -0
- package/dist/components/otp-field/otp-field-input.d.ts +12 -0
- package/dist/components/otp-field/otp-field-input.js +29 -0
- package/dist/components/otp-field/otp-field-separator.d.ts +11 -0
- package/dist/components/otp-field/otp-field-separator.js +16 -0
- package/dist/components/otp-field/otp-field.css +1 -0
- package/dist/components/otp-field/otp-field.d.ts +49 -0
- package/dist/components/otp-field/otp-field.js +79 -0
- package/dist/components/otp-field/otp-field.module.js +23 -0
- package/dist/components/prose/index.d.ts +2 -0
- package/dist/components/prose/prose.css +1 -0
- package/dist/components/prose/prose.d.ts +40 -0
- package/dist/components/prose/prose.js +27 -0
- package/dist/components/prose/prose.module.js +5 -0
- package/dist/components/select/select.js +1 -1
- package/dist/components/separator/separator.d.ts +7 -0
- package/dist/components/separator/separator.js +2 -1
- package/dist/components/text/text.css +1 -1
- package/dist/components/text/text.d.ts +2 -1
- package/dist/components/text/text.js +4 -2
- package/dist/components/textfield/textfield.css +1 -1
- package/dist/components/textfield/textfield.d.ts +18 -10
- package/dist/components/textfield/textfield.js +43 -39
- package/dist/components/textfield/textfield.module.js +3 -1
- package/dist/components/title/title.css +1 -1
- package/dist/components/title/title.d.ts +2 -1
- package/dist/components/title/title.js +4 -2
- package/dist/components/toast/toast-banner.js +1 -1
- package/dist/index.js +5 -3
- package/dist/specs/components/beam/a11y.xml +3 -3
- package/dist/specs/components/beam/props.xml +22 -1
- package/dist/specs/components/beam/usage.xml +1 -0
- package/dist/specs/components/meter/patterns.xml +12 -0
- package/dist/specs/components/meter/props.xml +7 -0
- package/dist/specs/components/meter/usage.xml +2 -0
- package/dist/specs/components/otp-field/a11y.xml +10 -0
- package/dist/specs/components/otp-field/meta.xml +23 -0
- package/dist/specs/components/otp-field/patterns.xml +66 -0
- package/dist/specs/components/otp-field/props.xml +29 -0
- package/dist/specs/components/otp-field/usage.xml +13 -0
- package/dist/specs/components/prose/a11y.xml +7 -0
- package/dist/specs/components/prose/meta.xml +18 -0
- package/dist/specs/components/prose/patterns.xml +59 -0
- package/dist/specs/components/prose/props.xml +37 -0
- package/dist/specs/components/prose/usage.xml +12 -0
- package/dist/specs/components/separator/props.xml +3 -0
- package/dist/specs/components/separator/usage.xml +1 -0
- package/dist/specs/components/text/props.xml +2 -2
- package/dist/specs/components/text/usage.xml +1 -0
- package/dist/specs/components/textfield/meta.xml +1 -0
- package/dist/specs/components/textfield/props.xml +6 -1
- package/dist/specs/components/textfield/usage.xml +2 -1
- package/dist/specs/components/title/props.xml +2 -2
- package/dist/specs/components/title/usage.xml +1 -0
- package/dist/specs/index.xml +21 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -6,6 +6,7 @@ import { Autocomplete } from "./components/autocomplete/autocomplete.js";
|
|
|
6
6
|
import { Avatar } from "./components/avatar/avatar.js";
|
|
7
7
|
import { AvatarGroup } from "./components/avatar-group/avatar-group.js";
|
|
8
8
|
import { Badge } from "./components/badge/badge.js";
|
|
9
|
+
import { Surface } from "./components/surface/surface.js";
|
|
9
10
|
import { Beam } from "./components/beam/beam.js";
|
|
10
11
|
import { Bleed } from "./components/bleed/bleed.js";
|
|
11
12
|
import { BreakpointsProvider } from "./components/breakpoints/breakpoints-provider.js";
|
|
@@ -28,7 +29,6 @@ import { getElevationProps } from "./core/elevation/get-elevation-props.js";
|
|
|
28
29
|
import { Elevator } from "./components/elevator/elevator.js";
|
|
29
30
|
import { Fieldset } from "./components/fieldset/fieldset.js";
|
|
30
31
|
import { FitText } from "./components/fit-text/fit-text.js";
|
|
31
|
-
import { Surface } from "./components/surface/surface.js";
|
|
32
32
|
import { Glow } from "./components/glow/glow.js";
|
|
33
33
|
import { Grid } from "./components/grid/grid.js";
|
|
34
34
|
import { IconButton } from "./components/icon-button/icon-button.js";
|
|
@@ -46,9 +46,12 @@ import { MenuSeparator } from "./components/menu/menu-separator.js";
|
|
|
46
46
|
import { MenuSubmenuTrigger } from "./components/menu/menu-submenu-trigger.js";
|
|
47
47
|
import { Menu, MenuSubmenu, MenuTrigger } from "./components/menu/menu.js";
|
|
48
48
|
import { Meter } from "./components/meter/meter.js";
|
|
49
|
+
import { Textfield } from "./components/textfield/textfield.js";
|
|
50
|
+
import { OTPField } from "./components/otp-field/otp-field.js";
|
|
49
51
|
import { PopoverSheet } from "./components/popover/popover-sheet.js";
|
|
50
52
|
import { Popover, PopoverClose, PopoverDescription, PopoverTitle, PopoverTrigger } from "./components/popover/popover.js";
|
|
51
53
|
import { ProgressiveBlur } from "./components/progressive-blur/progressive-blur.js";
|
|
54
|
+
import { Prose } from "./components/prose/prose.js";
|
|
52
55
|
import { Radio } from "./components/radio/radio.js";
|
|
53
56
|
import { RadioGroup } from "./components/radio/radio-group.js";
|
|
54
57
|
import { Select } from "./components/select/select.js";
|
|
@@ -60,7 +63,6 @@ import { StaticNoise } from "./components/static-noise/static-noise.js";
|
|
|
60
63
|
import { Switch } from "./components/switch/switch.js";
|
|
61
64
|
import { Tabs } from "./components/tabs/tabs.js";
|
|
62
65
|
import { Textarea } from "./components/textarea/textarea.js";
|
|
63
|
-
import { Textfield } from "./components/textfield/textfield.js";
|
|
64
66
|
import { Title } from "./components/title/title.js";
|
|
65
67
|
import { Toast } from "./components/toast/toast.js";
|
|
66
68
|
import { ToggleButton } from "./components/toggle-button/toggle-button.js";
|
|
@@ -68,4 +70,4 @@ import { ToggleGroup } from "./components/toggle-group/toggle-group.js";
|
|
|
68
70
|
import { Typewriter } from "./components/typewriter/typewriter.js";
|
|
69
71
|
import { TooltipContent } from "./components/tooltip/tooltip-content.js";
|
|
70
72
|
import { Tooltip, TooltipProvider, TooltipTrigger } from "./components/tooltip/tooltip.js";
|
|
71
|
-
export { Accordion, Annotate, Autocomplete, Avatar, AvatarGroup, Badge, Beam, Bleed, BreakpointsProvider, Button, ButtonLink, Checkbox, CheckboxGroup, Chip, ClampText, Dialog, DialogClose, DialogIndent, DialogIndentBackground, DialogSheet, DialogTrigger, ELEVATION_DEFAULTS, Elevator, Fieldset, FitText, Glow, Grid, IconButton, IconButtonLink, LinearProgress, Masonry, Menu, MenuCheckboxItem, MenuGroup, MenuItem, MenuLinkItem, MenuPopup, MenuRadioGroup, MenuRadioItem, MenuSeparator, MenuSubmenu, MenuSubmenuTrigger, MenuTrigger, Meter, Popover, PopoverClose, PopoverDescription, PopoverSheet, PopoverTitle, PopoverTrigger, ProgressiveBlur, Radio, RadioGroup, Select, Separator, Shimmer, Skeleton, Slider, Spinner, Stack, StaticNoise, Surface, Switch, Tabs, Text, Textarea, Textfield, Title, Toast, ToggleButton, ToggleGroup, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, Typewriter, getElevationProps, useBreakpoints };
|
|
73
|
+
export { Accordion, Annotate, Autocomplete, Avatar, AvatarGroup, Badge, Beam, Bleed, BreakpointsProvider, Button, ButtonLink, Checkbox, CheckboxGroup, Chip, ClampText, Dialog, DialogClose, DialogIndent, DialogIndentBackground, DialogSheet, DialogTrigger, ELEVATION_DEFAULTS, Elevator, Fieldset, FitText, Glow, Grid, IconButton, IconButtonLink, LinearProgress, Masonry, Menu, MenuCheckboxItem, MenuGroup, MenuItem, MenuLinkItem, MenuPopup, MenuRadioGroup, MenuRadioItem, MenuSeparator, MenuSubmenu, MenuSubmenuTrigger, MenuTrigger, Meter, OTPField, Popover, PopoverClose, PopoverDescription, PopoverSheet, PopoverTitle, PopoverTrigger, ProgressiveBlur, Prose, Radio, RadioGroup, Select, Separator, Shimmer, Skeleton, Slider, Spinner, Stack, StaticNoise, Surface, Switch, Tabs, Text, Textarea, Textfield, Title, Toast, ToggleButton, ToggleGroup, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, Typewriter, getElevationProps, useBreakpoints };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
2
|
<a11y surface="beam">
|
|
3
|
-
<rule>Root stays in the accessibility tree; bloom layer is empty div</rule>
|
|
3
|
+
<rule>Root stays in the accessibility tree; bloom layer is an empty decorative div with aria-hidden</rule>
|
|
4
4
|
<rule>Beam is not a focus or status indicator — consumer owns accessible names and state</rule>
|
|
5
|
-
<rule>Offscreen instances pause
|
|
6
|
-
<rule>prefers-reduced-motion
|
|
5
|
+
<rule>Offscreen instances set data-paused="true" and pause motion without firing activate/deactivate</rule>
|
|
6
|
+
<rule>prefers-reduced-motion keeps a static visible rest (no looping motion); animate=false still fades the effect away</rule>
|
|
7
7
|
</a11y>
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
<description>Effect family: rotating border, bottom line travel, or pulse breathe (outside/inside).</description>
|
|
20
20
|
</prop>
|
|
21
21
|
<prop name="size" kind="react">
|
|
22
|
-
<description>
|
|
22
|
+
<description>Size preset for border geometry and pulse-inside inner glow band (small compact / medium card). Ignored for line and pulse-outside.</description>
|
|
23
23
|
</prop>
|
|
24
24
|
<prop name="color" kind="react">
|
|
25
25
|
<description>Beam color: primary → --global-primary, highlight token name → --highlight-*, colorful → upstream neon multi-stop RGB palette.</description>
|
|
@@ -81,10 +81,31 @@
|
|
|
81
81
|
<prop name="hue" kind="css">
|
|
82
82
|
<description>Pulse hue drift angle written by the shared pulse driver.</description>
|
|
83
83
|
</prop>
|
|
84
|
+
<prop name="hue-base" kind="css">
|
|
85
|
+
<description>Base hue for hue-rotate filters on glow layers.</description>
|
|
86
|
+
</prop>
|
|
87
|
+
<prop name="stroke-opacity" kind="css">
|
|
88
|
+
<description>Opacity of the stroke/conic layer; variant CSS may override.</description>
|
|
89
|
+
</prop>
|
|
90
|
+
<prop name="inner-opacity" kind="css">
|
|
91
|
+
<description>Opacity of the inner glow layer; variant CSS may override.</description>
|
|
92
|
+
</prop>
|
|
93
|
+
<prop name="bloom-opacity" kind="css">
|
|
94
|
+
<description>Opacity of the bloom layer; variant CSS may override.</description>
|
|
95
|
+
</prop>
|
|
84
96
|
<prop name="pulse-glow-sx" kind="css">
|
|
85
97
|
<description>Per-axis X scale for pulse-outside glow geometry relative to a 350×140 reference.</description>
|
|
86
98
|
</prop>
|
|
87
99
|
<prop name="pulse-glow-sy" kind="css">
|
|
88
100
|
<description>Per-axis Y scale for pulse-outside glow geometry relative to a 350×140 reference.</description>
|
|
89
101
|
</prop>
|
|
102
|
+
<prop name="pulse-glow-boost" kind="css">
|
|
103
|
+
<description>Extra pulse-outside glow intensity multiplier (internal default 1; not a React prop).</description>
|
|
104
|
+
</prop>
|
|
105
|
+
<prop name="core-blur" kind="css">
|
|
106
|
+
<description>Core glow blur radius (BorderBeam glowSize=1 default).</description>
|
|
107
|
+
</prop>
|
|
108
|
+
<prop name="bloom-blur" kind="css">
|
|
109
|
+
<description>Bloom layer blur radius (BorderBeam glowSize=1 default).</description>
|
|
110
|
+
</prop>
|
|
90
111
|
</props>
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
<do>Pick `variant` for the job: `border` spin, `line` travel, `pulse-outside` / `pulse-inside` breathe — use Glow instead when the highlight should follow the pointer</do>
|
|
6
6
|
<do>Use sparingly as a focal accent — shared pulse rAF still paints per instance; prefer one Beam per view</do>
|
|
7
7
|
<dont>Treating Beam as Surface color/padding/border chrome — nest Surface/Stack children for the real card</dont>
|
|
8
|
+
<dont>Confusing prefers-reduced-motion (static rest stays visible) with `animate={false}` (effect fades away)</dont>
|
|
8
9
|
<dont>Relying on Beam for keyboard focus or status — decorative only</dont>
|
|
9
10
|
<dont>Expecting theme light/dark auto-tuning — colors are token-driven and theme-agnostic</dont>
|
|
10
11
|
<dont>Scattering many Beam instances on one page</dont>
|
|
@@ -50,4 +50,16 @@
|
|
|
50
50
|
]]></tsx>
|
|
51
51
|
<notes>From preview-grid `daily-activity-goal` nested tips sheet. Meter for score-within-range; LinearProgress stays on the live activity track outside the dialog.</notes>
|
|
52
52
|
</example>
|
|
53
|
+
|
|
54
|
+
<example title="Highlight variants">
|
|
55
|
+
<tsx><![CDATA[
|
|
56
|
+
<Stack rowGap="m">
|
|
57
|
+
<Meter label="Health" renderValue value={92} variant="green" />
|
|
58
|
+
<Meter label="Latency" renderValue value={38} variant="yellow" />
|
|
59
|
+
<Meter label="Errors" renderValue value={12} variant="red" />
|
|
60
|
+
<Meter label="Quota" renderValue value={64} variant="blue" />
|
|
61
|
+
</Stack>
|
|
62
|
+
]]></tsx>
|
|
63
|
+
<notes>`variant` highlight tokens tint track wash + indicator together (Chip-aligned). Default `primary` keeps brand indicator + contrast track.</notes>
|
|
64
|
+
</example>
|
|
53
65
|
</patterns>
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
<item>Use for storage usage, battery level, capacity, or score within a known range.</item>
|
|
5
5
|
<item>Use with a label when the metric needs a visible name.</item>
|
|
6
6
|
<item>Use with `renderValue` when the formatted value should appear beside the label.</item>
|
|
7
|
+
<item>Use highlight `variant` values when the track and indicator should share a semantic color.</item>
|
|
7
8
|
</when_to_use>
|
|
8
9
|
<when_not_to_use>
|
|
9
10
|
<item>Use LinearProgress for in-progress tasks or indeterminate work.</item>
|
|
@@ -19,12 +20,18 @@
|
|
|
19
20
|
<prop name="renderValue" kind="react">
|
|
20
21
|
<description>Shows the current value beside the label. Pass `true` for Base UI default formatting, or a function matching `Meter.Value` `children` to customize the printed label.</description>
|
|
21
22
|
</prop>
|
|
23
|
+
<prop name="variant" kind="react">
|
|
24
|
+
<description>Selects track and indicator colors. `primary` uses brand primary on the indicator; highlight token names tint both track (wash) and indicator via `--highlight-*`, matching Chip highlight variants.</description>
|
|
25
|
+
</prop>
|
|
22
26
|
<prop name="indicator-color" kind="css">
|
|
23
27
|
<description>Determinate meter indicator color.</description>
|
|
24
28
|
</prop>
|
|
25
29
|
<prop name="track-background" kind="css">
|
|
26
30
|
<description>Meter track background color.</description>
|
|
27
31
|
</prop>
|
|
32
|
+
<prop name="track-opacity" kind="css">
|
|
33
|
+
<description>Highlight variant track wash opacity mix against the indicator color.</description>
|
|
34
|
+
</prop>
|
|
28
35
|
<prop name="track-size" kind="css">
|
|
29
36
|
<description>Meter track thickness on the root.</description>
|
|
30
37
|
</prop>
|
|
@@ -4,10 +4,12 @@
|
|
|
4
4
|
<do>Labeled metrics: `label` + `renderValue` + optional `description`; tune print with Base UI `format` / `locale`</do>
|
|
5
5
|
<do>Workspace stacks: Stack `rowGap` of Meters — or nest one Meter inside Surface for sheet / tip chrome</do>
|
|
6
6
|
<do>Rich labels: pass a Stack row (icon + text) as `label` when needed — no custom label CSS</do>
|
|
7
|
+
<do>Color-coded metrics via highlight `variant` (`green`, `red`, `yellow`, …) or default `primary` — tints track wash + indicator like Chip</do>
|
|
7
8
|
<do>Prefer LinearProgress for in-progress or indeterminate tasks (`value={null}`) — Meter has no indeterminate mode</do>
|
|
8
9
|
<dont>Using Meter as an interactive value control — use Slider</dont>
|
|
9
10
|
<dont>Using Meter for indeterminate or in-progress work — use LinearProgress</dont>
|
|
10
11
|
<dont>Modeling invalid or disabled meter state — Base UI Meter exposes neither</dont>
|
|
11
12
|
<dont>Hiding the metric name when multiple meters share a view</dont>
|
|
12
13
|
<dont>Passing `value={null}` — Meter `value` is required `number`</dont>
|
|
14
|
+
<dont>Reinventing highlight track/indicator colors with raw `--highlight-*` CSS when `variant` covers the token</dont>
|
|
13
15
|
</usage>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<a11y surface="otp-field">
|
|
3
|
+
<rule>One Field: visible `label` names the first slot via Field.Label / htmlFor. Do not put aria-label on the first Input.</rule>
|
|
4
|
+
<rule>Slots after the first get `aria-label` `Character N of {length}` in English when the consumer omits `aria-label`. Consumer `aria-label` wins when passed.</rule>
|
|
5
|
+
<rule>Description and error copy live on the OTPField root helper slot (dirty+invalid), not per slot.</rule>
|
|
6
|
+
<rule>`OTPField.Separator` is a separator, not an input, and is not counted in `length`.</rule>
|
|
7
|
+
<rule>Slot host is always a native `input`. Chrome is Textfield `controlOnly` (no second Field).</rule>
|
|
8
|
+
<rule>Focus ring is per active slot (`:focus`). Input `data-focused` means any slot is focused — do not paint chrome from it.</rule>
|
|
9
|
+
<rule>Invalid wiggle is one-shot chrome only. Error copy / `aria-invalid` still name the error. Reduced motion skips the animation.</rule>
|
|
10
|
+
</a11y>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<meta id="otp-field" title="OTPField" category="form" shared_contract="otp-field">
|
|
3
|
+
<summary>Field-wrapped one-time code slots. Default slots compose Textfield controlOnly; one label/helper on Root.</summary>
|
|
4
|
+
<exports>
|
|
5
|
+
<export name="OTPField" />
|
|
6
|
+
<export name="OTPField.Root" />
|
|
7
|
+
<export name="OTPField.Input" />
|
|
8
|
+
<export name="OTPField.Separator" />
|
|
9
|
+
</exports>
|
|
10
|
+
<related>
|
|
11
|
+
<ref id="textfield" />
|
|
12
|
+
<ref id="fieldset" />
|
|
13
|
+
<ref id="text" />
|
|
14
|
+
<ref id="stack" />
|
|
15
|
+
</related>
|
|
16
|
+
<units>
|
|
17
|
+
<unit kind="a11y" href="a11y.xml" />
|
|
18
|
+
<unit kind="props" href="props.xml" />
|
|
19
|
+
<unit kind="usage" href="usage.xml" />
|
|
20
|
+
<unit kind="patterns" href="patterns.xml" />
|
|
21
|
+
</units>
|
|
22
|
+
<base_ui href="https://base-ui.com/react/components/otp-field#api-reference" />
|
|
23
|
+
</meta>
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<patterns surface="otp-field">
|
|
3
|
+
<example title="Labeled six-slot code">
|
|
4
|
+
<tsx><![CDATA[
|
|
5
|
+
<OTPField.Root
|
|
6
|
+
length={6}
|
|
7
|
+
label="Verification code"
|
|
8
|
+
description="Enter the 6-character code we sent to your device."
|
|
9
|
+
>
|
|
10
|
+
{Array.from({ length: 6 }, (_, slotIndex) => (
|
|
11
|
+
<OTPField.Input key={`character-${slotIndex + 1}`} />
|
|
12
|
+
))}
|
|
13
|
+
</OTPField.Root>
|
|
14
|
+
]]></tsx>
|
|
15
|
+
<notes>Explicit Inputs. First slot uses the field label; later slots get generated names.</notes>
|
|
16
|
+
</example>
|
|
17
|
+
|
|
18
|
+
<example title="Form name + autoSubmit">
|
|
19
|
+
<tsx><![CDATA[
|
|
20
|
+
<Form>
|
|
21
|
+
<OTPField.Root length={6} name="verificationCode" label="Verification code" autoSubmit>
|
|
22
|
+
<OTPField.Input />
|
|
23
|
+
<OTPField.Input />
|
|
24
|
+
<OTPField.Input />
|
|
25
|
+
<OTPField.Input />
|
|
26
|
+
<OTPField.Input />
|
|
27
|
+
<OTPField.Input />
|
|
28
|
+
</OTPField.Root>
|
|
29
|
+
</Form>
|
|
30
|
+
]]></tsx>
|
|
31
|
+
<notes>One concatenated form value. `autoSubmit` submits when complete. `Form` is `@base-ui/react/form`.</notes>
|
|
32
|
+
</example>
|
|
33
|
+
|
|
34
|
+
<example title="Grouped slots with Separator">
|
|
35
|
+
<tsx><![CDATA[
|
|
36
|
+
<OTPField.Root length={6} label="Verification code">
|
|
37
|
+
<Stack columnGap="s" direction="row">
|
|
38
|
+
<OTPField.Input />
|
|
39
|
+
<OTPField.Input />
|
|
40
|
+
<OTPField.Input />
|
|
41
|
+
</Stack>
|
|
42
|
+
<OTPField.Separator />
|
|
43
|
+
<Stack columnGap="s" direction="row">
|
|
44
|
+
<OTPField.Input />
|
|
45
|
+
<OTPField.Input />
|
|
46
|
+
<OTPField.Input />
|
|
47
|
+
</Stack>
|
|
48
|
+
</OTPField.Root>
|
|
49
|
+
]]></tsx>
|
|
50
|
+
<notes>Separator is not an input and is not counted in `length`. Stack owns group gaps.</notes>
|
|
51
|
+
</example>
|
|
52
|
+
|
|
53
|
+
<example title="Equal-grow in a form column">
|
|
54
|
+
<tsx><![CDATA[
|
|
55
|
+
<OTPField.Root length={6} label="Verification code" fullWidth>
|
|
56
|
+
<OTPField.Input />
|
|
57
|
+
<OTPField.Input />
|
|
58
|
+
<OTPField.Input />
|
|
59
|
+
<OTPField.Input />
|
|
60
|
+
<OTPField.Input />
|
|
61
|
+
<OTPField.Input />
|
|
62
|
+
</OTPField.Root>
|
|
63
|
+
]]></tsx>
|
|
64
|
+
<notes>Default hugs. `fullWidth` fills the column and grows slots equally.</notes>
|
|
65
|
+
</example>
|
|
66
|
+
</patterns>
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<props surface="otp-field">
|
|
3
|
+
<when_to_use>
|
|
4
|
+
<item>One-time codes, verification codes, and recovery codes as a single labeled field</item>
|
|
5
|
+
<item>Numeric, alphanumeric, or masked slot entry with form `name` submit of the concatenated value</item>
|
|
6
|
+
<item>Grouped slot rows with `OTPField.Separator` and Stack, not a `groups` prop</item>
|
|
7
|
+
</when_to_use>
|
|
8
|
+
<when_not_to_use>
|
|
9
|
+
<item>Auto-generating Inputs from `length` — always list one `OTPField.Input` per slot</item>
|
|
10
|
+
<item>Per-slot label, description, or error — Field chrome is on Root only</item>
|
|
11
|
+
<item>Swapping the slot host via `render` — host is always a native `input`; chrome is Textfield `controlOnly`</item>
|
|
12
|
+
<item>Inventing CSS `width: 100%` on slots when Root `fullWidth` covers equal-grow</item>
|
|
13
|
+
</when_not_to_use>
|
|
14
|
+
<prop name="description" kind="react">
|
|
15
|
+
<description>Helper copy rendered with `Field.Description` and `Text` while the field is valid.</description>
|
|
16
|
+
</prop>
|
|
17
|
+
<prop name="error" kind="react">
|
|
18
|
+
<description>Error copy rendered with `Field.Error` and `Text` in the same slot as `description`. Replaces description while the field is invalid and dirty.</description>
|
|
19
|
+
</prop>
|
|
20
|
+
<prop name="fullWidth" kind="react">
|
|
21
|
+
<description>When `true`, the field is `inline-size: 100%` and the inner slot Stack uses `fullWidth` + `expandChildren` so slots grow equally. Prefer this over CSS `width: 100%` on slots.</description>
|
|
22
|
+
</prop>
|
|
23
|
+
<prop name="label" kind="react">
|
|
24
|
+
<description>Visible label rendered with shared internal `FieldLabel`. Names the first slot.</description>
|
|
25
|
+
</prop>
|
|
26
|
+
<prop name="length" kind="react">
|
|
27
|
+
<description>Required slot count for clamp, complete, and hidden validation markup. Does not spawn Inputs — list exactly this many `OTPField.Input` children.</description>
|
|
28
|
+
</prop>
|
|
29
|
+
</props>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<usage surface="otp-field">
|
|
3
|
+
<do>List exactly `length` `OTPField.Input` children (Array.from or explicit)</do>
|
|
4
|
+
<do>Put `label` / `description` / `error` on `OTPField.Root` (or `OTPField`)</do>
|
|
5
|
+
<do>Let the first slot take the field label; later slots get generated `Character N of {length}` unless you pass `aria-label`</do>
|
|
6
|
+
<do>Group slots with Stack + `OTPField.Separator` when the code is shown as chunks</do>
|
|
7
|
+
<do>Span the form column with Root `fullWidth` so slots grow equally</do>
|
|
8
|
+
<do>Keep error copy on Root — invalid wiggle is extra chrome, not the only signal</do>
|
|
9
|
+
<dont>Pass `render` on Input — slot is always a native `input`; chrome is Textfield `controlOnly`</dont>
|
|
10
|
+
<dont>Put label/description/error on each slot</dont>
|
|
11
|
+
<dont>Expect Root to auto-generate Inputs from `length`</dont>
|
|
12
|
+
<dont>Invent a `groups` prop or width CSS on slots when Stack / `fullWidth` cover layout</dont>
|
|
13
|
+
</usage>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<a11y surface="prose">
|
|
3
|
+
<rule>Prose is grouping, not a widget — do not set a role on the root</rule>
|
|
4
|
+
<rule>Default root is a generic `div`; it does not require an accessible name</rule>
|
|
5
|
+
<rule>Landmark semantics only when the consumer replaces the root (`render={<article />}` or similar) and names the landmark if needed</rule>
|
|
6
|
+
<rule>Children keep their own roles (headings, lists, links); Prose does not restyle or re-role them</rule>
|
|
7
|
+
</a11y>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<meta id="prose" title="Prose" category="typography" shared_contract="prose">
|
|
3
|
+
<summary>Grouping region that applies heading-aware em rhythm to direct document blocks.</summary>
|
|
4
|
+
<exports>
|
|
5
|
+
<export name="Prose" />
|
|
6
|
+
</exports>
|
|
7
|
+
<related>
|
|
8
|
+
<ref id="text" />
|
|
9
|
+
<ref id="title" />
|
|
10
|
+
<ref id="stack" />
|
|
11
|
+
</related>
|
|
12
|
+
<units>
|
|
13
|
+
<unit kind="a11y" href="a11y.xml" />
|
|
14
|
+
<unit kind="props" href="props.xml" />
|
|
15
|
+
<unit kind="usage" href="usage.xml" />
|
|
16
|
+
<unit kind="patterns" href="patterns.xml" />
|
|
17
|
+
</units>
|
|
18
|
+
</meta>
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<patterns surface="prose">
|
|
3
|
+
<example title="Article with Title and Text">
|
|
4
|
+
<tsx><![CDATA[
|
|
5
|
+
<Prose render={<article />}>
|
|
6
|
+
<Title size="2" lineHeight="s" render={<h2 />}>
|
|
7
|
+
Heading-aware document rhythm
|
|
8
|
+
</Title>
|
|
9
|
+
<Text render={<p />}>
|
|
10
|
+
Direct heading and paragraph children pick up Prose gaps. Type stays on Title and Text.
|
|
11
|
+
</Text>
|
|
12
|
+
<Text render={<p />}>A second paragraph uses the block gap, not the heading-end gap.</Text>
|
|
13
|
+
</Prose>
|
|
14
|
+
]]></tsx>
|
|
15
|
+
<notes>Default `gap="l"`. Landmark via `render={<article />}`. Title in Prose uses `lineHeight="s"`. Do not wrap these blocks in Stack for rhythm.</notes>
|
|
16
|
+
</example>
|
|
17
|
+
|
|
18
|
+
<example title="Density and region align">
|
|
19
|
+
<tsx><![CDATA[
|
|
20
|
+
<Prose gap="s" align="center">
|
|
21
|
+
<Title size="3" lineHeight="s" render={<h3 />}>
|
|
22
|
+
Compact notes
|
|
23
|
+
</Title>
|
|
24
|
+
<Text render={<p />}>One Prose `align` sets the region. Omit Title/Text `align` so they inherit.</Text>
|
|
25
|
+
</Prose>
|
|
26
|
+
]]></tsx>
|
|
27
|
+
<notes>`s` < `m` < `l`. `align` maps to `--vui-prose-align` + `text-align` on the root. Title `lineHeight="s"`.</notes>
|
|
28
|
+
</example>
|
|
29
|
+
|
|
30
|
+
<example title="Line height on body">
|
|
31
|
+
<tsx><![CDATA[
|
|
32
|
+
<Prose>
|
|
33
|
+
<Title size="2" lineHeight="s" render={<h2 />}>
|
|
34
|
+
Tight heading, chosen body leading
|
|
35
|
+
</Title>
|
|
36
|
+
<Text render={<p />} lineHeight="l">
|
|
37
|
+
Headings in Prose stay on Title `lineHeight="s"`. Body leading is a Text prop — this paragraph uses `l`.
|
|
38
|
+
</Text>
|
|
39
|
+
<Text render={<p />} lineHeight="s">
|
|
40
|
+
A following paragraph can use `lineHeight="s"` when the article should sit denser.
|
|
41
|
+
</Text>
|
|
42
|
+
</Prose>
|
|
43
|
+
]]></tsx>
|
|
44
|
+
<notes>Prose does not own leading. Title inside Prose → `lineHeight="s"`. Text `lineHeight` per block.</notes>
|
|
45
|
+
</example>
|
|
46
|
+
|
|
47
|
+
<example title="Span hosts stay out">
|
|
48
|
+
<tsx><![CDATA[
|
|
49
|
+
<Prose>
|
|
50
|
+
<Title size="4" lineHeight="s">
|
|
51
|
+
This Title is a span — no Prose heading gap
|
|
52
|
+
</Title>
|
|
53
|
+
<Text>This Text is a span — no Prose block gap.</Text>
|
|
54
|
+
<Text render={<p />}>This paragraph is a matching block.</Text>
|
|
55
|
+
</Prose>
|
|
56
|
+
]]></tsx>
|
|
57
|
+
<notes>Default Title/Text hosts are `span`. Use `render` for h1–h6 / `p` when they should join rhythm. Title still `lineHeight="s"`.</notes>
|
|
58
|
+
</example>
|
|
59
|
+
</patterns>
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<props surface="prose">
|
|
3
|
+
<when_to_use>
|
|
4
|
+
<item>Long-form copy whose direct children are headings and blocks that need heading-aware vertical rhythm</item>
|
|
5
|
+
<item>Article, docs, or changelog regions where Stack `rowGap` would make heading→body and body→body the same</item>
|
|
6
|
+
<item>Optional `gap` density (`s` / `m` / `l`) and region `align` (Title/Text that omit `align` inherit it)</item>
|
|
7
|
+
</when_to_use>
|
|
8
|
+
<when_not_to_use>
|
|
9
|
+
<item>UI chrome spacing (cards, toolbars, form rows) — use Stack `rowGap` / `columnGap`</item>
|
|
10
|
+
<item>Restyling type, lists, links, or tables — stay on Title / Text / consumer CSS</item>
|
|
11
|
+
<item>Markdown parse or sanitize — Prose only spaces matching direct children</item>
|
|
12
|
+
</when_not_to_use>
|
|
13
|
+
<prop name="align" kind="react">
|
|
14
|
+
<description>Aligns the region using logical alignment values. Omit so `text-align` inherits from the parent. Title/Text children that omit `align` inherit this value.</description>
|
|
15
|
+
</prop>
|
|
16
|
+
<prop name="gap" kind="react">
|
|
17
|
+
<description>Em density for heading-aware vertical rhythm (`s` < `m` < `l`).</description>
|
|
18
|
+
</prop>
|
|
19
|
+
<prop name="ref" kind="react">
|
|
20
|
+
<description>Ref forwarded to the rendered prose root element.</description>
|
|
21
|
+
</prop>
|
|
22
|
+
<prop name="render" kind="react">
|
|
23
|
+
<description>Replaces the default `div` root using Base UI render semantics.</description>
|
|
24
|
+
</prop>
|
|
25
|
+
<prop name="align" kind="css">
|
|
26
|
+
<description>Region `text-align` (`--vui-prose-align`) when the `align` prop is set; omitted inherits parent `text-align`.</description>
|
|
27
|
+
</prop>
|
|
28
|
+
<prop name="gap-blocks" kind="css">
|
|
29
|
+
<description>Block sibling gap (`--vui-prose-gap-blocks`). Default `2em` (`l`); `s`/`m` assign smaller triples on the root.</description>
|
|
30
|
+
</prop>
|
|
31
|
+
<prop name="gap-heading-end" kind="css">
|
|
32
|
+
<description>Heading block-end gap (`--vui-prose-gap-heading-end`). Default `0.8em` (`l`).</description>
|
|
33
|
+
</prop>
|
|
34
|
+
<prop name="gap-heading-start" kind="css">
|
|
35
|
+
<description>Gap after a heading onto the next sibling (`--vui-prose-gap-heading-start`). Default `1.3em` (`l`).</description>
|
|
36
|
+
</prop>
|
|
37
|
+
</props>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<usage surface="prose">
|
|
3
|
+
<do>Put headings and blocks as **direct** children — Title/Text with `render={<h* />}` / `render={<p />}`, plus native `ul`/`ol`/`blockquote` when needed</do>
|
|
4
|
+
<do>Pick `gap` `s` / `m` / `l` (default `l`) for density; `align` on Prose to set one `text-align` for the region (omit Title/Text `align` so they inherit)</do>
|
|
5
|
+
<do>Use `render={<article />}` (or another landmark) when the region is the document landmark; name it if the landmark needs a name</do>
|
|
6
|
+
<do>Leave type on Title/Text; Title inside Prose uses `lineHeight="s"`; override `--vui-prose-gap-*` on the Prose root only when the em triple must change</do>
|
|
7
|
+
<dont>Using Stack `rowGap` as article rhythm — one gap cannot split heading→body vs body→body</dont>
|
|
8
|
+
<dont>Expecting default Title/Text `span` hosts to join rhythm — they are not heading/block tags</dont>
|
|
9
|
+
<dont>Setting `as` — replace the root with `render` only</dont>
|
|
10
|
+
<dont>Setting `align` on every Title/Text inside Prose when one Prose `align` covers the region</dont>
|
|
11
|
+
<dont>Setting a widget `role` on Prose, or painting font/color/list/link chrome in Prose CSS</dont>
|
|
12
|
+
</usage>
|
|
@@ -30,6 +30,9 @@
|
|
|
30
30
|
<prop name="vPadding" kind="react">
|
|
31
31
|
<description>Sets block padding on the Stack wrapper using one token for both sides or a `[start, end]` tuple.</description>
|
|
32
32
|
</prop>
|
|
33
|
+
<prop name="maxWidth" kind="react">
|
|
34
|
+
<description>Caps the Stack wrapper inline size. Use for a short dash in a horizontal row.</description>
|
|
35
|
+
</prop>
|
|
33
36
|
<prop name="border-style" kind="css">
|
|
34
37
|
<description>Separator line border style.</description>
|
|
35
38
|
</prop>
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
<do>Bookend action strips — Separator above/below a Button row inside Surface (editor footers)</do>
|
|
6
6
|
<do>Vertical gutters or toolbar splits — `orientation="vertical"` in a row Stack beside content or Button groups</do>
|
|
7
7
|
<do>Token thickness (`size`) and `variant` (e.g. dashed); space with Separator `hPadding`/`vPadding` or parent Stack gap — no custom border CSS</do>
|
|
8
|
+
<do>Short dash in a row — `maxWidth` token (Stack cap), not extra width CSS</do>
|
|
8
9
|
<dont>Using Select.Separator for general layout dividers — that part is for Select option lists only</dont>
|
|
9
10
|
<dont>Replacing Surface borders or card outlines with Separator — use Surface `border` props for container chrome</dont>
|
|
10
11
|
<dont>Inventing divider margin/gap CSS when parent Stack `rowGap` / `columnGap` and Separator padding props cover spacing</dont>
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
<item>Inventing CSS `letter-spacing` / `text-transform` / `width: 100%` when `tracking` / `transform` / parent Stack `fullWidth` cover the need</item>
|
|
13
13
|
</when_not_to_use>
|
|
14
14
|
<prop name="align" kind="react">
|
|
15
|
-
<description>Aligns text using logical alignment values.</description>
|
|
15
|
+
<description>Aligns text using logical alignment values. Omit so `text-align` inherits from the parent.</description>
|
|
16
16
|
</prop>
|
|
17
17
|
<prop name="balanced" kind="react">
|
|
18
18
|
<description>Enables balanced wrapping for block-like copy.</description>
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
<description>Controls white-space preservation and wrapping behavior.</description>
|
|
79
79
|
</prop>
|
|
80
80
|
<prop name="align" kind="css">
|
|
81
|
-
<description>Text alignment on the root
|
|
81
|
+
<description>Text alignment on the root when the `align` prop is set; omitted inherits parent `text-align`.</description>
|
|
82
82
|
</prop>
|
|
83
83
|
<prop name="font-family" kind="css">
|
|
84
84
|
<description>Font family; family prop presets via data attributes.</description>
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
<do>Hero / status motion: nest Typewriter inside Text when body tone/size should drive the typed line</do>
|
|
9
9
|
<do>Ellipsis: ClampText `render={<Text … />}` — FitText when copy must scale to fill width instead of truncate</do>
|
|
10
10
|
<do>Letter-spacing via `tracking`; text-transform via `transform`; width via `maxWidth` or parent Stack `fullWidth`</do>
|
|
11
|
+
<do>Omit `align` so copy inherits parent `text-align`; pass `align` only to override</do>
|
|
11
12
|
<do>Inline prose link: `Text render={<a href=… />}` — keeps preflight link chrome (no `data-no-link-style`)</do>
|
|
12
13
|
<dont>Using Text for headings — use Title (`render` for h1–h6)</dont>
|
|
13
14
|
<dont>Putting `data-no-link-style` on Text-as-a when the default underline hover is wanted</dont>
|
|
@@ -5,14 +5,19 @@
|
|
|
5
5
|
<item>Start or end addon slots inside the shared control group</item>
|
|
6
6
|
<item>Label, description, and error copy with validation states</item>
|
|
7
7
|
<item>Several related Textfields grouped under one section legend as Fieldset children (e.g. billing details)</item>
|
|
8
|
+
<item>Control chrome inside another Field (`controlOnly`) — native `size` / `fitContent` still apply</item>
|
|
8
9
|
</when_to_use>
|
|
9
10
|
<when_not_to_use>
|
|
10
11
|
<item>Multi-line entry — use Textarea</item>
|
|
11
|
-
<item>Custom size or variant props — fixed medium treatment</item>
|
|
12
|
+
<item>Custom visual size or variant props — fixed medium treatment (native HTML `size` is allowed)</item>
|
|
12
13
|
<item>Wrapping a lone Textfield in Fieldset when its own label is sufficient — Fieldset is for multi-field sections or shared legends</item>
|
|
13
14
|
<item>Using Fieldset instead of the Textfield label for a single control</item>
|
|
14
15
|
<item>Inventing CSS `width: 100%` / `w-full` when the `fullWidth` prop covers it</item>
|
|
16
|
+
<item>`controlOnly` as a standalone labeled field — omit it when you need Field chrome</item>
|
|
15
17
|
</when_not_to_use>
|
|
18
|
+
<prop name="controlOnly" kind="react">
|
|
19
|
+
<description>When `true`, skip Field.Root / label / helper. Control group + input only. Use inside another Field (OTP slots). Native `size` and `fitContent` still apply. `label`, `description`, and `error` are ignored.</description>
|
|
20
|
+
</prop>
|
|
16
21
|
<prop name="description" kind="react">
|
|
17
22
|
<description>Helper copy rendered with `Field.Description` and `Text` while the field is valid.</description>
|
|
18
23
|
</prop>
|
|
@@ -7,9 +7,10 @@
|
|
|
7
7
|
<do>Span the form column with Textfield `fullWidth` — open props before inventing width CSS</do>
|
|
8
8
|
<do>`startAddon` / `endAddon` for prefix text, icons, or actions inside the control group — IconButton/Button addons use `size="s"` and `type="button"`</do>
|
|
9
9
|
<do>Label, description, and error copy with validation states</do>
|
|
10
|
+
<do>Compose the control inside another Field with `controlOnly` — native `size` / `fitContent` still apply; skip a second Field.Root</do>
|
|
10
11
|
<dont>Multi-line entry — use Textarea</dont>
|
|
11
12
|
<dont>Inventing `width: 100%` / `w-full` CSS on the Textfield root when `fullWidth` covers it</dont>
|
|
12
|
-
<dont>Passing
|
|
13
|
+
<dont>Passing visual size or variant props — chrome is fixed medium. Native HTML `size` on the input is allowed</dont>
|
|
13
14
|
<dont>Expecting a focus ring when focus is only on addon controls</dont>
|
|
14
15
|
<dont>Wrapping a lone Textfield in Fieldset when its own label is enough</dont>
|
|
15
16
|
</usage>
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
<item>Inventing CSS `letter-spacing` / `text-transform` / `width: 100%` when `tracking` / `transform` / parent Stack `fullWidth` cover the need</item>
|
|
14
14
|
</when_not_to_use>
|
|
15
15
|
<prop name="align" kind="react">
|
|
16
|
-
<description>Aligns text using logical alignment values.</description>
|
|
16
|
+
<description>Aligns text using logical alignment values. Omit so `text-align` inherits from the parent.</description>
|
|
17
17
|
</prop>
|
|
18
18
|
<prop name="balanced" kind="react">
|
|
19
19
|
<description>Enables balanced wrapping for block-like titles.</description>
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
<description>Controls white-space preservation and wrapping behavior.</description>
|
|
74
74
|
</prop>
|
|
75
75
|
<prop name="align" kind="css">
|
|
76
|
-
<description>Title alignment on the root
|
|
76
|
+
<description>Title alignment on the root when the `align` prop is set; omitted inherits parent `text-align`.</description>
|
|
77
77
|
</prop>
|
|
78
78
|
<prop name="font-family" kind="css">
|
|
79
79
|
<description>Heading font family on the root.</description>
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
<do>Truncation: ClampText `render={<Title size=… render={<h* />} />}` — FitText wraps Title when short headlines must fill a constrained width</do>
|
|
8
8
|
<do>Fluid clamp across presets (`fluid` / theme `--effect-fluid-typography`); pass `fluid={false}` inside chrome or dense UI</do>
|
|
9
9
|
<do>Letter-spacing via `tracking`; text-transform via `transform`; width via `maxWidth` or parent Stack `fullWidth`</do>
|
|
10
|
+
<do>Omit `align` so the heading inherits parent `text-align`; pass `align` only to override</do>
|
|
10
11
|
<do>Title-as-prose-link (`render={<a />}`) keeps preflight link chrome — omit `data-no-link-style`</do>
|
|
11
12
|
<dont>Assuming `size` changes the HTML tag — choose `render` explicitly</dont>
|
|
12
13
|
<dont>Using Title for long body paragraphs — use Text</dont>
|