@devalok/shilp-sutra 0.37.0 → 0.38.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/MIGRATION.md +67 -0
- package/dist/_chunks/tiptap.js +977 -968
- package/dist/_chunks/tiptap.js.map +1 -1
- package/dist/composed/index.d.ts +0 -2
- package/dist/composed/index.d.ts.map +1 -1
- package/dist/composed/index.js +5 -6
- package/dist/composed/inline-edit.d.ts.map +1 -1
- package/dist/composed/inline-edit.js +42 -40
- package/dist/composed/inline-edit.js.map +1 -1
- package/dist/hooks/index.d.ts +2 -2
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +0 -1
- package/dist/test-utils/conformance.d.ts +37 -0
- package/dist/test-utils/conformance.d.ts.map +1 -0
- package/dist/ui/alert.d.ts +1 -2
- package/dist/ui/alert.d.ts.map +1 -1
- package/dist/ui/alert.js +1 -27
- package/dist/ui/alert.js.map +1 -1
- package/dist/ui/banner.d.ts +3 -5
- package/dist/ui/banner.d.ts.map +1 -1
- package/dist/ui/banner.js +13 -13
- package/dist/ui/banner.js.map +1 -1
- package/dist/ui/index.d.ts +1 -1
- package/dist/ui/index.d.ts.map +1 -1
- package/dist/ui/index.js +2 -2
- package/dist/ui/index.js.map +1 -1
- package/dist/ui/input.d.ts +1 -9
- package/dist/ui/input.d.ts.map +1 -1
- package/dist/ui/input.js +26 -27
- package/dist/ui/input.js.map +1 -1
- package/dist/ui/segmented-control.d.ts +1 -1
- package/dist/ui/segmented-control.d.ts.map +1 -1
- package/dist/ui/segmented-control.js +2 -4
- package/dist/ui/segmented-control.js.map +1 -1
- package/docs/components/_header.md +1 -1
- package/docs/components/composed/activity-feed.md +6 -0
- package/docs/components/composed/avatar-group.md +8 -0
- package/docs/components/composed/bulk-action-bar.md +6 -0
- package/docs/components/composed/command-palette.md +7 -0
- package/docs/components/composed/confirm-dialog.md +6 -0
- package/docs/components/composed/content-card.md +6 -0
- package/docs/components/composed/date-picker.md +10 -0
- package/docs/components/composed/deadline-indicator.md +7 -0
- package/docs/components/composed/emoji-picker.md +8 -0
- package/docs/components/composed/empty-state.md +8 -0
- package/docs/components/composed/error-boundary.md +7 -0
- package/docs/components/composed/file-preview.md +8 -0
- package/docs/components/composed/filter-bar.md +8 -0
- package/docs/components/composed/form-section.md +6 -0
- package/docs/components/composed/global-loading.md +6 -0
- package/docs/components/composed/inline-edit.md +8 -0
- package/docs/components/composed/loading-skeleton.md +7 -0
- package/docs/components/composed/markdown-viewer.md +9 -0
- package/docs/components/composed/master-detail.md +7 -0
- package/docs/components/composed/member-picker.md +7 -0
- package/docs/components/composed/multi-select-popover.md +11 -0
- package/docs/components/composed/page-header.md +7 -0
- package/docs/components/composed/page-skeletons.md +10 -0
- package/docs/components/composed/priority-indicator.md +7 -0
- package/docs/components/composed/rich-chat-input.md +12 -0
- package/docs/components/composed/rich-text-editor.md +9 -0
- package/docs/components/composed/schedule-view.md +8 -0
- package/docs/components/composed/simple-tooltip.md +7 -0
- package/docs/components/composed/status-badge.md +10 -0
- package/docs/components/shell/app-command-palette.md +8 -0
- package/docs/components/shell/bottom-navbar.md +12 -0
- package/docs/components/shell/command-registry.md +7 -0
- package/docs/components/shell/link-context.md +15 -0
- package/docs/components/shell/notification-center.md +10 -0
- package/docs/components/shell/notification-preferences.md +7 -0
- package/docs/components/shell/sidebar.md +11 -0
- package/docs/components/shell/top-bar.md +11 -0
- package/docs/components/ui/accordion.md +10 -0
- package/docs/components/ui/alert-dialog.md +10 -1
- package/docs/components/ui/alert.md +13 -2
- package/docs/components/ui/aspect-ratio.md +7 -0
- package/docs/components/ui/autocomplete.md +7 -0
- package/docs/components/ui/avatar.md +10 -0
- package/docs/components/ui/badge-group.md +7 -0
- package/docs/components/ui/badge-indicator.md +7 -0
- package/docs/components/ui/badge.md +12 -0
- package/docs/components/ui/banner.md +10 -1
- package/docs/components/ui/breadcrumb.md +8 -0
- package/docs/components/ui/button-group.md +24 -4
- package/docs/components/ui/button-processing.md +5 -0
- package/docs/components/ui/button.md +9 -0
- package/docs/components/ui/card.md +16 -4
- package/docs/components/ui/charts.md +9 -0
- package/docs/components/ui/chat.md +9 -0
- package/docs/components/ui/checkbox.md +15 -1
- package/docs/components/ui/chip.md +9 -0
- package/docs/components/ui/code.md +7 -0
- package/docs/components/ui/collapsible.md +9 -0
- package/docs/components/ui/color-input.md +8 -0
- package/docs/components/ui/color-swatch.md +7 -0
- package/docs/components/ui/combobox.md +16 -3
- package/docs/components/ui/container.md +11 -0
- package/docs/components/ui/context-menu.md +8 -0
- package/docs/components/ui/data-table-body.md +4 -0
- package/docs/components/ui/data-table-bulk-actions.md +4 -0
- package/docs/components/ui/data-table-card.md +4 -0
- package/docs/components/ui/data-table-context.md +4 -0
- package/docs/components/ui/data-table-header.md +4 -0
- package/docs/components/ui/data-table-pagination.md +4 -0
- package/docs/components/ui/data-table-toolbar.md +7 -0
- package/docs/components/ui/data-table.md +25 -0
- package/docs/components/ui/devalok-grain.md +8 -0
- package/docs/components/ui/dialog.md +10 -0
- package/docs/components/ui/dropdown-menu.md +10 -0
- package/docs/components/ui/file-upload.md +8 -0
- package/docs/components/ui/form.md +21 -0
- package/docs/components/ui/hover-card.md +9 -0
- package/docs/components/ui/icon-button.md +7 -0
- package/docs/components/ui/icon-context.md +7 -0
- package/docs/components/ui/icon-group.md +9 -0
- package/docs/components/ui/icon.md +7 -0
- package/docs/components/ui/input-otp.md +29 -7
- package/docs/components/ui/input.md +11 -2
- package/docs/components/ui/label.md +7 -1
- package/docs/components/ui/link.md +7 -0
- package/docs/components/ui/menubar.md +9 -0
- package/docs/components/ui/navigation-menu.md +9 -0
- package/docs/components/ui/number-input.md +10 -0
- package/docs/components/ui/pagination.md +10 -0
- package/docs/components/ui/popover.md +9 -0
- package/docs/components/ui/progress-ring.md +7 -0
- package/docs/components/ui/progress.md +7 -0
- package/docs/components/ui/radio.md +27 -2
- package/docs/components/ui/search-input.md +8 -0
- package/docs/components/ui/segmented-control.md +21 -6
- package/docs/components/ui/select.md +14 -3
- package/docs/components/ui/separator.md +7 -0
- package/docs/components/ui/sheet.md +10 -0
- package/docs/components/ui/sidebar.md +30 -0
- package/docs/components/ui/skeleton.md +7 -0
- package/docs/components/ui/slider.md +15 -1
- package/docs/components/ui/spinner.md +7 -0
- package/docs/components/ui/split-button.md +11 -0
- package/docs/components/ui/stack.md +8 -0
- package/docs/components/ui/stat-card.md +9 -0
- package/docs/components/ui/status-dot.md +9 -0
- package/docs/components/ui/stepper.md +8 -0
- package/docs/components/ui/switch.md +8 -0
- package/docs/components/ui/table.md +8 -0
- package/docs/components/ui/tabs.md +10 -2
- package/docs/components/ui/text.md +9 -4
- package/docs/components/ui/textarea.md +7 -1
- package/docs/components/ui/toast.md +9 -0
- package/docs/components/ui/toaster.md +8 -0
- package/docs/components/ui/toggle-group.md +13 -4
- package/docs/components/ui/toggle.md +10 -2
- package/docs/components/ui/tooltip.md +11 -1
- package/docs/components/ui/tree-view.md +12 -0
- package/docs/components/ui/visually-hidden.md +7 -0
- package/docs/recipes/customize-brand.md +216 -0
- package/docs/recipes/index.md +51 -0
- package/docs/recipes/install-astro.md +178 -0
- package/docs/recipes/install-next-app-router.md +230 -0
- package/docs/recipes/install-next-pages.md +123 -0
- package/docs/recipes/install-remix.md +171 -0
- package/docs/recipes/install-tanstack-start.md +143 -0
- package/docs/recipes/install-vite.md +170 -0
- package/docs/recipes/server-components.md +209 -0
- package/docs/recipes/troubleshoot.md +217 -0
- package/llms-full.txt +1133 -88
- package/llms.txt +33 -16
- package/package.json +45 -35
- package/dist/composed/responsive-overlay.d.ts +0 -23
- package/dist/composed/responsive-overlay.d.ts.map +0 -1
- package/dist/composed/responsive-overlay.js +0 -40
- package/dist/composed/responsive-overlay.js.map +0 -1
- package/dist/hooks/use-toast.d.ts +0 -17
- package/dist/hooks/use-toast.d.ts.map +0 -1
- package/dist/hooks/use-toast.js +0 -3
- package/dist/tailwind/index.cjs +0 -41
- package/dist/tailwind/index.d.ts +0 -2
- package/dist/tailwind/index.d.ts.map +0 -1
- package/dist/tailwind/index.js +0 -2
- package/dist/tailwind/preset.d.ts +0 -25
- package/dist/tailwind/preset.d.ts.map +0 -1
- package/dist/tailwind/preset.js +0 -17
- package/dist/tailwind/preset.js.map +0 -1
- package/docs/components/composed/responsive-overlay.md +0 -34
|
@@ -32,9 +32,17 @@
|
|
|
32
32
|
/>
|
|
33
33
|
```
|
|
34
34
|
|
|
35
|
+
## Composability
|
|
36
|
+
- **Two modes, one component:** `compact={false}` (default) = drag-and-drop zone with visible affordance; `compact={true}` = inline "Upload" button. Swap by prop, same callback.
|
|
37
|
+
- **Progress integration:** Drive `uploading` + `progress` from your upload logic. The component renders an embedded Progress bar when uploading=true.
|
|
38
|
+
- **Validation is built-in:** `accept` (MIME types / extensions) + `maxSize` are enforced before `onFiles` fires. Invalid files produce `error` state instead of calling through. Your onFiles never has to re-validate.
|
|
39
|
+
- **Multi-file:** `multiple={true}` accepts File[] (array always, even for single-file mode — use `files[0]`).
|
|
40
|
+
- **Composes with Toast for feedback:** Pair with toast.success on upload complete, toast.error on failure.
|
|
41
|
+
|
|
35
42
|
## Gotchas
|
|
36
43
|
- compact=true renders a small inline button; false (default) renders a large drag-and-drop zone
|
|
37
44
|
- Client-side validation: invalid files are rejected before onFiles is called
|
|
45
|
+
- `onFiles` always receives an array — use `files[0]` even when `multiple={false}`
|
|
38
46
|
|
|
39
47
|
## Changes
|
|
40
48
|
### v0.1.0
|
|
@@ -31,10 +31,31 @@
|
|
|
31
31
|
</FormField>
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
+
## Composability
|
|
35
|
+
The FormField/useFormField pair is the central a11y wiring pattern. Components that consume it automatically become accessible inside a FormField without any explicit ARIA work on your end.
|
|
36
|
+
|
|
37
|
+
**FormField cascades through context to:**
|
|
38
|
+
- **Input, Textarea, NumberInput, InputOTP** — auto-receive `aria-describedby` (wired to FormHelperText's id), `aria-invalid` (when state="error"), and `aria-required` (when required=true).
|
|
39
|
+
- **FormHelperText** — auto-reads `state` and `helperTextId` from context; renders `role="alert"` on error so screen readers interrupt.
|
|
40
|
+
- **Label** — pair it with an Input via `htmlFor` / `id` in the normal way; FormField doesn't auto-wire labels (labels need the explicit association to satisfy screen readers reliably).
|
|
41
|
+
|
|
42
|
+
**Explicit props always override context.** Setting `state="error"` on an Input inside a FormField with `state="helper"` makes only that Input look errored.
|
|
43
|
+
|
|
44
|
+
**Nesting is NOT supported.** Don't nest FormField inside FormField — only the outermost context wins, and some a11y wiring silently breaks.
|
|
45
|
+
|
|
46
|
+
**Consuming context in your own components:**
|
|
47
|
+
```tsx
|
|
48
|
+
const field = useFormField() // returns { state, helperTextId, required } or undefined
|
|
49
|
+
// Thread field?.state onto your control's state prop,
|
|
50
|
+
// and field?.helperTextId onto aria-describedby.
|
|
51
|
+
```
|
|
52
|
+
|
|
34
53
|
## Gotchas
|
|
35
54
|
- getFormFieldA11y() was REMOVED — use useFormField() hook instead
|
|
36
55
|
- FormHelperText auto-reads state and id from FormField context
|
|
37
56
|
- FormHelperText renders role="alert" when state="error"
|
|
57
|
+
- Don't nest FormField components — one FormField per field
|
|
58
|
+
- FormField does NOT auto-wire Label→Input association; use `<Label htmlFor="x" />` + `<Input id="x" />` explicitly
|
|
38
59
|
|
|
39
60
|
## Changes
|
|
40
61
|
### v0.18.0
|
|
@@ -17,8 +17,17 @@
|
|
|
17
17
|
</HoverCard>
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
+
## Composability
|
|
21
|
+
- Radix HoverCard — accepts `open`, `onOpenChange`, `defaultOpen`, `openDelay` (ms before show), `closeDelay` (ms before hide).
|
|
22
|
+
- **Distinction from Tooltip:** HoverCardContent can contain interactive elements (links, buttons, form fields). Tooltip is for inert text labels. If you need a rich hover preview — user card, link preview, product card — use HoverCard.
|
|
23
|
+
- **Distinction from Popover:** Popover opens on click/focus; HoverCard opens on hover/focus. Use HoverCard when the intent is a passive preview, Popover when the user must explicitly invoke the panel.
|
|
24
|
+
- **Trigger:** `<HoverCardTrigger asChild>` wraps any element. Works on `<span>`, `<a>`, `<button>` — the trigger doesn't need to be interactive itself (unlike Popover where click matters).
|
|
25
|
+
- **Accessibility:** Hover-only interactions are invisible on touch. Pair with a focus-visible state or use Popover instead for critical content.
|
|
26
|
+
- **Portal + z-index:** z-popover (1400), same as DropdownMenu/Popover.
|
|
27
|
+
|
|
20
28
|
## Gotchas
|
|
21
29
|
- Overlay component — uses Framer Motion for enter/exit animations (v0.18.0)
|
|
30
|
+
- Don't nest another HoverCard inside one — both open on the same hover and conflict
|
|
22
31
|
|
|
23
32
|
## Changes
|
|
24
33
|
### v0.18.0
|
|
@@ -25,6 +25,13 @@
|
|
|
25
25
|
<IconButton icon={<Icon icon={IconTrash} />} variant="solid" color="error" aria-label="Delete" />
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
+
## Composability
|
|
29
|
+
- **Built on Button internally** — inherits ALL Button features: variant, color, loading, onClickAsync, processing, ButtonGroup context consumption, asChild. Use IconButton when your button is icon-only; use Button for label-plus-icon.
|
|
30
|
+
- **aria-label is TypeScript-enforced** — the library makes it impossible to forget. This is the key accessibility constraint for icon-only buttons.
|
|
31
|
+
- **IconProvider cascade:** The `icon` prop expects `<Icon icon={...} />` — Icon auto-sizes based on IconButton's `size` via IconProvider context (no need to pass size to the nested Icon).
|
|
32
|
+
- **Inside ButtonGroup:** Inherits variant/color/size/weight from ButtonGroup context just like Button does. Useful for icon-only toolbars.
|
|
33
|
+
- **IconGroup vs ButtonGroup of IconButtons:** IconGroup is for static icon clusters (not interactive, or for decorative displays). ButtonGroup of IconButtons is for interactive toolbars where each icon is a button.
|
|
34
|
+
|
|
28
35
|
## Gotchas
|
|
29
36
|
- aria-label is enforced by TypeScript — you MUST provide it
|
|
30
37
|
- Prefer IconButton over Button with size="icon-*" for icon-only buttons
|
|
@@ -28,6 +28,13 @@ import { IconProvider, useIconContext } from '@devalok/shilp-sutra/ui'
|
|
|
28
28
|
const { size, stroke } = useIconContext()
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
+
## Composability
|
|
32
|
+
- **Low-level context primitive** — rarely used directly. Most consumers want IconGroup (which wraps IconProvider + layout) or just nest Icons inside Button/Input/Badge (which provide their own IconProvider).
|
|
33
|
+
- **When to reach for IconProvider manually:** Building a custom container that hosts many Icons (a dashboard tile row, a custom toolbar, a nav rail) and you want consistent icon sizing without repeating props. IconProvider is the escape hatch for that.
|
|
34
|
+
- **Consumer contract:** `useIconContext()` returns `{}` (empty) when no provider is present — your consumer code MUST handle that (fall back to a default size/stroke). Don't assume a provider exists.
|
|
35
|
+
- **Memoization:** The provider memoizes its value, so consumers don't re-render unless size or stroke actually change. Safe to use in tight loops / frequently-re-rendering trees.
|
|
36
|
+
- **Composes down:** IconProvider nests — inner providers override outer ones. A `<Button size="lg"><IconProvider size="xs">...</IconProvider></Button>` overrides the button's icon sizing.
|
|
37
|
+
|
|
31
38
|
## Gotchas
|
|
32
39
|
- Used internally by IconGroup and Button to propagate icon sizing to children
|
|
33
40
|
- If no provider is present, `useIconContext()` returns `{}` (empty object) — consumers should fall back to defaults
|
|
@@ -26,6 +26,15 @@
|
|
|
26
26
|
</IconGroup>
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
+
## Composability
|
|
30
|
+
- **IconProvider wrapper + layout** — IconGroup is IconProvider plus a flex container. All child `<Icon>` elements auto-size via the provider. Don't pass size to nested Icons.
|
|
31
|
+
- **IconGroup vs ButtonGroup-of-IconButtons:**
|
|
32
|
+
- IconGroup = static icon displays (legend rows, feature lists, decorative clusters). Not interactive.
|
|
33
|
+
- ButtonGroup of IconButtons = interactive toolbar where each icon is a button.
|
|
34
|
+
- If you want a formatting toolbar (interactive), use ButtonGroup or ToggleGroup, not IconGroup.
|
|
35
|
+
- **Toolbar mode:** Set `role="toolbar"` + provide `label` to give the group a semantic role. Only then does `label` become `aria-label` on the container.
|
|
36
|
+
- **Gap semantics:** tight=2px, default=4px, loose=8px — smaller values than ButtonGroup or Stack because icons are visually dense.
|
|
37
|
+
|
|
29
38
|
## Gotchas
|
|
30
39
|
- Wraps children in an IconProvider — all child Icons inherit size/stroke from the group
|
|
31
40
|
- `label` is only applied as `aria-label` when `role="toolbar"` is set
|
|
@@ -30,6 +30,13 @@
|
|
|
30
30
|
<Icon icon={IconPlus} state="success" />
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
+
## Composability
|
|
34
|
+
- **IconContext consumption** — Icon auto-reads `size` and `stroke` from the nearest IconProvider. Button, IconButton, IconGroup, Input (startSection/endSection), Badge, NumberInput, and more all wrap their children in IconProvider so nested Icons auto-size. Explicit props always override context.
|
|
35
|
+
- **Use Tabler icon components** — the `icon` prop expects a ForwardRef SVG component (the shape Tabler React exports). Any icon lib following that shape works, but Tabler is the standard.
|
|
36
|
+
- **Label for a11y:** Without `label`, Icon is `aria-hidden="true"` (decorative — appropriate inside labeled buttons). With `label`, renders `role="img"` + aria-label + `<title>` for standalone icons.
|
|
37
|
+
- **State overrides animate:** When both are set, state wins. `state="loading"` renders a bare Spinner; success/error render animated glyphs via Framer Motion.
|
|
38
|
+
- **Reduced motion:** All animations respect `prefers-reduced-motion` — animate props fall back to static render.
|
|
39
|
+
|
|
33
40
|
## Gotchas
|
|
34
41
|
- Without `label`, the icon renders `aria-hidden="true"` (decorative)
|
|
35
42
|
- With `label`, the icon renders `role="img"` with `aria-label` and a `<title>` element
|
|
@@ -5,17 +5,32 @@
|
|
|
5
5
|
- Category: ui
|
|
6
6
|
|
|
7
7
|
## Props
|
|
8
|
-
|
|
8
|
+
### InputOTP
|
|
9
|
+
maxLength: number (REQUIRED) — total number of slots
|
|
10
|
+
value: string (controlled value; defaults to empty string)
|
|
11
|
+
onChange: (value: string) => void
|
|
12
|
+
onComplete: (value: string) => void — fires when all slots filled
|
|
13
|
+
pattern: string | RegExp — restrict input (e.g. `REGEXP_ONLY_DIGITS`)
|
|
14
|
+
state: "default" | "error" (error adds red border; auto-inherits from FormField)
|
|
15
|
+
size: "sm" | "md" | "lg" (slot dimensions — propagates to InputOTPSlot via context)
|
|
16
|
+
disabled: boolean
|
|
17
|
+
containerClassName: string (on the outer group container — separate from inner input's className)
|
|
18
|
+
|
|
19
|
+
### InputOTPSlot
|
|
20
|
+
index: number (REQUIRED, 0-based) — which position this slot renders
|
|
9
21
|
|
|
10
22
|
## Compound Components
|
|
11
|
-
InputOTP (root)
|
|
12
|
-
InputOTPGroup
|
|
13
|
-
InputOTPSlot (index: number, REQUIRED)
|
|
14
|
-
InputOTPSeparator
|
|
23
|
+
InputOTP (root — maxLength, value, onChange, size propagated via context)
|
|
24
|
+
InputOTPGroup (visual group of slots)
|
|
25
|
+
InputOTPSlot (index: number, REQUIRED — reads size from context)
|
|
26
|
+
InputOTPSeparator (visual separator, e.g. between two groups of 3)
|
|
27
|
+
|
|
28
|
+
## Defaults
|
|
29
|
+
size="md", state="default"
|
|
15
30
|
|
|
16
31
|
## Example
|
|
17
32
|
```jsx
|
|
18
|
-
<InputOTP maxLength={6}>
|
|
33
|
+
<InputOTP maxLength={6} onComplete={verifyCode}>
|
|
19
34
|
<InputOTPGroup>
|
|
20
35
|
<InputOTPSlot index={0} />
|
|
21
36
|
<InputOTPSlot index={1} />
|
|
@@ -30,8 +45,15 @@
|
|
|
30
45
|
</InputOTP>
|
|
31
46
|
```
|
|
32
47
|
|
|
48
|
+
## Composability
|
|
49
|
+
- Size propagates from InputOTP → InputOTPSlot via `InputOTPSizeContext` — don't set size on individual slots.
|
|
50
|
+
- Inside `<FormField>`: auto-inherits state, aria-describedby, aria-required from context. Explicit `state="error"` overrides.
|
|
51
|
+
- Underlying library is `input-otp` (OTPInput) — all standard library props (pattern, inputMode, autoFocus, etc.) pass through.
|
|
52
|
+
|
|
33
53
|
## Gotchas
|
|
34
|
-
- Each InputOTPSlot requires an `index` prop (0-based)
|
|
54
|
+
- Each InputOTPSlot requires an `index` prop (0-based) matching its position
|
|
55
|
+
- `onComplete` only fires when ALL slots are filled — use `onChange` for per-character reactivity
|
|
56
|
+
- `containerClassName` (outer visual group) is distinct from `className` (the hidden input element itself)
|
|
35
57
|
|
|
36
58
|
## Changes
|
|
37
59
|
### v0.18.0
|
|
@@ -14,8 +14,6 @@
|
|
|
14
14
|
startSectionType: 'icon' | 'label' (section display type — auto-inferred from content)
|
|
15
15
|
endSectionType: 'icon' | 'label' (section display type — auto-inferred from content)
|
|
16
16
|
wrapperClassName: string (classes for the wrapper div — border, bg, ring)
|
|
17
|
-
startIcon: ReactNode (@deprecated — use startSection)
|
|
18
|
-
endIcon: ReactNode (@deprecated — use endSection)
|
|
19
17
|
(plus all standard HTML input attributes except native "size")
|
|
20
18
|
|
|
21
19
|
## Types
|
|
@@ -32,6 +30,13 @@
|
|
|
32
30
|
<Input endSection=".00" endSectionType="label" startSection={<Icon icon={IconCurrencyDollar} />} placeholder="0" />
|
|
33
31
|
```
|
|
34
32
|
|
|
33
|
+
## Composability
|
|
34
|
+
- **FormField auto-consumption:** Inside a `<FormField>`, Input auto-inherits `state`, `aria-describedby` (linked to FormHelperText), `aria-invalid` (when state="error"), `aria-required`. Explicit props on Input override context.
|
|
35
|
+
- **IconProvider cascade:** Icons in `startSection` / `endSection` are auto-sized via IconProvider per the input's `size` (xs → sm icon, sm → sm icon, md → md icon, lg → md icon). Don't pass explicit size to nested `<Icon>`.
|
|
36
|
+
- **Container-first architecture (v0.29.0+):** Border, background, focus ring all live on the wrapper div (accessed via `wrapperClassName`). The raw `<input>` inside is transparent. `className` goes to the input element; `wrapperClassName` goes to the wrapper.
|
|
37
|
+
- **Section types:** Strings in startSection/endSection auto-render as `label` (tinted bg + border separator); React elements auto-render as `icon` (fixed-width centered cell). Override via `startSectionType` / `endSectionType`.
|
|
38
|
+
- **Label pairing:** Always pair with `<Label htmlFor="x" />` + `<Input id="x" />` or wrap both in a `<label>` — FormField doesn't auto-wire the visual label to the input.
|
|
39
|
+
|
|
35
40
|
## Gotchas
|
|
36
41
|
- HTML native "size" attribute is excluded — use CSS width instead
|
|
37
42
|
- state="error" sets aria-invalid automatically
|
|
@@ -43,6 +48,10 @@
|
|
|
43
48
|
- Section type is auto-inferred: strings default to `'label'` (tinted bg + border), React elements default to `'icon'` (fixed-width centered). Override with `startSectionType`/`endSectionType`.
|
|
44
49
|
|
|
45
50
|
## Changes
|
|
51
|
+
### v0.38.0
|
|
52
|
+
- **Removed** (BREAKING) deprecated `startIcon` / `endIcon` props. Use `startSection` / `endSection`.
|
|
53
|
+
- **Removed** (BREAKING) deprecated `inputVariants` export. Use `inputWrapperVariants`.
|
|
54
|
+
|
|
46
55
|
### v0.29.0
|
|
47
56
|
- **Changed** v2 rewrite: container-first architecture with wrapper div holding focus ring
|
|
48
57
|
- **Added** `xs` size (28px height)
|
|
@@ -14,8 +14,14 @@
|
|
|
14
14
|
<Label htmlFor="email" required>Email Address</Label>
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
+
## Composability
|
|
18
|
+
- **Label is NOT auto-wired by FormField** — you must explicitly pair `<Label htmlFor="x" />` with the matching `<Input id="x" />` (or Checkbox/Radio/Switch/Select). Screen-reader label association depends on this.
|
|
19
|
+
- Radix Label primitive underneath — clicking the Label focuses its associated control, which is why the `htmlFor`/`id` pairing matters.
|
|
20
|
+
- `required={true}` only renders the red asterisk; it does NOT set `aria-required` on the associated control (that comes from FormField context on the control itself).
|
|
21
|
+
- Works with any input-like component in the library — pair with Input, Textarea, NumberInput, Checkbox, Radio, Switch, Select, Combobox, Autocomplete.
|
|
22
|
+
|
|
17
23
|
## Gotchas
|
|
18
|
-
- Use with FormField for automatic aria wiring
|
|
24
|
+
- Use with FormField for automatic aria wiring on the control (but the Label-to-control association is always manual)
|
|
19
25
|
|
|
20
26
|
## Changes
|
|
21
27
|
### v0.2.0
|
|
@@ -18,8 +18,15 @@
|
|
|
18
18
|
<Link asChild><NextLink href="/about">About</NextLink></Link>
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
+
## Composability
|
|
22
|
+
- **Framework router integration via `asChild`:** `<Link asChild><NextLink href="/foo">...</NextLink></Link>` — Link's styling transfers to the child (NextLink/react-router Link/any `<a>`-like component) while preserving the child's navigation semantics. Don't use `asChild` for plain anchors — it's unnecessary and adds a layer.
|
|
23
|
+
- **inline vs block:** Default `inline` (display: inline) so Link composes naturally inside paragraphs and mixed text. Set `inline={false}` for full-width link regions (card wrappers, list items).
|
|
24
|
+
- No context, no cascade. Safe to use anywhere.
|
|
25
|
+
- For icon + text links, nest Icon inside — Link doesn't auto-size via IconProvider (unlike Button), so pass an explicit `size` to the Icon.
|
|
26
|
+
|
|
21
27
|
## Gotchas
|
|
22
28
|
- Use asChild with framework-specific Link components (e.g. Next.js Link)
|
|
29
|
+
- Don't nest interactive elements (buttons, form controls) inside a Link — that's invalid HTML and breaks screen-reader navigation
|
|
23
30
|
|
|
24
31
|
## Changes
|
|
25
32
|
### v0.18.0
|
|
@@ -30,8 +30,17 @@
|
|
|
30
30
|
</Menubar>
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
+
## Composability
|
|
34
|
+
- Radix Menubar — shares the item-variant vocabulary with DropdownMenu and ContextMenu (checkbox items, radio groups, sub-menus, labels, separators, shortcuts).
|
|
35
|
+
- **Key difference from DropdownMenu:** Menubar is the horizontal top-bar-menu pattern (File / Edit / View / Help). Multiple MenubarMenu children sit side-by-side at the root; opening one closes the others, and arrow keys move between them.
|
|
36
|
+
- **Typical use:** Desktop app-like UIs where the menu is always visible at the top (code editors, design tools). For a single collapsed trigger, use DropdownMenu instead.
|
|
37
|
+
- **Controlled open:** Pass `value` + `onValueChange` to Menubar root to control which MenubarMenu is open (value = menu's `value` prop or falsy for none).
|
|
38
|
+
- **Portal + z-index:** z-popover (1400).
|
|
39
|
+
|
|
33
40
|
## Gotchas
|
|
34
41
|
- Follows the standard Radix Menubar compound pattern
|
|
42
|
+
- Don't use Menubar for a single dropdown — use DropdownMenu instead
|
|
43
|
+
- MenubarShortcut is decorative (same as DropdownMenuShortcut) — bind shortcuts separately
|
|
35
44
|
|
|
36
45
|
## Changes
|
|
37
46
|
### v0.18.0
|
|
@@ -31,8 +31,17 @@
|
|
|
31
31
|
</NavigationMenu>
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
+
## Composability
|
|
35
|
+
- **Radix NavigationMenu** underneath — keyboard model (Tab between items, Arrow for panel navigation, Esc to close) is pre-wired.
|
|
36
|
+
- **NavigationMenuLink vs NavigationMenuTrigger:** Use Link for simple nav items (no dropdown); use Trigger + Content for items with a panel of sub-links.
|
|
37
|
+
- **NavigationMenuViewport** is the animated container that holds the active Content panel — it auto-positions below the triggers. NavigationMenuIndicator is the small arrow/caret pointing from trigger to viewport.
|
|
38
|
+
- **Purpose:** Top-level site navigation (Products, Solutions, Resources) with rich dropdowns containing links grouped by category. Don't use for context menus (use DropdownMenu) or action toolbars (use ButtonGroup).
|
|
39
|
+
- **Router integration:** Use `<NavigationMenuLink asChild><NextLink href="/...">...</NextLink></NavigationMenuLink>` for framework-specific Link components.
|
|
40
|
+
- **Portal + z-popover (1400)** — content panels portal to body and stack above Dialog/Sheet.
|
|
41
|
+
|
|
34
42
|
## Gotchas
|
|
35
43
|
- Uses Framer Motion for enter/exit animations (v0.18.0)
|
|
44
|
+
- NavigationMenu is NOT for sidebar nav — use Sidebar; NOT for mobile nav — use Sheet or BottomNavbar
|
|
36
45
|
|
|
37
46
|
## Changes
|
|
38
47
|
### v0.18.0
|
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
## Props
|
|
8
8
|
value: number (default: 0)
|
|
9
9
|
onValueChange: (value: number) => void
|
|
10
|
+
size: "xs" | "sm" | "md" | "lg"
|
|
11
|
+
state: "default" | "error" | "warning" | "success" (border color)
|
|
10
12
|
min: number
|
|
11
13
|
max: number
|
|
12
14
|
step: number (default: 1)
|
|
@@ -14,6 +16,8 @@
|
|
|
14
16
|
|
|
15
17
|
## Defaults
|
|
16
18
|
value: 0
|
|
19
|
+
size: "md"
|
|
20
|
+
state: "default"
|
|
17
21
|
step: 1
|
|
18
22
|
min: Number.MIN_SAFE_INTEGER
|
|
19
23
|
max: Number.MAX_SAFE_INTEGER
|
|
@@ -23,6 +27,12 @@
|
|
|
23
27
|
<NumberInput value={qty} onValueChange={setQty} min={1} max={99} />
|
|
24
28
|
```
|
|
25
29
|
|
|
30
|
+
## Composability
|
|
31
|
+
- **FormField auto-consumption:** Inside `<FormField>`, inherits `aria-describedby`, `aria-invalid`, `aria-required`. `state` from FormField context drives the border color unless overridden.
|
|
32
|
+
- **IconProvider cascade:** The `+` and `−` stepper icons auto-size via IconProvider per the input size (xs/sm/md/lg → xs/sm/sm/md icons).
|
|
33
|
+
- **Controlled only** (design choice): no `defaultValue` — pair `value` + `onValueChange` every time. The min/max bounds disable the stepper buttons automatically when reached.
|
|
34
|
+
- **Label pairing:** Manual via `<Label htmlFor="x" />` + `<NumberInput id="x" />`.
|
|
35
|
+
|
|
26
36
|
## Gotchas
|
|
27
37
|
- Controlled only — buttons won't work without onValueChange
|
|
28
38
|
|
|
@@ -36,8 +36,18 @@
|
|
|
36
36
|
</PaginationRoot>
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
+
## Composability
|
|
40
|
+
- **Two APIs in one component:**
|
|
41
|
+
- Low-level compound (PaginationRoot + Content + Item + Link + Previous/Next/Ellipsis) — full control over rendering
|
|
42
|
+
- High-level `PaginationNav` — pass `totalPages` + `currentPage` + `onPageChange` and it renders the whole thing using `generatePagination`
|
|
43
|
+
- **Use PaginationNav for 95% of cases.** Reach for the compound API when you need custom page-button rendering (e.g. pagination with input "jump to page" or custom ellipsis handling).
|
|
44
|
+
- **generatePagination** utility — pass `current`, `total`, `siblingCount` to get the `[1, ..., 5, 6, 7, ..., 20]` structure. Use it standalone if rendering pagination elsewhere (e.g. in a DataTable's footer).
|
|
45
|
+
- **Router integration:** Each PaginationLink accepts `asChild` — wrap with NextLink/react-router Link for URL-based pagination.
|
|
46
|
+
- **DataTable already has built-in pagination** — don't add Pagination separately; use DataTable's `pagination` prop.
|
|
47
|
+
|
|
39
48
|
## Gotchas
|
|
40
49
|
- Root component is PaginationRoot (NOT Pagination)
|
|
50
|
+
- PaginationNav is the convenience wrapper — prefer it unless you need custom rendering
|
|
41
51
|
|
|
42
52
|
## Changes
|
|
43
53
|
### v0.1.1
|
|
@@ -18,8 +18,17 @@
|
|
|
18
18
|
</Popover>
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
+
## Composability
|
|
22
|
+
- Built on Radix Popover — accepts `open`, `onOpenChange`, `defaultOpen`, `modal`.
|
|
23
|
+
- **Trigger:** `<PopoverTrigger asChild>` wraps any focusable element.
|
|
24
|
+
- **PopoverAnchor:** Optional — decouples the visual anchor from the interactive trigger. Useful when the trigger is small (e.g. an icon button) but you want the popover to position relative to a larger surrounding element.
|
|
25
|
+
- **Positioning:** PopoverContent accepts `side` (top / right / bottom / left), `align` (start / center / end), `sideOffset`, and `collisionPadding` — all forwarded to Floating UI via Radix.
|
|
26
|
+
- **Portal rendering:** Content portals to body; z-index is `z-popover` (1400) — above Dialog (`z-dialog`) so nested popovers-in-dialogs work correctly.
|
|
27
|
+
- **Not for tooltips** — use Tooltip for transient hover-label content; Popover is for interactive content (forms, menus, pickers).
|
|
28
|
+
|
|
21
29
|
## Gotchas
|
|
22
30
|
- Uses Framer Motion for enter/exit animations (v0.18.0)
|
|
31
|
+
- If content needs to be modal (backdrop, focus trap), pass `modal={true}` to the root Popover
|
|
23
32
|
|
|
24
33
|
## Changes
|
|
25
34
|
### v0.18.0
|
|
@@ -35,6 +35,13 @@
|
|
|
35
35
|
/>
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
+
## Composability
|
|
39
|
+
- **SVG-based circular progress** — pure SVG, no chart library dependency.
|
|
40
|
+
- **MultiProgressRing** — concentric rings for multi-metric visualization (Apple Activity style: Move + Exercise + Stand). Don't stack more than 4-5 rings at a size; the innermost rings become too thin. Size auto-allocates ring stroke; pass `size="lg"` for more room.
|
|
41
|
+
- **ProgressRing vs Progress:** Use ProgressRing for dashboard tiles, at-a-glance status, multi-metric visualizations. Use Progress for linear "percentage filled" UIs.
|
|
42
|
+
- **`showValue={true}`** renders the percentage in the center (ProgressRing only). For MultiProgressRing, use external labels since center would conflict with multiple values.
|
|
43
|
+
- **Label for a11y:** `label` falls back to `"{n}% progress"` so screen readers always get meaningful context.
|
|
44
|
+
|
|
38
45
|
## Gotchas
|
|
39
46
|
- Uses Framer Motion for the animated fill — not server-safe
|
|
40
47
|
- Value is clamped to `[0, max]` internally
|
|
@@ -22,6 +22,13 @@
|
|
|
22
22
|
<Progress size="sm" /> {/* indeterminate */}
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
+
## Composability
|
|
26
|
+
- **Radix Progress** underneath — `value` (determinate) or absent (indeterminate). Standard ARIA: role=progressbar, aria-valuenow/min/max.
|
|
27
|
+
- **autoColor semantic signal:** Maps value thresholds to color so consumers don't have to manually compute: 0–59=default, 60–84=warning, 85–100=success, >100=error. Useful for storage meters, budget trackers, goal progress.
|
|
28
|
+
- **Indeterminate:** Omit `value` to get a continuous animated indeterminate bar. Use for unknown-duration loads.
|
|
29
|
+
- **Progress vs ProgressRing:** Progress is a linear bar (horizontal). ProgressRing is circular. ProgressRing offers multi-ring stacked variants; Progress doesn't.
|
|
30
|
+
- **Inside Card/StatCard:** StatCard has a `progress` prop that renders a thin inline version — use that inside StatCards instead of a separate Progress.
|
|
31
|
+
|
|
25
32
|
## Gotchas
|
|
26
33
|
- Omit value (or pass undefined) for indeterminate animation
|
|
27
34
|
- `autoColor` overrides `color` when `value` is set — do not pass both unless you want autoColor to win
|
|
@@ -4,9 +4,26 @@
|
|
|
4
4
|
- Server-safe: No
|
|
5
5
|
- Category: ui
|
|
6
6
|
|
|
7
|
+
## Props
|
|
8
|
+
### RadioGroup
|
|
9
|
+
value: string (controlled)
|
|
10
|
+
onValueChange: (value: string) => void
|
|
11
|
+
defaultValue: string
|
|
12
|
+
disabled: boolean (propagates to all items)
|
|
13
|
+
orientation: "horizontal" | "vertical"
|
|
14
|
+
name: string (form name for all items)
|
|
15
|
+
|
|
16
|
+
### RadioGroupItem
|
|
17
|
+
value: string (REQUIRED — what's selected when this item is checked)
|
|
18
|
+
size: "sm" | "md" | "lg"
|
|
19
|
+
disabled: boolean (item-level override)
|
|
20
|
+
|
|
7
21
|
## Compound Components
|
|
8
|
-
RadioGroup (root
|
|
9
|
-
RadioGroupItem (value
|
|
22
|
+
RadioGroup (root — value, onValueChange, defaultValue, disabled propagated)
|
|
23
|
+
RadioGroupItem (value REQUIRED, size/disabled individually overridable)
|
|
24
|
+
|
|
25
|
+
## Defaults
|
|
26
|
+
RadioGroupItem size="md"
|
|
10
27
|
|
|
11
28
|
## Example
|
|
12
29
|
```jsx
|
|
@@ -22,9 +39,17 @@
|
|
|
22
39
|
</RadioGroup>
|
|
23
40
|
```
|
|
24
41
|
|
|
42
|
+
## Composability
|
|
43
|
+
- Radix RadioGroup — keyboard navigation (arrow keys to move between items, space to select) is pre-wired.
|
|
44
|
+
- **RadioGroup propagates** `disabled` to every RadioGroupItem; items can opt back in with `disabled={false}` for granular control (rare).
|
|
45
|
+
- **Labels:** RadioGroupItem has no intrinsic label — pair each with `<Label htmlFor="x" />` + `<RadioGroupItem id="x" value="..." />`. Screen readers announce the group label (from FormField or aria-labelledby on RadioGroup) plus each item's label.
|
|
46
|
+
- **Form libraries:** RadioGroup works with react-hook-form via Controller (onValueChange maps to field.onChange). The `name` prop puts a hidden form input per item for native form serialization.
|
|
47
|
+
- **FormField integration:** RadioGroup does NOT auto-consume FormField error state. For error visuals, style the RadioGroup surround (e.g. via aria-invalid on a wrapping fieldset) — individual radios don't show red borders the way Inputs do.
|
|
48
|
+
|
|
25
49
|
## Gotchas
|
|
26
50
|
- Each RadioGroupItem needs a unique `value` prop
|
|
27
51
|
- Pair each item with a Label for accessibility
|
|
52
|
+
- RadioGroup does NOT auto-inherit FormField error state — handle error styling at the group level
|
|
28
53
|
|
|
29
54
|
## Changes
|
|
30
55
|
### v0.4.2
|
|
@@ -26,8 +26,16 @@
|
|
|
26
26
|
/>
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
+
## Composability
|
|
30
|
+
- Purpose-built variant of Input for search — renders the leading search icon and a contextual clear/loading trailing slot. For anything more custom, use Input with explicit `startSection` / `endSection`.
|
|
31
|
+
- **`onClear` makes the X button appear** only when `value` is non-empty. Pair them so users can reset.
|
|
32
|
+
- **`loading={true}` swaps the clear button for a spinner** with `aria-busy="true"` on the input — useful for debounced/async search.
|
|
33
|
+
- Doesn't auto-consume FormField (no `state` prop) — wrap a regular Input inside FormField for validated search fields.
|
|
34
|
+
- Keyboard: Escape auto-triggers `onClear` when wired (handled via `type="search"`'s native behavior on most browsers).
|
|
35
|
+
|
|
29
36
|
## Gotchas
|
|
30
37
|
- HTML native "size" attribute is excluded — use CSS width instead
|
|
38
|
+
- Clear button only appears when both `onClear` is provided AND `value` is non-empty
|
|
31
39
|
|
|
32
40
|
## Changes
|
|
33
41
|
### v0.15.0
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
- Category: ui
|
|
6
6
|
|
|
7
7
|
## Props
|
|
8
|
-
size: "sm" | "md" | "lg"
|
|
9
|
-
variant: "
|
|
8
|
+
size: "sm" | "md" | "lg"
|
|
9
|
+
variant: "default" | "solid"
|
|
10
10
|
options: SegmentedControlOption[] (REQUIRED)
|
|
11
11
|
selectedId: string (REQUIRED)
|
|
12
12
|
onSelect: (id: string) => void (REQUIRED)
|
|
@@ -14,14 +14,18 @@
|
|
|
14
14
|
|
|
15
15
|
## Types
|
|
16
16
|
SegmentedControlOption = { id: string, text: string, icon?: ComponentType<{ className?: string }> }
|
|
17
|
-
SegmentedControlSize = 'sm' | 'md' | 'lg'
|
|
18
|
-
SegmentedControlVariant = '
|
|
17
|
+
SegmentedControlSize = 'sm' | 'md' | 'lg'
|
|
18
|
+
SegmentedControlVariant = 'default' | 'solid'
|
|
19
|
+
|
|
20
|
+
## Defaults
|
|
21
|
+
size: "md"
|
|
22
|
+
variant: "default"
|
|
19
23
|
|
|
20
24
|
## Example
|
|
21
25
|
```jsx
|
|
22
26
|
<SegmentedControl
|
|
23
27
|
size="md"
|
|
24
|
-
variant="
|
|
28
|
+
variant="default"
|
|
25
29
|
options={[
|
|
26
30
|
{ id: 'list', text: 'List' },
|
|
27
31
|
{ id: 'grid', text: 'Grid' },
|
|
@@ -31,16 +35,27 @@
|
|
|
31
35
|
/>
|
|
32
36
|
```
|
|
33
37
|
|
|
38
|
+
## Composability
|
|
39
|
+
- **Data-driven, not compound** — unlike Tabs/ToggleGroup, SegmentedControl takes an `options` array rather than children. This makes it easier to render from a list but harder to customize per-option styling; use Tabs if you need compound children.
|
|
40
|
+
- **When to use vs Tabs:** SegmentedControl is for mutually-exclusive VIEW-MODE toggles (List/Grid/Kanban) — short labels, no associated content panel. Tabs is for content switching where each tab has a corresponding TabsContent. Both render `role="tablist"`.
|
|
41
|
+
- **Option icons** auto-size based on the `size` prop — don't set explicit icon sizes.
|
|
42
|
+
- Fully controlled — there's no `defaultSelectedId`. Manage state in parent.
|
|
43
|
+
- Built from scratch (no Radix primitive) — standard HTML buttons with `aria-selected` and roving tabindex.
|
|
44
|
+
|
|
34
45
|
## Gotchas
|
|
35
46
|
- Controlled only — selectedId + onSelect are required
|
|
36
47
|
- Uses data-driven API (options prop), not compound children
|
|
48
|
+
- Use Tabs (not SegmentedControl) when you need associated content panels per option
|
|
37
49
|
|
|
38
50
|
## Changes
|
|
51
|
+
### v0.38.0
|
|
52
|
+
- **Removed** (BREAKING) deprecated `variant="accent"` alias. Use `variant="solid"`.
|
|
53
|
+
|
|
39
54
|
### v0.18.0
|
|
40
55
|
- **Fixed** `bg-interactive` changed to `bg-accent-9`, `bg-field` changed to `bg-surface-3` (OKLCH migration)
|
|
41
56
|
|
|
42
57
|
### v0.4.2
|
|
43
|
-
- **Changed** (BREAKING) `color` prop renamed to `variant`
|
|
58
|
+
- **Changed** (BREAKING) `color` prop renamed to `variant`
|
|
44
59
|
|
|
45
60
|
### v0.1.1
|
|
46
61
|
- **Fixed** `tabIndex={0}` changed to `tabIndex={-1}` on tablist wrapper — fixes double-focus keyboard navigation bug
|
|
@@ -6,11 +6,13 @@
|
|
|
6
6
|
|
|
7
7
|
## Props
|
|
8
8
|
### SelectTrigger
|
|
9
|
-
|
|
9
|
+
variant: "default" | "outline" | "ghost"
|
|
10
|
+
color: "default" | "error" | "success" | "warning" (sets aria-invalid when error)
|
|
11
|
+
size: "xs" | "sm" | "md" | "lg"
|
|
10
12
|
|
|
11
13
|
## Compound Components
|
|
12
14
|
Select (root — value, onValueChange, defaultValue)
|
|
13
|
-
SelectTrigger (size
|
|
15
|
+
SelectTrigger (variant/color/size go HERE, not on Select root)
|
|
14
16
|
SelectValue (placeholder)
|
|
15
17
|
SelectContent
|
|
16
18
|
SelectGroup (optional grouping)
|
|
@@ -19,7 +21,7 @@
|
|
|
19
21
|
SelectSeparator
|
|
20
22
|
|
|
21
23
|
## Defaults
|
|
22
|
-
SelectTrigger
|
|
24
|
+
SelectTrigger variant="default", color="default", size="md"
|
|
23
25
|
|
|
24
26
|
## Example
|
|
25
27
|
```jsx
|
|
@@ -34,6 +36,15 @@
|
|
|
34
36
|
</Select>
|
|
35
37
|
```
|
|
36
38
|
|
|
39
|
+
## Composability
|
|
40
|
+
- **Radix Select** underneath — `value`/`onValueChange`/`defaultValue`/`open`/`onOpenChange` standard state.
|
|
41
|
+
- **Styling props live on SelectTrigger, not Select root.** The Select root owns value/state; the Trigger owns appearance (variant/color/size). Setting `<Select size="lg">` silently does nothing — TypeScript won't catch it.
|
|
42
|
+
- **SelectItem requires `value`** — unique within the Select. Labels are the children of SelectItem.
|
|
43
|
+
- **SelectGroup + SelectLabel:** Organize options into sections with a non-interactive section heading.
|
|
44
|
+
- **FormField integration:** Set `color="error"` on SelectTrigger for error visuals. Not auto-consumed from FormField (same as Checkbox/Radio — form-library convention for select controls).
|
|
45
|
+
- **Portal + z-popover (1400):** content portals to body, stacks above Dialog/Sheet/other overlays.
|
|
46
|
+
- **Select vs Combobox vs Autocomplete:** Select = short fixed list, click-to-open, no typeahead. Combobox = searchable, forced selection. Autocomplete = searchable, free text. Pick by list size and search need.
|
|
47
|
+
|
|
37
48
|
## Gotchas
|
|
38
49
|
- Size goes on SelectTrigger, NOT on Select root
|
|
39
50
|
- `<Select size="lg">` is silently ignored (no TypeScript error)
|
|
@@ -18,8 +18,15 @@
|
|
|
18
18
|
<Separator orientation="vertical" className="h-6" />
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
+
## Composability
|
|
22
|
+
- Radix Separator — no context, no cascade. Drop it anywhere; it inherits its stretch dimension from its parent container (full width for horizontal, full height for vertical — but vertical needs an explicit height from the parent flexbox).
|
|
23
|
+
- `decorative={true}` (default) sets `role="none"` — screen readers skip it. Set `decorative={false}` for semantic separators (e.g. between navigation sections) so screen readers announce the boundary.
|
|
24
|
+
- Gradient variants compose cleanly with any surface color — they use `bg-transparent` + inline linear-gradient, so the underlying `bg-*` of the parent shows through.
|
|
25
|
+
- Common inside Menu/DropdownMenu/Sheet components; their internal *Separator subcomponents already wrap this one.
|
|
26
|
+
|
|
21
27
|
## Gotchas
|
|
22
28
|
- When decorative is true, the separator is hidden from screen readers
|
|
29
|
+
- Vertical separator needs an explicit height from the parent flex container (`h-6`, `h-full`, etc.)
|
|
23
30
|
|
|
24
31
|
## Changes
|
|
25
32
|
### v0.22.0
|
|
@@ -32,8 +32,18 @@
|
|
|
32
32
|
</Sheet>
|
|
33
33
|
```
|
|
34
34
|
|
|
35
|
+
## Defaults
|
|
36
|
+
side="right"
|
|
37
|
+
|
|
38
|
+
## Composability
|
|
39
|
+
- Same primitives as Dialog — open/onOpenChange/defaultOpen, trigger asChild, portal rendering, focus trap.
|
|
40
|
+
- `side` controls both the enter-from edge and the layout: top/bottom sides full-width, left/right sides ~75% of viewport (capped at sm breakpoint).
|
|
41
|
+
- Use Sheet for side-anchored drawers (settings panels, mobile navigation); use Dialog for centered modals.
|
|
42
|
+
- On mobile (`isMobile` from use-mobile), consider Sheet as the mobile-friendly equivalent of a Dialog/Popover — more thumb-reachable on tall screens.
|
|
43
|
+
|
|
35
44
|
## Gotchas
|
|
36
45
|
- Uses Framer Motion for slide enter/exit animations (v0.18.0)
|
|
46
|
+
- SheetTitle is required for accessibility (same rule as Dialog)
|
|
37
47
|
|
|
38
48
|
## Changes
|
|
39
49
|
### v0.18.0
|