prime-ui-kit 0.2.3 → 0.3.1

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 (244) hide show
  1. package/README.md +172 -34
  2. package/dist/components/accordion/Accordion.d.ts +70 -0
  3. package/dist/components/accordion/Accordion.d.ts.map +1 -0
  4. package/dist/components/avatar/Avatar.d.ts +58 -0
  5. package/dist/components/avatar/Avatar.d.ts.map +1 -0
  6. package/dist/components/badge/Badge.d.ts +51 -0
  7. package/dist/components/badge/Badge.d.ts.map +1 -0
  8. package/dist/components/banner/Banner.d.ts +78 -0
  9. package/dist/components/banner/Banner.d.ts.map +1 -0
  10. package/dist/components/breadcrumb/Breadcrumb.d.ts +47 -0
  11. package/dist/components/breadcrumb/Breadcrumb.d.ts.map +1 -0
  12. package/dist/components/button/Button.d.ts +57 -0
  13. package/dist/components/button/Button.d.ts.map +1 -0
  14. package/dist/components/button-group/ButtonGroup.d.ts +34 -0
  15. package/dist/components/button-group/ButtonGroup.d.ts.map +1 -0
  16. package/dist/components/card/Card.d.ts +234 -0
  17. package/dist/components/card/Card.d.ts.map +1 -0
  18. package/dist/components/checkbox/Checkbox.d.ts +42 -0
  19. package/dist/components/checkbox/Checkbox.d.ts.map +1 -0
  20. package/dist/components/code-block/CodeBlock.d.ts +17 -0
  21. package/dist/components/code-block/CodeBlock.d.ts.map +1 -0
  22. package/dist/components/color-picker/ColorPicker.d.ts +76 -0
  23. package/dist/components/color-picker/ColorPicker.d.ts.map +1 -0
  24. package/dist/components/color-picker/ColorPickerRac.d.ts +76 -0
  25. package/dist/components/color-picker/ColorPickerRac.d.ts.map +1 -0
  26. package/dist/components/command-menu/CommandMenu.d.ts +74 -0
  27. package/dist/components/command-menu/CommandMenu.d.ts.map +1 -0
  28. package/dist/components/data-table/DataTable.d.ts +78 -0
  29. package/dist/components/data-table/DataTable.d.ts.map +1 -0
  30. package/dist/components/datepicker/Datepicker.d.ts +104 -0
  31. package/dist/components/datepicker/Datepicker.d.ts.map +1 -0
  32. package/dist/components/digit-input/DigitInput.d.ts +21 -0
  33. package/dist/components/digit-input/DigitInput.d.ts.map +1 -0
  34. package/dist/components/divider/Divider.d.ts +26 -0
  35. package/dist/components/divider/Divider.d.ts.map +1 -0
  36. package/dist/components/drawer/Drawer.d.ts +71 -0
  37. package/dist/components/drawer/Drawer.d.ts.map +1 -0
  38. package/dist/components/dropdown/Dropdown.d.ts +147 -0
  39. package/dist/components/dropdown/Dropdown.d.ts.map +1 -0
  40. package/dist/components/dropdown/dropdownGeometry.d.ts +10 -0
  41. package/dist/components/dropdown/dropdownGeometry.d.ts.map +1 -0
  42. package/dist/components/dropdown/menuKeyboard.d.ts +4 -0
  43. package/dist/components/dropdown/menuKeyboard.d.ts.map +1 -0
  44. package/dist/components/dropdown/useDropdownPosition.d.ts +18 -0
  45. package/dist/components/dropdown/useDropdownPosition.d.ts.map +1 -0
  46. package/dist/components/example-frame/ExampleFrame.d.ts +53 -0
  47. package/dist/components/example-frame/ExampleFrame.d.ts.map +1 -0
  48. package/dist/components/file-upload/FileUpload.d.ts +176 -0
  49. package/dist/components/file-upload/FileUpload.d.ts.map +1 -0
  50. package/dist/components/hint/Hint.d.ts +27 -0
  51. package/dist/components/hint/Hint.d.ts.map +1 -0
  52. package/dist/components/index.css +6306 -5964
  53. package/dist/components/index.css.map +7 -1
  54. package/dist/components/index.d.ts +95 -2679
  55. package/dist/components/index.d.ts.map +1 -0
  56. package/dist/components/index.js +8750 -6666
  57. package/dist/components/index.js.map +7 -1
  58. package/dist/components/input/Input.d.ts +73 -0
  59. package/dist/components/input/Input.d.ts.map +1 -0
  60. package/dist/components/kbd/Kbd.d.ts +15 -0
  61. package/dist/components/kbd/Kbd.d.ts.map +1 -0
  62. package/dist/components/label/Label.d.ts +29 -0
  63. package/dist/components/label/Label.d.ts.map +1 -0
  64. package/dist/components/link-button/LinkButton.d.ts +18 -0
  65. package/dist/components/link-button/LinkButton.d.ts.map +1 -0
  66. package/dist/components/modal/Modal.d.ts +58 -0
  67. package/dist/components/modal/Modal.d.ts.map +1 -0
  68. package/dist/components/notification/Notification.d.ts +42 -0
  69. package/dist/components/notification/Notification.d.ts.map +1 -0
  70. package/dist/components/notification/NotificationStore.d.ts +18 -0
  71. package/dist/components/notification/NotificationStore.d.ts.map +1 -0
  72. package/dist/components/page-content/PageContent.d.ts +51 -0
  73. package/dist/components/page-content/PageContent.d.ts.map +1 -0
  74. package/dist/components/pagination/Pagination.d.ts +19 -0
  75. package/dist/components/pagination/Pagination.d.ts.map +1 -0
  76. package/dist/components/popover/Popover.d.ts +48 -0
  77. package/dist/components/popover/Popover.d.ts.map +1 -0
  78. package/dist/components/popover/popoverGeometry.d.ts +6 -0
  79. package/dist/components/popover/popoverGeometry.d.ts.map +1 -0
  80. package/dist/components/popover/usePopoverPosition.d.ts +18 -0
  81. package/dist/components/popover/usePopoverPosition.d.ts.map +1 -0
  82. package/dist/components/progress-bar/ProgressBar.d.ts +14 -0
  83. package/dist/components/progress-bar/ProgressBar.d.ts.map +1 -0
  84. package/dist/components/progress-circle/ProgressCircle.d.ts +33 -0
  85. package/dist/components/progress-circle/ProgressCircle.d.ts.map +1 -0
  86. package/dist/components/radio/Radio.d.ts +40 -0
  87. package/dist/components/radio/Radio.d.ts.map +1 -0
  88. package/dist/components/scroll-container/ScrollContainer.d.ts +42 -0
  89. package/dist/components/scroll-container/ScrollContainer.d.ts.map +1 -0
  90. package/dist/components/segmented-control/SegmentedControl.d.ts +40 -0
  91. package/dist/components/segmented-control/SegmentedControl.d.ts.map +1 -0
  92. package/dist/components/segmented-progress-bar/SegmentedProgressBar.d.ts +22 -0
  93. package/dist/components/segmented-progress-bar/SegmentedProgressBar.d.ts.map +1 -0
  94. package/dist/components/select/Select.d.ts +84 -0
  95. package/dist/components/select/Select.d.ts.map +1 -0
  96. package/dist/components/select/selectListbox.d.ts +13 -0
  97. package/dist/components/select/selectListbox.d.ts.map +1 -0
  98. package/dist/components/slider/Slider.d.ts +23 -0
  99. package/dist/components/slider/Slider.d.ts.map +1 -0
  100. package/dist/components/stepper/HorizontalStepper.d.ts +41 -0
  101. package/dist/components/stepper/HorizontalStepper.d.ts.map +1 -0
  102. package/dist/components/stepper/Stepper.d.ts +77 -0
  103. package/dist/components/stepper/Stepper.d.ts.map +1 -0
  104. package/dist/components/stepper/VerticalStepper.d.ts +41 -0
  105. package/dist/components/stepper/VerticalStepper.d.ts.map +1 -0
  106. package/dist/components/stepper/stepperAlignContext.d.ts +11 -0
  107. package/dist/components/stepper/stepperAlignContext.d.ts.map +1 -0
  108. package/dist/components/switch/Switch.d.ts +48 -0
  109. package/dist/components/switch/Switch.d.ts.map +1 -0
  110. package/dist/components/tabs/Tabs.d.ts +69 -0
  111. package/dist/components/tabs/Tabs.d.ts.map +1 -0
  112. package/dist/components/tag/Tag.d.ts +29 -0
  113. package/dist/components/tag/Tag.d.ts.map +1 -0
  114. package/dist/components/textarea/Textarea.d.ts +43 -0
  115. package/dist/components/textarea/Textarea.d.ts.map +1 -0
  116. package/dist/components/tooltip/Tooltip.d.ts +47 -0
  117. package/dist/components/tooltip/Tooltip.d.ts.map +1 -0
  118. package/dist/components/typography/Typography.d.ts +30 -0
  119. package/dist/components/typography/Typography.d.ts.map +1 -0
  120. package/dist/hooks/useControllableState.d.ts +8 -0
  121. package/dist/hooks/useControllableState.d.ts.map +1 -0
  122. package/dist/hooks/useEscapeKey.d.ts +7 -0
  123. package/dist/hooks/useEscapeKey.d.ts.map +1 -0
  124. package/dist/hooks/useFieldIds.d.ts +17 -0
  125. package/dist/hooks/useFieldIds.d.ts.map +1 -0
  126. package/dist/hooks/useFocusTrap.d.ts +16 -0
  127. package/dist/hooks/useFocusTrap.d.ts.map +1 -0
  128. package/dist/hooks/useMergedRefs.d.ts +3 -0
  129. package/dist/hooks/useMergedRefs.d.ts.map +1 -0
  130. package/dist/hooks/useOutsideClick.d.ts +16 -0
  131. package/dist/hooks/useOutsideClick.d.ts.map +1 -0
  132. package/dist/hooks/useOverlayModal.d.ts +7 -0
  133. package/dist/hooks/useOverlayModal.d.ts.map +1 -0
  134. package/dist/hooks/usePosition.d.ts +41 -0
  135. package/dist/hooks/usePosition.d.ts.map +1 -0
  136. package/dist/hooks/useResponsiveMonths.d.ts +18 -0
  137. package/dist/hooks/useResponsiveMonths.d.ts.map +1 -0
  138. package/dist/hooks/useScrollLock.d.ts +8 -0
  139. package/dist/hooks/useScrollLock.d.ts.map +1 -0
  140. package/dist/icons/Icon.d.ts +13 -0
  141. package/dist/icons/Icon.d.ts.map +1 -0
  142. package/dist/icons/index.d.ts +40 -0
  143. package/dist/icons/index.d.ts.map +1 -0
  144. package/dist/index.css +6299 -7273
  145. package/dist/index.css.map +7 -1
  146. package/dist/index.d.ts +4 -8
  147. package/dist/index.d.ts.map +1 -0
  148. package/dist/index.js +8784 -6676
  149. package/dist/index.js.map +7 -1
  150. package/dist/internal/ControlSizeContext.d.ts +17 -0
  151. package/dist/internal/ControlSizeContext.d.ts.map +1 -0
  152. package/dist/internal/DividerContentContext.d.ts +4 -0
  153. package/dist/internal/DividerContentContext.d.ts.map +1 -0
  154. package/dist/internal/OverlayPortalLayerContext.d.ts +13 -0
  155. package/dist/internal/OverlayPortalLayerContext.d.ts.map +1 -0
  156. package/dist/internal/Portal.d.ts +8 -0
  157. package/dist/internal/Portal.d.ts.map +1 -0
  158. package/dist/internal/context.d.ts +11 -0
  159. package/dist/internal/context.d.ts.map +1 -0
  160. package/dist/internal/cx.d.ts +2 -0
  161. package/dist/internal/cx.d.ts.map +1 -0
  162. package/dist/internal/data-attributes.d.ts +6 -0
  163. package/dist/internal/data-attributes.d.ts.map +1 -0
  164. package/dist/internal/highlightTsxHtml.d.ts +3 -0
  165. package/dist/internal/highlightTsxHtml.d.ts.map +1 -0
  166. package/dist/internal/layoutPxFromPrimitives.d.ts +13 -0
  167. package/dist/internal/layoutPxFromPrimitives.d.ts.map +1 -0
  168. package/dist/internal/mergeRefs.d.ts +4 -0
  169. package/dist/internal/mergeRefs.d.ts.map +1 -0
  170. package/dist/internal/runtimeUnits.d.ts +3 -0
  171. package/dist/internal/runtimeUnits.d.ts.map +1 -0
  172. package/dist/internal/scrollAncestors.d.ts +3 -0
  173. package/dist/internal/scrollAncestors.d.ts.map +1 -0
  174. package/dist/internal/slot.d.ts +19 -0
  175. package/dist/internal/slot.d.ts.map +1 -0
  176. package/dist/internal/states.d.ts +87 -0
  177. package/dist/internal/states.d.ts.map +1 -0
  178. package/dist/layout/app-shell/AppShell.d.ts +54 -0
  179. package/dist/layout/app-shell/AppShell.d.ts.map +1 -0
  180. package/dist/layout/index.d.ts +7 -0
  181. package/dist/layout/index.d.ts.map +1 -0
  182. package/dist/layout/sidebar/Sidebar.d.ts +241 -0
  183. package/dist/layout/sidebar/Sidebar.d.ts.map +1 -0
  184. package/dist/layout/sidebar/SidebarRoot.d.ts +51 -0
  185. package/dist/layout/sidebar/SidebarRoot.d.ts.map +1 -0
  186. package/dist/layout/sidebar/sidebar-context.d.ts +19 -0
  187. package/dist/layout/sidebar/sidebar-context.d.ts.map +1 -0
  188. package/dist/layout/sidebar/sidebarLayout.d.ts +12 -0
  189. package/dist/layout/sidebar/sidebarLayout.d.ts.map +1 -0
  190. package/dist/tokens/primitives.d.ts +390 -0
  191. package/dist/tokens/primitives.d.ts.map +1 -0
  192. package/dist/tokens/semantic.d.ts +732 -0
  193. package/dist/tokens/semantic.d.ts.map +1 -0
  194. package/dist/tokens/themes/dark.d.ts +143 -0
  195. package/dist/tokens/themes/dark.d.ts.map +1 -0
  196. package/dist/tokens/themes/light.d.ts +16 -0
  197. package/dist/tokens/themes/light.d.ts.map +1 -0
  198. package/package.json +14 -6
  199. package/src/components/accordion/COMPONENT.md +137 -0
  200. package/src/components/avatar/COMPONENT.md +100 -0
  201. package/src/components/badge/COMPONENT.md +78 -0
  202. package/src/components/banner/COMPONENT.md +118 -0
  203. package/src/components/breadcrumb/COMPONENT.md +94 -0
  204. package/src/components/button/COMPONENT.md +80 -0
  205. package/src/components/button-group/COMPONENT.md +83 -0
  206. package/src/components/card/COMPONENT.md +489 -0
  207. package/src/components/checkbox/COMPONENT.md +98 -0
  208. package/src/components/code-block/COMPONENT.md +58 -0
  209. package/src/components/color-picker/COMPONENT.md +149 -0
  210. package/src/components/command-menu/COMPONENT.md +167 -0
  211. package/src/components/data-table/COMPONENT.md +113 -0
  212. package/src/components/datepicker/COMPONENT.md +137 -0
  213. package/src/components/digit-input/COMPONENT.md +68 -0
  214. package/src/components/divider/COMPONENT.md +70 -0
  215. package/src/components/drawer/COMPONENT.md +149 -0
  216. package/src/components/dropdown/COMPONENT.md +192 -0
  217. package/src/components/file-upload/COMPONENT.md +200 -0
  218. package/src/components/hint/COMPONENT.md +67 -0
  219. package/src/components/input/COMPONENT.md +119 -0
  220. package/src/components/kbd/COMPONENT.md +59 -0
  221. package/src/components/label/COMPONENT.md +94 -0
  222. package/src/components/link-button/COMPONENT.md +60 -0
  223. package/src/components/modal/COMPONENT.md +105 -0
  224. package/src/components/notification/COMPONENT.md +120 -0
  225. package/src/components/pagination/COMPONENT.md +61 -0
  226. package/src/components/popover/COMPONENT.md +93 -0
  227. package/src/components/progress-bar/COMPONENT.md +59 -0
  228. package/src/components/progress-circle/COMPONENT.md +63 -0
  229. package/src/components/radio/COMPONENT.md +95 -0
  230. package/src/components/segmented-control/COMPONENT.md +86 -0
  231. package/src/components/segmented-progress-bar/COMPONENT.md +75 -0
  232. package/src/components/select/COMPONENT.md +175 -0
  233. package/src/components/slider/COMPONENT.md +62 -0
  234. package/src/components/stepper/COMPONENT.md +186 -0
  235. package/src/components/switch/COMPONENT.md +98 -0
  236. package/src/components/tabs/COMPONENT.md +114 -0
  237. package/src/components/tag/COMPONENT.md +67 -0
  238. package/src/components/textarea/COMPONENT.md +98 -0
  239. package/src/components/tooltip/COMPONENT.md +87 -0
  240. package/src/components/typography/COMPONENT.md +89 -0
  241. package/src/styles/theme-dark.css +43 -0
  242. package/src/styles/theme-light.css +43 -0
  243. package/src/styles/tokens.css +23 -4
  244. package/src/styles/tokens.test.ts +0 -27
@@ -0,0 +1,105 @@
1
+ # Modal
2
+
3
+ ## About
4
+
5
+ Centered overlay dialog with a portal, backdrop, focus trap, scroll lock, and optional built-in header, body, and footer. `Modal.Panel` composes these pieces so consumers rarely touch internal layers.
6
+
7
+ - **Use** for blocking confirmation, forms, or disclosures that need full attention and clear dismiss semantics.
8
+ - **Use** when Escape, overlay click, and an explicit close control should all be able to dismiss the dialog (configurable on `Modal.Root`).
9
+ - **Do not use** for non-blocking hints or menus; prefer lighter overlays (for example [Popover](../popover/COMPONENT.md)).
10
+ - **Do not use** for edge-docked sheets; prefer [Drawer](../drawer/COMPONENT.md) when the pattern is a side panel.
11
+ - **Do not use** nested modal stacks without extra focus and stacking discipline; the kit does not add a second modal layer API.
12
+
13
+ ## Composition
14
+
15
+ - **`Modal.Root`** — holds open state (controlled via **`open`** / **`onOpenChange`** or uncontrolled via **`defaultOpen`**), and options **`closeOnEscape`** / **`closeOnOverlayClick`**. Renders **`children`** only (no DOM wrapper).
16
+ - **`Modal.Trigger`** — optional; **`React.Children.only`**: pass **exactly one** React element; its **`onClick`** is merged to call **`onOpen`** when the event is not **`defaultPrevented`**.
17
+ - **`Modal.Panel`** — when open: **`createPortal`** (default container `document.body`), fullscreen **`role="presentation"`** overlay, then **`role="dialog"`** with **`aria-modal="true"`**. If **`title`** is set, renders an internal header (**`h2`**, optional description, optional built-in close icon button), wraps **`children`** in an internal body, and optional **`footer`**. Without **`title`**, **`children`** render directly inside the dialog surface—supply **`aria-label`** or **`aria-labelledby`** (and **`aria-describedby`** when needed).
18
+ - **`Modal.Close`** — same single-child contract as **`Trigger`**; merges **`onClick`** to **`onClose`** when not **`defaultPrevented`**. Typical placement: a control inside **`footer`**.
19
+ - **Order:** **`Modal.Root`** → **`Modal.Trigger`** (if any) and **`Modal.Panel`** as siblings (or only **`Modal.Panel`** in controlled flows).
20
+
21
+ ### Minimal example
22
+
23
+ ```tsx
24
+ import { Button, Modal } from "prime-ui-kit";
25
+
26
+ export function Example() {
27
+ return (
28
+ <Modal.Root>
29
+ <Modal.Trigger>
30
+ <Button.Root>Open</Button.Root>
31
+ </Modal.Trigger>
32
+ <Modal.Panel title="Title">
33
+ <p>Content</p>
34
+ </Modal.Panel>
35
+ </Modal.Root>
36
+ );
37
+ }
38
+ ```
39
+
40
+ ## Rules
41
+
42
+ - **Shell tokens:** overlay padding, panel padding, gaps between header/body/footer, and max width use **`--prime-sys-size-modal-*`** (semantic `size.modal` in `tokens/semantic.ts`). Title/description text tiers may still follow control typography tokens where the chrome matches **`size`** `m`.
43
+ - **Controlled:** omit **`Modal.Trigger`** and drive **`open`** / **`onOpenChange`** on **`Modal.Root`**; **`Modal.Panel`** still portals when **`open`** is true.
44
+ - **Uncontrolled:** use **`defaultOpen`** or rely on **`Modal.Trigger`**; **`onOpenChange`** fires for any transition.
45
+ - **`Modal.Trigger`** and **`Modal.Close`** require **exactly one** child element (**`cloneElement`**); fragments or multiple nodes are invalid.
46
+ - **Accessibility:** with **`title`** / **`description`**, **`aria-labelledby`** / **`aria-describedby`** on the dialog are wired from the internal header. Without a visible title, set **`aria-label`** on **`Modal.Panel`** or valid **`aria-labelledby`** / **`aria-describedby`** yourself.
47
+ - While open, siblings of the portal subtree on **`document.body`** get **`inert`** and **`aria-hidden="true"`** to hide background from assistive tech; restore runs on close.
48
+ - **Focus:** focus is trapped inside the dialog while open; initial focus follows browser / trap behavior—ensure a focusable control or manage focus if the first paint is static text only.
49
+ - **`showClose`** (default **`true`** when a header is shown) controls the header icon button; **`closeAriaLabel`** defaults to **`"Close"`**.
50
+ - **`container`** on **`Modal.Panel`** overrides the portal target (for tests or custom stacking); default is **`document.body`**.
51
+ - **`overlayClassName`**, **`footerClassName`**, **`bodyClassName`**, and **`bodyStyle`** target the overlay, **`<footer>`**, and body wrapper respectively; without **`title`**, **`bodyClassName`** / **`bodyStyle`** do not apply (no inner body wrapper).
52
+
53
+ ## API
54
+
55
+ ### Modal.Root
56
+
57
+ | Prop | Type | Default | Required | Description |
58
+ |------|------|---------|----------|-------------|
59
+ | open | `boolean` | — | No | Controlled open state |
60
+ | defaultOpen | `boolean` | `false` | No | Initial open when uncontrolled |
61
+ | onOpenChange | `(open: boolean) => void` | — | No | Fires when open state changes |
62
+ | closeOnEscape | `boolean` | `true` | No | Whether Escape closes the dialog |
63
+ | closeOnOverlayClick | `boolean` | `true` | No | Whether a direct backdrop click closes |
64
+ | children | `React.ReactNode` | — | No | e.g. `Modal.Trigger` and `Modal.Panel` |
65
+
66
+ ### Modal.Trigger
67
+
68
+ | Prop | Type | Default | Required | Description |
69
+ |------|------|---------|----------|-------------|
70
+ | children | `React.ReactElement<{ onClick?: React.MouseEventHandler }>` | — | Yes | Single element whose `onClick` is composed with open |
71
+
72
+ ### Modal.Close
73
+
74
+ | Prop | Type | Default | Required | Description |
75
+ |------|------|---------|----------|-------------|
76
+ | children | `React.ReactElement<{ onClick?: React.MouseEventHandler; className?: string; size?: "s" \| "m" \| "l" \| "xl" }>` | — | Yes | Single element whose `onClick` is composed with close |
77
+
78
+ ### Modal.Panel
79
+
80
+ | Prop | Type | Default | Required | Description |
81
+ |------|------|---------|----------|-------------|
82
+ | title | `React.ReactNode` | — | No | If set, builds header with `h2` and optional description |
83
+ | description | `React.ReactNode` | — | No | Shown under the title when `title` is set |
84
+ | icon | `React.ReactNode` | — | No | Icon slot in the header row |
85
+ | showClose | `boolean` | `true` | No | Header close icon button when `title` is set |
86
+ | closeAriaLabel | `string` | `"Close"` | No | `aria-label` for the header close control |
87
+ | children | `React.ReactNode` | — | No | Main content; wrapped in internal body when `title` is set |
88
+ | footer | `React.ReactNode` | — | No | Rendered in an internal `footer` |
89
+ | container | `HTMLElement \| null` | — | No | Portal container; default `document.body` |
90
+ | overlayClassName | `string` | — | No | Class on the fullscreen backdrop |
91
+ | footerClassName | `string` | — | No | Class on the `footer` element |
92
+ | bodyClassName | `string` | — | No | Class on the internal body when `title` is set |
93
+ | bodyStyle | `React.CSSProperties` | — | No | Inline style on the internal body when `title` is set |
94
+ | aria-label | `string` | — | No | Dialog name when there is no `title`-driven label |
95
+ | aria-labelledby | `string` | — | No | Overrides auto wiring from the built-in header |
96
+ | aria-describedby | `string` | — | No | Overrides auto wiring from the built-in description |
97
+ | className | `string` | — | No | Class on the `role="dialog"` root |
98
+ | style | `React.CSSProperties` | — | No | Style on the `role="dialog"` root |
99
+ | …rest | `Omit<React.HTMLAttributes<HTMLDivElement>, "title">` | — | No | Other attributes forwarded to the dialog root |
100
+
101
+ ## Related
102
+
103
+ - [Button](../button/COMPONENT.md)
104
+ - [Drawer](../drawer/COMPONENT.md)
105
+ - [Popover](../popover/COMPONENT.md)
@@ -0,0 +1,120 @@
1
+ # Notification
2
+
3
+ **Проектирование по умолчанию:** при проектировании экранов и примеров изначально выбирай **`m`** для `size` (где есть ось размера), если явно не оговорено иное.
4
+
5
+ ## About
6
+
7
+ Toast notifications: `NotificationProvider` keeps a queue, hooks expose `notify` / `dismiss` / `dismissAll`, and cards render in a portal with stacked groups per corner and semantic type.
8
+
9
+ - **Use** for short, non-blocking feedback after actions (save, send, background job finished) when you do not need to trap focus.
10
+ - **Use** when the message should appear above the page chrome and auto-dismiss or offer a single secondary action.
11
+ - **Use** `useNotificationStore` when the UI must read `items` (e.g. custom lists or bulk dismiss).
12
+ - **Use** `NotificationCard` alone only for static previews or fully custom wiring; live toasts go through the provider and `notify`.
13
+ - **Do not use** for errors that require a blocking decision, long forms, or primary workflow inside the toast—prefer [Modal](../modal/COMPONENT.md), [Drawer](../drawer/COMPONENT.md), or [Banner](../banner/COMPONENT.md).
14
+ - **Do not use** multiple nested `NotificationProvider`s unless you intentionally want several portals and duplicate zones.
15
+
16
+ ## Composition
17
+
18
+ - **`NotificationProvider`** — wraps the tree that calls hooks; provides context and mounts a **portal** with a fixed viewport. Each screen **position** gets a **zone**; inside a zone, notifications are split into **stacks by `type`** (`success`, `error`, `warning`, `info`).
19
+ - **`NotificationStack` / `NotificationStackItem`** (internal) — ordered list with Framer Motion; hovering expands the stack and sets **`paused`** on cards so countdown timers stop until collapse.
20
+ - **`NotificationCard`** — root is an `article` with a live region role, leading icon, title row (optional **badge**), optional description, optional **action** (`Button.Root`), optional close control, and a progress track unless **`persistent`**.
21
+
22
+ ### Minimal example
23
+
24
+ ```tsx
25
+ import { NotificationProvider, useNotifications } from "prime-ui-kit";
26
+
27
+ export function Example() {
28
+ return (
29
+ <NotificationProvider>
30
+ <Notifier />
31
+ </NotificationProvider>
32
+ );
33
+ }
34
+
35
+ function Notifier() {
36
+ const { notify } = useNotifications();
37
+ return (
38
+ <button type="button" onClick={() => notify({ type: "info", title: "Hello" })}>
39
+ Notify
40
+ </button>
41
+ );
42
+ }
43
+ ```
44
+
45
+ ## Rules
46
+
47
+ - Call **`useNotifications`** or **`useNotificationStore`** only under **`NotificationProvider`**; both hooks throw if context is missing.
48
+ - **`notify`** returns a string **`id`**; pass it to **`dismiss`** or use **`dismissAll`** for every active toast.
49
+ - **`useNotificationStore`** exposes the same methods plus **`items`**: `NotificationRecord[]` of non-dismissing entries only (no internal closing-animation flag).
50
+ - Options passed to **`notify`** are merged with defaults: **`size`** `"m"`, **`position`** from the provider, **`duration`** `5000` ms, **`persistent`** `false`, **`closable`** `true`.
51
+ - With **`persistent`**, there is no auto-dismiss, no progress bar, and duration does not drive closing; users or **`dismiss`** / **`dismissAll`** must close the card. Visually, **`persistent`** also turns on the accent-tinted **border** and (unless **`prefers-reduced-motion`**) the **`notification-glow`** shadow pulse — default **`notify()`** uses **`persistent: false`**, so live toasts look flatter unless you opt in.
52
+ - If **`duration <= 0`**, the countdown effect does not run—time-based auto-dismiss does not occur; close via **`dismiss`** or the close button when **`closable`**.
53
+ - Stacks are keyed by **`position`** and **`type`**; **`max`** (default `5`) caps depth per stack—older items in that stack are dropped when exceeded.
54
+ - **`type`** `error` and **`warning`** use **`role="alert"`** and **`aria-live="assertive"`**; other types use **`role="status"`** and **`aria-live="polite"`**.
55
+ - Close control uses **`aria-label="Dismiss notification"`**; default type icons are **`aria-hidden`** inside the icon wrapper.
56
+ - Each stack list has **`aria-label`** `Notifications at <position>`.
57
+ - Nested **`NotificationProvider`** instances each render their own portal and zones—usually one root provider is enough.
58
+ - **`NotificationCard`** is not connected to the store by itself; supply **`item`**, **`paused`**, and **`onDismiss`** and keep **`item`** fields consistent with **`NotificationRecord`**.
59
+
60
+ ## API
61
+
62
+ ### NotificationProvider
63
+
64
+ | Prop | Type | Default | Required | Description |
65
+ |------|------|---------|----------|-------------|
66
+ | children | `React.ReactNode` | — | Yes | Subtree where notification hooks are used |
67
+ | position | `NotificationPosition` | `"top-right"` | No | Default **`position`** for **`notify`** when omitted in options |
68
+ | max | `number` | `5` | No | Maximum items per stack (same **`position`** + **`type`**) |
69
+
70
+ ### NotificationOptions (`notify` payload)
71
+
72
+ | Prop | Type | Default | Required | Description |
73
+ |------|------|---------|----------|-------------|
74
+ | type | `"success" \| "error" \| "warning" \| "info"` | — | Yes | Semantics, default icon, and stack grouping |
75
+ | title | `string` | — | Yes | Primary heading |
76
+ | description | `string` | — | No | Secondary text |
77
+ | size | `"s" \| "m" \| "l"` | `"m"` | No | Card scale |
78
+ | position | `NotificationPosition` | provider default | No | Corner or edge anchor |
79
+ | duration | `number` | `5000` | No | Auto-dismiss delay in ms (ignored for closing when **`persistent`**; see Rules when `<= 0`) |
80
+ | persistent | `boolean` | `false` | No | Disable timer and progress UI |
81
+ | icon | `React.ReactNode` | type default | No | Custom leading icon |
82
+ | badge | `string \| number` | — | No | Label next to the title |
83
+ | closable | `boolean` | `true` | No | Show dismiss button |
84
+ | action | `{ label: string; onClick: () => void }` | — | No | Secondary action rendered as neutral stroke **`Button.Root`** |
85
+
86
+ `notify` returns the new record’s **`id`** (`string`).
87
+
88
+ ### NotificationRecord
89
+
90
+ All **`NotificationOptions`** fields plus required **`id`**, **`position`**, **`size`**, **`duration`**, **`persistent`**, **`closable`**, and **`createdAt`** (`number`, ms). Defaults are filled when the record is created inside **`notify`**.
91
+
92
+ ### NotificationCard
93
+
94
+ | Prop | Type | Default | Required | Description |
95
+ |------|------|---------|----------|-------------|
96
+ | item | `NotificationRecord` | — | Yes | Card data |
97
+ | paused | `boolean` | — | Yes | Pause countdown (e.g. expanded stack) |
98
+ | onDismiss | `(id: string) => void` | — | Yes | Called to remove the toast (timer or UI) |
99
+ | stackDepth | `number` | `0` | No | Stack index for layout / `data-*` |
100
+ | stackExpanded | `boolean` | `false` | No | Whether the parent stack is expanded |
101
+ | className | `string` | — | No | Extra class on the root `article` |
102
+
103
+ ### useNotifications()
104
+
105
+ | Field | Type | Description |
106
+ |------|------|-------------|
107
+ | notify | `(options: NotificationOptions) => string` | Enqueue a notification |
108
+ | dismiss | `(id: string) => void` | Dismiss one by id |
109
+ | dismissAll | `() => void` | Dismiss all active |
110
+
111
+ ### useNotificationStore()
112
+
113
+ Same **`notify`**, **`dismiss`**, and **`dismissAll`** as above, plus **`items`**: `NotificationRecord[]` (active only, excluding items in the exit-animation phase).
114
+
115
+ ## Related
116
+
117
+ - [Banner](../banner/COMPONENT.md)
118
+ - [Button](../button/COMPONENT.md)
119
+ - [Drawer](../drawer/COMPONENT.md)
120
+ - [Modal](../modal/COMPONENT.md)
@@ -0,0 +1,61 @@
1
+ # Pagination
2
+
3
+ **Проектирование по умолчанию:** при проектировании экранов и примеров изначально выбирай **`m`** для `size` (где есть ось размера), если явно не оговорено иное.
4
+
5
+ ## About
6
+
7
+ Page navigation for chunked lists: previous and next controls with chevron icons, numeric page buttons, and ellipsis cells when there are many pages.
8
+
9
+ - **Use** for server-driven or client-paged tables, search results, and any UI where the user moves between discrete page indices.
10
+ - **Use** when you already know **`totalPages`** and can keep **`page`** in sync (controlled pattern).
11
+ - **Do not use** as the only paging affordance when you rely on infinite scroll and never expose page numbers—this component assumes explicit pages.
12
+ - **Do not use** when **`totalPages` is less than 1** and you still need a visible empty state; the root returns **`null`** in that case, so render a message or hide the bar in the parent.
13
+ - **Do not use** expecting localized arrow or “Page N” strings from props; labels are fixed in the implementation (English).
14
+ - **Do not use** if you must replace inner markup (custom arrows, slots); structure and **`Button`** wiring are fixed—extend or fork for deep customization.
15
+
16
+ ## Composition
17
+
18
+ - **`Pagination`** is a single-part namespace: **`Pagination.Root`** only (exported as **`Pagination = { Root }`**).
19
+ - **`Pagination.Root`** renders a **`nav`** with **`aria-label="Pagination"`**, **`data-size`**, and a row of controls: **previous** (ghost neutral **`Button`** with **`Button.Icon`**), a sequence of numeric page **`Button.Root`** cells or ellipsis **`span`**s (**`aria-hidden`**), then **next** (same as previous).
20
+ - Page items use **`Button.Root`** with **`size`** from the root; the current page uses **`variant="primary"`** and **`mode="filled"`**; other numbers and arrows use **`variant="neutral"`** and **`mode="ghost"`**.
21
+
22
+ ### Minimal example
23
+
24
+ ```tsx
25
+ import { Pagination } from "prime-ui-kit";
26
+
27
+ export function Example() {
28
+ return (
29
+ <Pagination.Root page={1} totalPages={5} onPageChange={() => {}} />
30
+ );
31
+ }
32
+ ```
33
+
34
+ ## Rules
35
+
36
+ - **Controlled only:** **`page`**, **`totalPages`**, and **`onPageChange`** are required; there is no uncontrolled mode.
37
+ - **`page`** is clamped internally to **`1 … totalPages`** before rendering and when handling clicks.
38
+ - If **`totalPages < 1`**, the component returns **`null`** (render nothing).
39
+ - With **`totalPages ≤ 7`**, every page number is shown. With more pages, the row is shortened using **`siblingCount`** (default **`1`**) and ellipsis segments (**`…`**).
40
+ - **First page:** previous control is **`disabled`**. **Last page:** next is **`disabled`**. **Single page:** both arrows are **`disabled`**.
41
+ - The active page button sets **`aria-current="page"`**; other page buttons use **`aria-label={`Page ${n}`}`**; arrows use **“Previous page”** / **“Next page”**.
42
+ - **`className`** merges onto the root **`nav`** only.
43
+ - Syncing **`page`** to the URL, router, or query params is the responsibility of the parent.
44
+
45
+ ## API
46
+
47
+ ### Pagination.Root
48
+
49
+ | Prop | Type | Default | Required | Description |
50
+ |------|------|---------|----------|-------------|
51
+ | page | `number` | — | Yes | Current page; clamped to `1 … totalPages` for display and navigation |
52
+ | totalPages | `number` | — | Yes | Page count; if `< 1`, renders nothing |
53
+ | onPageChange | `(page: number) => void` | — | Yes | Called when the user selects a page or an arrow |
54
+ | siblingCount | `number` | `1` | No | How many page indices to show on each side of the current page when `totalPages > 7` |
55
+ | size | `"s" \| "m" \| "l" \| "xl"` | `"m"` | No | Control and ellipsis scale (`--prime-sys-size-control-*`) |
56
+ | className | `string` | — | No | Extra class on the root `nav` |
57
+
58
+ ## Related
59
+
60
+ - [Button](../button/COMPONENT.md)
61
+ - [DataTable](../data-table/COMPONENT.md)
@@ -0,0 +1,93 @@
1
+ # Popover
2
+
3
+ **Проектирование по умолчанию:** при проектировании экранов и примеров изначально выбирай **`m`** для `size` (где есть ось размера), если явно не оговорено иное.
4
+
5
+ ## About
6
+
7
+ A composite “anchor + portaled panel”: clicking the trigger toggles a non-modal dialog next to the anchor, positioned toward the viewport edge and dismissible with Escape or an outside click.
8
+
9
+ - **Use** for short contextual panels—help text, compact filters, a few fields or actions—without leaving the page or blocking the whole UI.
10
+ - **Use** when content should stay visually tied to a specific control (metrics, footnotes, inline configuration).
11
+ - **Do not use** for full-screen or blocking flows; prefer [Modal](../modal/COMPONENT.md).
12
+ - **Do not use** for hover-only hints; prefer [Tooltip](../tooltip/COMPONENT.md).
13
+ - **Do not use** expecting left/right placement relative to the anchor; only **top** and **bottom** sides are supported.
14
+ - **Do not use** with multiple or fragment children under `Popover.Trigger`; exactly **one** element is supported.
15
+
16
+ ## Composition
17
+
18
+ - **`Popover.Root`** — holds open state (controlled or uncontrolled), stable ids for trigger and content, and the trigger element ref used for positioning.
19
+ - **`Popover.Trigger`** — must wrap **exactly one** `React` element; ref, `aria-*`, and click-to-toggle are merged onto that child (`cloneElement`).
20
+ - **`Popover.Content`** — rendered in a **portal** only when open; root is a scroll container with `role="dialog"`, positioned from the trigger via `side` / `align`, wraps children in `ControlSizeProvider` when `size` is set, and applies optional **`insetPadding`** / **`insetGap`** on the same node (`data-inset-padding`, `data-inset-gap`).
21
+
22
+ ### Minimal example
23
+
24
+ ```tsx
25
+ import { Popover } from "prime-ui-kit";
26
+
27
+ export function Example() {
28
+ return (
29
+ <Popover.Root>
30
+ <Popover.Trigger asChild>
31
+ <button type="button">Open</button>
32
+ </Popover.Trigger>
33
+ <Popover.Content insetPadding="x2" insetGap="x3">
34
+ Panel
35
+ </Popover.Content>
36
+ </Popover.Root>
37
+ );
38
+ }
39
+ ```
40
+
41
+ ## Rules
42
+
43
+ - **Uncontrolled:** use `defaultOpen` on `Popover.Root` for the initial open state after mount.
44
+ - **Controlled:** pass `open` and `onOpenChange` together; parent can open or close from outside logic.
45
+ - When closed, **`Popover.Content`** returns `null` (nothing is mounted in the portal).
46
+ - **`trapFocus`** on `Popover.Content` keeps Tab cycling inside the panel while open and restores focus on close when enabled.
47
+ - **Escape** and **outside click** call close; clicks on a portaled **Select** listbox that belongs to the panel are ignored as outside (see `isPortaledSelectListboxOwnedByContainer`).
48
+ - **Non-modal:** `aria-modal={false}`—focus can leave the page; this is intentional for a lightweight overlay.
49
+ - Trigger receives **`aria-expanded`**, **`aria-haspopup="dialog"`**, and **`aria-controls`** pointing at the content id; **`aria-labelledby`** on the panel references the trigger id—give the anchor a visible name or **`aria-label`** when there is no text.
50
+ - There is no popover-level **`disabled`**; if the anchor (e.g. [Button](../button/COMPONENT.md)) is disabled, it will not open.
51
+ - **`asChild`** exists for slot API compatibility; merging always applies to the single child—**`asChild={false}`** does not render an internal button.
52
+ - Density: tune **`Popover.Content` `size`** for the control tier and **`insetPadding` / `insetGap`** for inner spacing; optional **`className`** on `Content` for further styling.
53
+
54
+ ## API
55
+
56
+ ### Popover.Root
57
+
58
+ | Prop | Type | Default | Required | Description |
59
+ |------|------|---------|----------|-------------|
60
+ | open | `boolean` | — | No | Controlled open state; use with `onOpenChange`. |
61
+ | defaultOpen | `boolean` | `false` | No | Initial state in uncontrolled mode. |
62
+ | onOpenChange | `(open: boolean) => void` | — | No | Fires when opening or closing (trigger, Escape, outside click). |
63
+ | children | `React.ReactNode` | — | Yes | Typically `Popover.Trigger` and `Popover.Content`. |
64
+
65
+ ### Popover.Trigger
66
+
67
+ | Prop | Type | Default | Required | Description |
68
+ |------|------|---------|----------|-------------|
69
+ | children | `React.ReactElement` | — | Yes | Single anchor element; ref, ARIA, and click handler are merged in. |
70
+ | asChild | `boolean` | `true` | No | Reserved for slot API compatibility; behavior always merges with the single child. |
71
+
72
+ ### Popover.Content
73
+
74
+ | Prop | Type | Default | Required | Description |
75
+ |------|------|---------|----------|-------------|
76
+ | align | `"start" \| "center" \| "end"` | `"start"` | No | Horizontal alignment of the panel relative to the trigger. |
77
+ | side | `"bottom" \| "top"` | `"bottom"` | No | Preferred side; layout may flip at the viewport edge. |
78
+ | sameMinWidthAsTrigger | `boolean` | `false` | No | Panel min width matches the trigger (`border-box`), still subject to panel max width and viewport. |
79
+ | size | `"s" \| "m" \| "l" \| "xl"` | `"m"` | No | Control density tier for nested controls via `ControlSizeProvider`. |
80
+ | trapFocus | `boolean` | `false` | No | Trap focus inside the panel while open. |
81
+ | insetPadding | `"none" \| "x1" \| "x2" \| "x3"` | `"none"` | No | Extra inset padding relative to the panel tier (`data-inset-padding`). |
82
+ | insetGap | `"none" \| "x2" \| "x3" \| "x4"` | `"none"` | No | Vertical gap between direct children (`data-inset-gap`). |
83
+ | className | `string` | — | No | Extra class on the panel root. |
84
+ | children | `React.ReactNode` | — | Yes | Panel body. |
85
+
86
+ ## Related
87
+
88
+ - [Button](../button/COMPONENT.md), [LinkButton](../link-button/COMPONENT.md) — typical triggers.
89
+ - [Select](../select/COMPONENT.md), [Dropdown](../dropdown/COMPONENT.md) — nested overlays; Select listbox clicks are treated as inside the popover when owned by the panel.
90
+ - [Input](../input/COMPONENT.md), [Textarea](../textarea/COMPONENT.md), [Checkbox](../checkbox/COMPONENT.md), [Switch](../switch/COMPONENT.md) — fields inside the panel.
91
+ - [Typography](../typography/COMPONENT.md), [Label](../label/COMPONENT.md), [Hint](../hint/COMPONENT.md) — text and labels in the panel.
92
+ - [Modal](../modal/COMPONENT.md) — blocking modal flow.
93
+ - [Tooltip](../tooltip/COMPONENT.md) — short hover/focus hint without an action panel.
@@ -0,0 +1,59 @@
1
+ # ProgressBar
2
+
3
+ **Проектирование по умолчанию:** при проектировании экранов и примеров изначально выбирай **`m`** для `size` (где есть ось размера), если явно не оговорено иное.
4
+
5
+ ## About
6
+
7
+ A horizontal completion indicator built on the native `progress` element: fill is driven by `value` and `max`, with optional text label and `size` for track density.
8
+
9
+ - **Use** for determinate tasks—uploads, downloads, or any operation where progress maps to a numeric range.
10
+ - **Use** in multi-step flows when the user should see how far they are through a bounded sequence (steps, checklist, wizard).
11
+ - **Use** with a `label` when the bar needs a short visible name tied to the meter for assistive tech.
12
+ - **Do not use** for indeterminate or endless “busy” feedback; this API always requires a numeric `value` (use a spinner or another pattern instead).
13
+ - **Do not use** for vertical or circular meters; the track is horizontal only—for a circular indicator in the kit, see Related.
14
+ - **Do not use** expecting extra native attributes on the inner `progress`; they are not forwarded—wrap the component if you need custom markup.
15
+
16
+ ## Composition
17
+
18
+ - **`ProgressBar`** is a single-part namespace: only **`ProgressBar.Root`** is public.
19
+ - **`ProgressBar.Root`** renders a wrapper `div` with `data-size`, an optional **`label`** as a `span` with a generated `id`, and a native **`<progress>`** for the track. The bar spans the full width of its container.
20
+
21
+ ### Minimal example
22
+
23
+ ```tsx
24
+ import { ProgressBar } from "prime-ui-kit";
25
+
26
+ export function Example() {
27
+ return <ProgressBar.Root value={40} max={100} />;
28
+ }
29
+ ```
30
+
31
+ ## Rules
32
+
33
+ - **`value`** is required and clamped to **`[0, max]`**; negative values become `0`, values above `max` become `max`.
34
+ - **`max`** defaults to **`100`**; if **`max <= 0`**, the implementation uses **`100`** as the effective maximum.
35
+ - **`size`** defaults to **`m`**; allowed values are **`s`**, **`m`**, **`l`**, **`xl`** (`ProgressBarSize`).
36
+ - There is no **`disabled`**, **`loading`**, or **`error`** prop—mute or hide the block at the screen level if needed.
37
+ - Native **`progress`** exposes the **`progressbar`** role with **`value`** / **`max`** to the accessibility tree; with **`label`**, **`aria-labelledby`** points at the label element.
38
+ - The bar is not interactive and is not keyboard-focusable; keep focus on real controls nearby.
39
+ - “Controlled” usage is just React state passed into **`value`**; there is no separate uncontrolled mode with an internal store.
40
+
41
+ ## API
42
+
43
+ ### ProgressBar.Root
44
+
45
+ | Prop | Type | Default | Required | Description |
46
+ |------|------|---------|----------|-------------|
47
+ | `value` | `number` | — | Yes | Current value; clamped to `[0, max]` after `max` is normalized. |
48
+ | `max` | `number` | `100` | No | Upper bound; if `max <= 0`, `100` is used. |
49
+ | `label` | `string` | — | No | Text above the track; when set, the progress element gets `aria-labelledby` referencing the label. |
50
+ | `size` | `"s" \| "m" \| "l" \| "xl"` | `"m"` | No | Track height and label typography scale. |
51
+ | `className` | `string` | — | No | Class on the outer wrapper around the label and `progress`. |
52
+ | `ref` | `React.Ref<HTMLProgressElement>` | — | No | Ref to the native `progress` element. |
53
+
54
+ ## Related
55
+
56
+ - [SegmentedProgressBar](../segmented-progress-bar/COMPONENT.md) — stacked proportional segments (e.g. status mix) instead of a single value.
57
+ - [ProgressCircle](../progress-circle/COMPONENT.md) — circular determinate indicator when layout calls for a ring or compact numeric emphasis.
58
+ - [Typography](../typography/COMPONENT.md) — headings and supporting copy around a status block.
59
+ - [Button](../button/COMPONENT.md) — cancel, pause, or actions next to the bar.
@@ -0,0 +1,63 @@
1
+ # ProgressCircle
2
+
3
+ **Проектирование по умолчанию:** при проектировании экранов и примеров изначально выбирай **`m`** для `size` (где есть ось размера), если явно не оговорено иное.
4
+
5
+ ## About
6
+
7
+ Circular progress indicator: an SVG ring with `progressbar` semantics and optional centered content inside the ring.
8
+
9
+ - **Use** when you need a compact fraction of a known maximum (percent, steps, seats vs capacity) in a round layout.
10
+ - **Use** with **`max`** when the scale is not 0–100 (e.g. 12 months, 60 seats).
11
+ - **Use** **`children`** for a short label or number in the center when it should match the visual focus of the ring.
12
+ - **Do not use** for indeterminate or endless loading without a numeric fraction; there is no indeterminate mode in the API.
13
+ - **Do not use** as the primary focus target or form control; the SVG is informational and not keyboard-focusable.
14
+ - **Do not use** expecting a polymorphic root or `asChild`; the implementation is a fixed wrapper with SVG plus optional inner slot.
15
+
16
+ ## Composition
17
+
18
+ - **`ProgressCircle`** exposes **`Root`** only (`ProgressCircle.Root`).
19
+ - **`ProgressCircle.Root`** — root `div` (`inline-flex`), sets **`data-size`** and a CSS variable for the inner slot size.
20
+ - **SVG** — **`role="progressbar"`** with track and fill circles; fill length follows **`value`** / **`max`**.
21
+ - **Optional `children`** — when present, rendered in an inner container centered over the ring; omit when the ring alone is enough.
22
+
23
+ ### Minimal example
24
+
25
+ ```tsx
26
+ import { ProgressCircle } from "prime-ui-kit";
27
+
28
+ export function Example() {
29
+ return <ProgressCircle.Root value={40} />;
30
+ }
31
+ ```
32
+
33
+ ## Rules
34
+
35
+ - Progress is **always driven by props**: pass **`value`** on each render; there is no internal stored progress state.
36
+ - **`value`** is clamped to **`[0, max]`**; negative values become `0`, values above **`max`** become **`max`**.
37
+ - If **`max <= 0`**, the implementation uses **`100`** as the scale to avoid division by zero.
38
+ - Set **`label`** when the SVG needs an accessible name and the center has no suitable visible text (it maps to **`aria-label`** on the SVG).
39
+ - The SVG exposes **`aria-valuenow`**, **`aria-valuemin={0}`**, and **`aria-valuemax`** equal to the effective maximum.
40
+ - There are no **`disabled`**, **`loading`**, or **`error`** props; reflect those with surrounding UI or by freezing **`value`** updates.
41
+ - Root is **`inline-flex`** and does not stretch to full width; place it inside your own flex or grid layout when aligning with other content.
42
+ - One visual style only; scale appearance with **`size`** (`s`–`xl` from progress-circle primitives).
43
+
44
+ ## API
45
+
46
+ ### ProgressCircle.Root
47
+
48
+ | Prop | Type | Default | Required | Description |
49
+ |------|------|---------|----------|-------------|
50
+ | value | `number` | — | Yes | Current value; clamped to `[0, max]` |
51
+ | max | `number` | `100` | No | Upper bound; if `max <= 0`, `100` is used |
52
+ | size | `"s" \| "m" \| "l" \| "xl"` | `"m"` | No | Diameter and stroke width from `progressCircle` primitives |
53
+ | label | `string` | — | No | Accessible name for the SVG (`aria-label`) |
54
+ | children | `React.ReactNode` | — | No | Centered content inside the ring |
55
+ | className | `string` | — | No | Extra class on the root `div` |
56
+ | ref | `React.Ref<HTMLDivElement>` | — | No | Ref on the root `div` |
57
+
58
+ ## Related
59
+
60
+ - [ProgressBar](../progress-bar/COMPONENT.md) — linear fraction of a maximum when a horizontal bar fits better.
61
+ - [SegmentedProgressBar](../segmented-progress-bar/COMPONENT.md) — multiple proportional segments on one track.
62
+ - [Typography](../typography/COMPONENT.md) — captions and units beside or around the ring.
63
+ - [Button](../button/COMPONENT.md) — cancel, retry, or other actions next to a long-running task.
@@ -0,0 +1,95 @@
1
+ # Radio
2
+
3
+ **Проектирование по умолчанию:** при проектировании экранов и примеров изначально выбирай **`m`** для `size` (где есть ось размера), если явно не оговорено иное.
4
+
5
+ ## About
6
+
7
+ A compound radio control: a field wrapper, a [Label](../label/COMPONENT.md) row with a native `input type="radio"` and decorative marker, plus optional [Hint](../hint/COMPONENT.md) and error text wired to `aria-describedby` and invalid state.
8
+
9
+ - **When to use** — exactly one choice from a set of mutually exclusive options that submit with the form (`name`, `value`, `required`).
10
+ - **When to use** — grouped options that should behave as a single native radio group (same `name` on each `Radio.Root`).
11
+ - **When to use** — hint or inline validation aligned under the label column.
12
+ - **When not to use** — multiple independent toggles or “select many” lists (prefer [Checkbox](../checkbox/COMPONENT.md)).
13
+ - **When not to use** — a single binary on/off setting where a switch matches the product language (prefer [Switch](../switch/COMPONENT.md)).
14
+ - **When not to use** — a compact segmented bar of modes in one control (prefer [Segmented control](../segmented-control/COMPONENT.md)).
15
+ - **When not to use** — you need `asChild` or fully custom markup; the marker and label row are fixed by the implementation.
16
+
17
+ ## Composition
18
+
19
+ - **`Radio.Root`** — `div` with `data-size`, `data-variant`, `data-disabled`, `data-invalid`; provides context and `ControlSizeProvider` for child parts (`inputId`, hint/error ids, `describedBy`, registration for hint/error slots).
20
+ - **`Radio.Label`** — [Label](../label/COMPONENT.md) with `htmlFor` tied to the input id; contains the native `input[type=radio]`, decorative SVG rings, and optional text in a trailing column.
21
+ - **`Radio.Hint`** — optional; registers presence so its id is merged into the input’s `aria-describedby`; uses disabled hint variant when the root is `disabled`.
22
+ - **`Radio.Error`** — optional; error-styled message and registers invalid state (same as `variant="error"` on the root for styling).
23
+ - **Order:** `Root` → `Label` → `Hint` and/or `Error` when needed. Public API: `Radio` with `Root`, `Label`, `Hint`, `Error`.
24
+
25
+ ### Minimal example
26
+
27
+ ```tsx
28
+ import { Radio } from "prime-ui-kit";
29
+
30
+ export function Example() {
31
+ return (
32
+ <Radio.Root name="option" value="a">
33
+ <Radio.Label>Option</Radio.Label>
34
+ </Radio.Root>
35
+ );
36
+ }
37
+ ```
38
+
39
+ ## Rules
40
+
41
+ - Support **controlled** (`checked` + `onChange`) and **uncontrolled** (`defaultChecked`); forward standard input change semantics from the native radio.
42
+ - Build a **group** with multiple **`Radio.Root`** instances sharing the same **`name`**; wrap in **`fieldset`** / **`legend`** when the group needs a visible or programmatic heading.
43
+ - There is **no** separate `RadioGroup` component—selection is native HTML behavior or your own controlled state.
44
+ - **`variant="error"`** or a mounted **`Radio.Error`** sets **`aria-invalid`** on the input and error styling on the root; **`disabled`** disables the input and dims the label/hint treatment.
45
+ - **`aria-describedby`** on the root is merged with hint and error ids when those slots mount.
46
+ - Set **`aria-label`** or ensure visible label text when **`Radio.Label`** has no readable children (icon-only or empty label).
47
+ - **`Radio.Root`** **`ref`** is forwarded to the **native `input`** element.
48
+ - DOM **`type`** is always **`radio`**; the design-system **`size`** prop lives on **`Radio.Root`** and does not map to the HTML `size` attribute (that key is omitted from input props).
49
+
50
+ ## API
51
+
52
+ ### Radio.Root
53
+
54
+ | Prop | Type | Default | Required | Description |
55
+ |------|------|---------|----------|-------------|
56
+ | variant | `"default" \| "error"` | `"default"` | no | Error styling and `data-invalid` when `error` or when `Radio.Error` is mounted. |
57
+ | size | `"s" \| "m" \| "l" \| "xl"` | `"m"` | no | Marker, typography, and hint/error scale. |
58
+ | id | `string` | auto (`useId`) | no | Input id; paired with `Radio.Label` via `htmlFor`. |
59
+ | className | `string` | — | no | Class on the field wrapper `div`. |
60
+ | disabled | `boolean` | — | no | Disables the input and label row. |
61
+ | aria-describedby | `string` | — | no | Combined with hint and error ids when those slots exist. |
62
+ | children | `React.ReactNode` | — | no | Typically `Label`, optional `Hint` / `Error`. |
63
+ | ref | `React.Ref<HTMLInputElement>` | — | no | Ref to the native radio input. |
64
+ | …rest | `Omit<React.InputHTMLAttributes<HTMLInputElement>, "type" \| "size">` | — | no | Other native attributes on the `input` (e.g. `name`, `value`, `checked`, `defaultChecked`, `onChange`, `required`, `readOnly`, `form`). `type` is always `radio`. |
65
+
66
+ ### Radio.Label
67
+
68
+ | Prop | Type | Default | Required | Description |
69
+ |------|------|---------|----------|-------------|
70
+ | children | `React.ReactNode` | — | no | Label text beside the marker; omit only if an accessible name is set on the root input via remaining root props (e.g. `aria-label`). |
71
+ | className | `string` | — | no | Class on the label row. |
72
+ | …rest | `Omit<React.HTMLAttributes<HTMLLabelElement>, "htmlFor" \| "size">` | — | no | Other label attributes; `htmlFor` and `size` come from context. |
73
+
74
+ ### Radio.Hint
75
+
76
+ | Prop | Type | Default | Required | Description |
77
+ |------|------|---------|----------|-------------|
78
+ | children | `React.ReactNode` | — | yes | Supplementary text below the label. |
79
+ | className | `string` | — | no | Additional class on the hint slot. |
80
+ | …rest | `Omit<React.HTMLAttributes<HTMLParagraphElement>, "id">` | — | no | Paragraph attributes; `id` is managed internally. |
81
+
82
+ ### Radio.Error
83
+
84
+ | Prop | Type | Default | Required | Description |
85
+ |------|------|---------|----------|-------------|
86
+ | children | `React.ReactNode` | — | yes | Error message text. |
87
+ | className | `string` | — | no | Additional class on the error slot. |
88
+ | …rest | `Omit<React.HTMLAttributes<HTMLParagraphElement>, "id">` | — | no | Paragraph attributes; `id` is managed internally for `aria-describedby`. |
89
+
90
+ ## Related
91
+
92
+ - [Checkbox](../checkbox/COMPONENT.md) — independent or multi-select toggles.
93
+ - [Switch](../switch/COMPONENT.md) — binary setting with a different control pattern.
94
+ - [Label](../label/COMPONENT.md), [Hint](../hint/COMPONENT.md) — primitives used inside Radio; pair with [Input](../input/COMPONENT.md) in larger forms.
95
+ - [Segmented control](../segmented-control/COMPONENT.md) — compact mode switching in one bar.