@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.
Files changed (183) hide show
  1. package/MIGRATION.md +67 -0
  2. package/dist/_chunks/tiptap.js +977 -968
  3. package/dist/_chunks/tiptap.js.map +1 -1
  4. package/dist/composed/index.d.ts +0 -2
  5. package/dist/composed/index.d.ts.map +1 -1
  6. package/dist/composed/index.js +5 -6
  7. package/dist/composed/inline-edit.d.ts.map +1 -1
  8. package/dist/composed/inline-edit.js +42 -40
  9. package/dist/composed/inline-edit.js.map +1 -1
  10. package/dist/hooks/index.d.ts +2 -2
  11. package/dist/hooks/index.d.ts.map +1 -1
  12. package/dist/hooks/index.js +0 -1
  13. package/dist/test-utils/conformance.d.ts +37 -0
  14. package/dist/test-utils/conformance.d.ts.map +1 -0
  15. package/dist/ui/alert.d.ts +1 -2
  16. package/dist/ui/alert.d.ts.map +1 -1
  17. package/dist/ui/alert.js +1 -27
  18. package/dist/ui/alert.js.map +1 -1
  19. package/dist/ui/banner.d.ts +3 -5
  20. package/dist/ui/banner.d.ts.map +1 -1
  21. package/dist/ui/banner.js +13 -13
  22. package/dist/ui/banner.js.map +1 -1
  23. package/dist/ui/index.d.ts +1 -1
  24. package/dist/ui/index.d.ts.map +1 -1
  25. package/dist/ui/index.js +2 -2
  26. package/dist/ui/index.js.map +1 -1
  27. package/dist/ui/input.d.ts +1 -9
  28. package/dist/ui/input.d.ts.map +1 -1
  29. package/dist/ui/input.js +26 -27
  30. package/dist/ui/input.js.map +1 -1
  31. package/dist/ui/segmented-control.d.ts +1 -1
  32. package/dist/ui/segmented-control.d.ts.map +1 -1
  33. package/dist/ui/segmented-control.js +2 -4
  34. package/dist/ui/segmented-control.js.map +1 -1
  35. package/docs/components/_header.md +1 -1
  36. package/docs/components/composed/activity-feed.md +6 -0
  37. package/docs/components/composed/avatar-group.md +8 -0
  38. package/docs/components/composed/bulk-action-bar.md +6 -0
  39. package/docs/components/composed/command-palette.md +7 -0
  40. package/docs/components/composed/confirm-dialog.md +6 -0
  41. package/docs/components/composed/content-card.md +6 -0
  42. package/docs/components/composed/date-picker.md +10 -0
  43. package/docs/components/composed/deadline-indicator.md +7 -0
  44. package/docs/components/composed/emoji-picker.md +8 -0
  45. package/docs/components/composed/empty-state.md +8 -0
  46. package/docs/components/composed/error-boundary.md +7 -0
  47. package/docs/components/composed/file-preview.md +8 -0
  48. package/docs/components/composed/filter-bar.md +8 -0
  49. package/docs/components/composed/form-section.md +6 -0
  50. package/docs/components/composed/global-loading.md +6 -0
  51. package/docs/components/composed/inline-edit.md +8 -0
  52. package/docs/components/composed/loading-skeleton.md +7 -0
  53. package/docs/components/composed/markdown-viewer.md +9 -0
  54. package/docs/components/composed/master-detail.md +7 -0
  55. package/docs/components/composed/member-picker.md +7 -0
  56. package/docs/components/composed/multi-select-popover.md +11 -0
  57. package/docs/components/composed/page-header.md +7 -0
  58. package/docs/components/composed/page-skeletons.md +10 -0
  59. package/docs/components/composed/priority-indicator.md +7 -0
  60. package/docs/components/composed/rich-chat-input.md +12 -0
  61. package/docs/components/composed/rich-text-editor.md +9 -0
  62. package/docs/components/composed/schedule-view.md +8 -0
  63. package/docs/components/composed/simple-tooltip.md +7 -0
  64. package/docs/components/composed/status-badge.md +10 -0
  65. package/docs/components/shell/app-command-palette.md +8 -0
  66. package/docs/components/shell/bottom-navbar.md +12 -0
  67. package/docs/components/shell/command-registry.md +7 -0
  68. package/docs/components/shell/link-context.md +15 -0
  69. package/docs/components/shell/notification-center.md +10 -0
  70. package/docs/components/shell/notification-preferences.md +7 -0
  71. package/docs/components/shell/sidebar.md +11 -0
  72. package/docs/components/shell/top-bar.md +11 -0
  73. package/docs/components/ui/accordion.md +10 -0
  74. package/docs/components/ui/alert-dialog.md +10 -1
  75. package/docs/components/ui/alert.md +13 -2
  76. package/docs/components/ui/aspect-ratio.md +7 -0
  77. package/docs/components/ui/autocomplete.md +7 -0
  78. package/docs/components/ui/avatar.md +10 -0
  79. package/docs/components/ui/badge-group.md +7 -0
  80. package/docs/components/ui/badge-indicator.md +7 -0
  81. package/docs/components/ui/badge.md +12 -0
  82. package/docs/components/ui/banner.md +10 -1
  83. package/docs/components/ui/breadcrumb.md +8 -0
  84. package/docs/components/ui/button-group.md +24 -4
  85. package/docs/components/ui/button-processing.md +5 -0
  86. package/docs/components/ui/button.md +9 -0
  87. package/docs/components/ui/card.md +16 -4
  88. package/docs/components/ui/charts.md +9 -0
  89. package/docs/components/ui/chat.md +9 -0
  90. package/docs/components/ui/checkbox.md +15 -1
  91. package/docs/components/ui/chip.md +9 -0
  92. package/docs/components/ui/code.md +7 -0
  93. package/docs/components/ui/collapsible.md +9 -0
  94. package/docs/components/ui/color-input.md +8 -0
  95. package/docs/components/ui/color-swatch.md +7 -0
  96. package/docs/components/ui/combobox.md +16 -3
  97. package/docs/components/ui/container.md +11 -0
  98. package/docs/components/ui/context-menu.md +8 -0
  99. package/docs/components/ui/data-table-body.md +4 -0
  100. package/docs/components/ui/data-table-bulk-actions.md +4 -0
  101. package/docs/components/ui/data-table-card.md +4 -0
  102. package/docs/components/ui/data-table-context.md +4 -0
  103. package/docs/components/ui/data-table-header.md +4 -0
  104. package/docs/components/ui/data-table-pagination.md +4 -0
  105. package/docs/components/ui/data-table-toolbar.md +7 -0
  106. package/docs/components/ui/data-table.md +25 -0
  107. package/docs/components/ui/devalok-grain.md +8 -0
  108. package/docs/components/ui/dialog.md +10 -0
  109. package/docs/components/ui/dropdown-menu.md +10 -0
  110. package/docs/components/ui/file-upload.md +8 -0
  111. package/docs/components/ui/form.md +21 -0
  112. package/docs/components/ui/hover-card.md +9 -0
  113. package/docs/components/ui/icon-button.md +7 -0
  114. package/docs/components/ui/icon-context.md +7 -0
  115. package/docs/components/ui/icon-group.md +9 -0
  116. package/docs/components/ui/icon.md +7 -0
  117. package/docs/components/ui/input-otp.md +29 -7
  118. package/docs/components/ui/input.md +11 -2
  119. package/docs/components/ui/label.md +7 -1
  120. package/docs/components/ui/link.md +7 -0
  121. package/docs/components/ui/menubar.md +9 -0
  122. package/docs/components/ui/navigation-menu.md +9 -0
  123. package/docs/components/ui/number-input.md +10 -0
  124. package/docs/components/ui/pagination.md +10 -0
  125. package/docs/components/ui/popover.md +9 -0
  126. package/docs/components/ui/progress-ring.md +7 -0
  127. package/docs/components/ui/progress.md +7 -0
  128. package/docs/components/ui/radio.md +27 -2
  129. package/docs/components/ui/search-input.md +8 -0
  130. package/docs/components/ui/segmented-control.md +21 -6
  131. package/docs/components/ui/select.md +14 -3
  132. package/docs/components/ui/separator.md +7 -0
  133. package/docs/components/ui/sheet.md +10 -0
  134. package/docs/components/ui/sidebar.md +30 -0
  135. package/docs/components/ui/skeleton.md +7 -0
  136. package/docs/components/ui/slider.md +15 -1
  137. package/docs/components/ui/spinner.md +7 -0
  138. package/docs/components/ui/split-button.md +11 -0
  139. package/docs/components/ui/stack.md +8 -0
  140. package/docs/components/ui/stat-card.md +9 -0
  141. package/docs/components/ui/status-dot.md +9 -0
  142. package/docs/components/ui/stepper.md +8 -0
  143. package/docs/components/ui/switch.md +8 -0
  144. package/docs/components/ui/table.md +8 -0
  145. package/docs/components/ui/tabs.md +10 -2
  146. package/docs/components/ui/text.md +9 -4
  147. package/docs/components/ui/textarea.md +7 -1
  148. package/docs/components/ui/toast.md +9 -0
  149. package/docs/components/ui/toaster.md +8 -0
  150. package/docs/components/ui/toggle-group.md +13 -4
  151. package/docs/components/ui/toggle.md +10 -2
  152. package/docs/components/ui/tooltip.md +11 -1
  153. package/docs/components/ui/tree-view.md +12 -0
  154. package/docs/components/ui/visually-hidden.md +7 -0
  155. package/docs/recipes/customize-brand.md +216 -0
  156. package/docs/recipes/index.md +51 -0
  157. package/docs/recipes/install-astro.md +178 -0
  158. package/docs/recipes/install-next-app-router.md +230 -0
  159. package/docs/recipes/install-next-pages.md +123 -0
  160. package/docs/recipes/install-remix.md +171 -0
  161. package/docs/recipes/install-tanstack-start.md +143 -0
  162. package/docs/recipes/install-vite.md +170 -0
  163. package/docs/recipes/server-components.md +209 -0
  164. package/docs/recipes/troubleshoot.md +217 -0
  165. package/llms-full.txt +1133 -88
  166. package/llms.txt +33 -16
  167. package/package.json +45 -35
  168. package/dist/composed/responsive-overlay.d.ts +0 -23
  169. package/dist/composed/responsive-overlay.d.ts.map +0 -1
  170. package/dist/composed/responsive-overlay.js +0 -40
  171. package/dist/composed/responsive-overlay.js.map +0 -1
  172. package/dist/hooks/use-toast.d.ts +0 -17
  173. package/dist/hooks/use-toast.d.ts.map +0 -1
  174. package/dist/hooks/use-toast.js +0 -3
  175. package/dist/tailwind/index.cjs +0 -41
  176. package/dist/tailwind/index.d.ts +0 -2
  177. package/dist/tailwind/index.d.ts.map +0 -1
  178. package/dist/tailwind/index.js +0 -2
  179. package/dist/tailwind/preset.d.ts +0 -25
  180. package/dist/tailwind/preset.d.ts.map +0 -1
  181. package/dist/tailwind/preset.js +0 -17
  182. package/dist/tailwind/preset.js.map +0 -1
  183. 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
- Standard input-otp props (maxLength, pattern, etc.)
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, value, onValueChange, defaultValue)
9
- RadioGroupItem (value: string, REQUIRED)
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" (REQUIRED) — also accepts legacy "small" | "medium" | "big"
9
- variant: "filled" | "tonal" (REQUIRED)
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' | 'small' | 'medium' | 'big' (legacy aliases)
18
- SegmentedControlVariant = 'filled' | 'tonal'
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="tonal"
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` (values `filled`/`tonal` unchanged)
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
- size: "sm" | "md" | "lg"
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 goes HERE, not on Select root)
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 size: "md"
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