@rinzai/zen 0.14.0 → 0.15.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 (169) hide show
  1. package/CHANGELOG.md +138 -0
  2. package/README.md +16 -1
  3. package/dist/carousel/index.d.ts.map +1 -1
  4. package/dist/carousel/index.js +5 -5
  5. package/dist/docs/README.md +205 -0
  6. package/dist/docs/accordion.md +122 -0
  7. package/dist/docs/alert.md +69 -0
  8. package/dist/docs/animated-number.md +105 -0
  9. package/dist/docs/avatar.md +91 -0
  10. package/dist/docs/backdrop.md +94 -0
  11. package/dist/docs/badge.md +37 -0
  12. package/dist/docs/breadcrumbs.md +44 -0
  13. package/dist/docs/button-group.md +76 -0
  14. package/dist/docs/button.md +131 -0
  15. package/dist/docs/calendar-heatmap.md +73 -0
  16. package/dist/docs/calendar.md +101 -0
  17. package/dist/docs/canvas.md +584 -0
  18. package/dist/docs/card.md +180 -0
  19. package/dist/docs/carousel.md +162 -0
  20. package/dist/docs/chart.md +325 -0
  21. package/dist/docs/checkbox.md +88 -0
  22. package/dist/docs/chip.md +52 -0
  23. package/dist/docs/code-block.md +116 -0
  24. package/dist/docs/code-input.md +52 -0
  25. package/dist/docs/collapse.md +46 -0
  26. package/dist/docs/color-picker.md +145 -0
  27. package/dist/docs/combobox.md +208 -0
  28. package/dist/docs/command-palette.md +65 -0
  29. package/dist/docs/date-picker.md +167 -0
  30. package/dist/docs/dialog.md +271 -0
  31. package/dist/docs/disclosure.md +88 -0
  32. package/dist/docs/editable-cell.md +90 -0
  33. package/dist/docs/empty-state.md +46 -0
  34. package/dist/docs/field.md +82 -0
  35. package/dist/docs/file-drop.md +44 -0
  36. package/dist/docs/filter-bar.md +66 -0
  37. package/dist/docs/header.md +51 -0
  38. package/dist/docs/hover-card.md +110 -0
  39. package/dist/docs/icon-picker.md +65 -0
  40. package/dist/docs/input-group.md +89 -0
  41. package/dist/docs/input.md +33 -0
  42. package/dist/docs/inset.md +57 -0
  43. package/dist/docs/kbd.md +34 -0
  44. package/dist/docs/keypad.md +51 -0
  45. package/dist/docs/load-more.md +66 -0
  46. package/dist/docs/locale-provider.md +99 -0
  47. package/dist/docs/marquee.md +86 -0
  48. package/dist/docs/menu.md +403 -0
  49. package/dist/docs/meter.md +102 -0
  50. package/dist/docs/money-input.md +169 -0
  51. package/dist/docs/month-picker.md +80 -0
  52. package/dist/docs/navigation-menu.md +157 -0
  53. package/dist/docs/number-field.md +129 -0
  54. package/dist/docs/page-header.md +44 -0
  55. package/dist/docs/page-transition.md +93 -0
  56. package/dist/docs/pagination.md +42 -0
  57. package/dist/docs/pills.md +85 -0
  58. package/dist/docs/player-bar.md +158 -0
  59. package/dist/docs/popover.md +200 -0
  60. package/dist/docs/progress-ring.md +70 -0
  61. package/dist/docs/radio-cards.md +82 -0
  62. package/dist/docs/radio-group.md +86 -0
  63. package/dist/docs/range-slider.md +64 -0
  64. package/dist/docs/repeat-picker.md +44 -0
  65. package/dist/docs/resizable.md +107 -0
  66. package/dist/docs/seek-bar.md +68 -0
  67. package/dist/docs/segmented.md +89 -0
  68. package/dist/docs/select.md +148 -0
  69. package/dist/docs/separator.md +76 -0
  70. package/dist/docs/side-nav.md +75 -0
  71. package/dist/docs/skeleton.md +89 -0
  72. package/dist/docs/slider.md +87 -0
  73. package/dist/docs/sortable.md +182 -0
  74. package/dist/docs/sparkline.md +56 -0
  75. package/dist/docs/spinner.md +69 -0
  76. package/dist/docs/status-pill.md +81 -0
  77. package/dist/docs/stepper.md +59 -0
  78. package/dist/docs/tab-bar.md +78 -0
  79. package/dist/docs/table-of-contents.md +43 -0
  80. package/dist/docs/table.md +714 -0
  81. package/dist/docs/tabs.md +140 -0
  82. package/dist/docs/tag-input.md +45 -0
  83. package/dist/docs/textarea.md +25 -0
  84. package/dist/docs/theme-toggle.md +85 -0
  85. package/dist/docs/theming.md +193 -0
  86. package/dist/docs/time-field.md +71 -0
  87. package/dist/docs/timeline.md +50 -0
  88. package/dist/docs/toast.md +53 -0
  89. package/dist/docs/toggle-group.md +83 -0
  90. package/dist/docs/toggle.md +38 -0
  91. package/dist/docs/toolbar.md +97 -0
  92. package/dist/docs/tooltip.md +60 -0
  93. package/dist/docs/tree.md +295 -0
  94. package/dist/docs/trend.md +50 -0
  95. package/dist/icons/arrow-path.d.ts +3 -0
  96. package/dist/icons/arrow-path.d.ts.map +1 -0
  97. package/dist/icons/arrows-right-left.d.ts +3 -0
  98. package/dist/icons/arrows-right-left.d.ts.map +1 -0
  99. package/dist/icons/backward.d.ts +3 -0
  100. package/dist/icons/backward.d.ts.map +1 -0
  101. package/dist/icons/forward.d.ts +3 -0
  102. package/dist/icons/forward.d.ts.map +1 -0
  103. package/dist/icons/heart-solid.d.ts +3 -0
  104. package/dist/icons/heart-solid.d.ts.map +1 -0
  105. package/dist/icons/heart.d.ts +3 -0
  106. package/dist/icons/heart.d.ts.map +1 -0
  107. package/dist/icons/musical-note.d.ts +3 -0
  108. package/dist/icons/musical-note.d.ts.map +1 -0
  109. package/dist/icons/pause.d.ts +3 -0
  110. package/dist/icons/pause.d.ts.map +1 -0
  111. package/dist/icons/play.d.ts +3 -0
  112. package/dist/icons/play.d.ts.map +1 -0
  113. package/dist/icons/queue-list.d.ts +3 -0
  114. package/dist/icons/queue-list.d.ts.map +1 -0
  115. package/dist/icons/speaker-wave.d.ts +3 -0
  116. package/dist/icons/speaker-wave.d.ts.map +1 -0
  117. package/dist/icons/speaker-x-mark.d.ts +3 -0
  118. package/dist/icons/speaker-x-mark.d.ts.map +1 -0
  119. package/dist/index.d.ts +7 -1
  120. package/dist/index.d.ts.map +1 -1
  121. package/dist/lib.js +80 -74
  122. package/dist/marquee/index.d.ts +15 -0
  123. package/dist/marquee/index.d.ts.map +1 -0
  124. package/dist/marquee/index.js +90 -0
  125. package/dist/menu/index.d.ts.map +1 -1
  126. package/dist/menu/index.js +7 -6
  127. package/dist/player-bar/index.d.ts +11 -0
  128. package/dist/player-bar/index.d.ts.map +1 -0
  129. package/dist/player-bar/index.js +17 -0
  130. package/dist/radio-cards/index.d.ts +2 -1
  131. package/dist/radio-cards/index.d.ts.map +1 -1
  132. package/dist/radio-cards/index.js +3 -3
  133. package/dist/seek-bar/index.d.ts +24 -0
  134. package/dist/seek-bar/index.d.ts.map +1 -0
  135. package/dist/seek-bar/index.js +120 -0
  136. package/dist/segmented/index.d.ts +3 -2
  137. package/dist/segmented/index.d.ts.map +1 -1
  138. package/dist/segmented/index.js +19 -18
  139. package/dist/slider/index.d.ts +2 -1
  140. package/dist/slider/index.d.ts.map +1 -1
  141. package/dist/tab-bar/index.d.ts +4 -2
  142. package/dist/tab-bar/index.d.ts.map +1 -1
  143. package/dist/tab-bar/index.js +24 -19
  144. package/dist/textarea/index.d.ts +6 -2
  145. package/dist/textarea/index.d.ts.map +1 -1
  146. package/dist/textarea/index.js +2 -1
  147. package/dist/theme.css +40 -0
  148. package/dist/utils/duration.d.ts +3 -0
  149. package/dist/utils/duration.d.ts.map +1 -0
  150. package/dist/utils/duration.js +7 -0
  151. package/dist/utils/messages.d.ts +2 -0
  152. package/dist/utils/messages.d.ts.map +1 -1
  153. package/dist/utils/messages.js +3 -1
  154. package/dist/utils/slot.d.ts +5 -2
  155. package/dist/utils/slot.d.ts.map +1 -1
  156. package/dist/utils/slot.js +16 -11
  157. package/dist/utils/useAnchoredPopup.d.ts.map +1 -1
  158. package/dist/utils/useAnchoredPopup.js +3 -2
  159. package/dist/utils/useHeightVar.d.ts +7 -0
  160. package/dist/utils/useHeightVar.d.ts.map +1 -0
  161. package/dist/utils/useHeightVar.js +17 -0
  162. package/dist/utils/useMediaPlayer.d.ts +38 -0
  163. package/dist/utils/useMediaPlayer.d.ts.map +1 -0
  164. package/dist/utils/useMediaPlayer.js +91 -0
  165. package/dist/utils/useMediaSession.d.ts +24 -0
  166. package/dist/utils/useMediaSession.d.ts.map +1 -0
  167. package/dist/utils/useMediaSession.js +76 -0
  168. package/dist/zen.css +1 -1
  169. package/package.json +4 -2
package/CHANGELOG.md ADDED
@@ -0,0 +1,138 @@
1
+ # Changelog
2
+
3
+ ## 0.15.0
4
+
5
+ ### New components
6
+
7
+ - **PlayerBar**: what's playing, in a glass bar along the bottom of the screen, laid out as you like. On phones it sits on the TabBar beside it.
8
+ - **SeekBar**: where playback is, filled to the time played and shaded on to what's loaded, between the time played and the length. Pass `playing` and it glides between the player's time updates, moved by the browser's compositor rather than per-frame renders. Dragging doesn't interrupt playback; letting go seeks; the arrow keys seek 5 seconds.
9
+ - **Marquee**: one line of text cut off with an ellipsis that scrolls to show the rest: on hover or keyboard focus of itself or of its `data-zen-marquee-host` (a row, a tile), or always (`trigger="always"`, for what's playing). Only text that doesn't fit moves, at the same speed whatever its length, with fades at the edges. Stays an ellipsis for reduced motion; the accessible name is the text.
10
+ - **useMediaPlayer**: drives your own `<audio>` or `<video>` (`const [ref, player] = useMediaPlayer()`, then `<audio ref={ref} />`) and keeps its state: playing, waiting, time, duration, buffered, volume. With `play`, `pause`, `toggle`, `seek`, `setVolume` and `setMuted`.
11
+ - **useMediaSession**: puts what's playing on the lock screen and the OS media controls (title, artist, cover, position), and lets play, pause, seek, previous, next and the media keys control your player.
12
+ - **formatDuration**: seconds as a clock reads them (3:07, 1:02:07).
13
+
14
+ ### Renamed
15
+
16
+ - **TextArea** is now **Textarea** (and `TextAreaProps` is `TextareaProps`), like the element. The old names still work, marked deprecated, until the next breaking release.
17
+
18
+ ### Added to existing components
19
+
20
+ - **TabBar**: its height is kept in `--zen-tab-bar-height` on its parent (0px while hidden), for a PlayerBar to sit on and the page to pad its end by. PlayerBar keeps `--zen-player-bar-height` the same way.
21
+ - **Segmented**, **RadioCards**: `label` is optional. Without it, name the group with `aria-label` or `aria-labelledby`, as on RadioGroup.
22
+ - **Slider**: `step="any"` for no snapping.
23
+
24
+ ### Fixes
25
+
26
+ - **SegmentedItem**: an icon and its text sit side by side; they stacked.
27
+ - **Popover, Menu, Select and every anchored popup**: with no room below the trigger, a centred popup wider than its trigger stayed below it, off screen (Chrome) or over the trigger (Firefox), instead of flipping above. It flips now, and a popup lined up with an edge also switches edges when there's no room beside it.
28
+ - **Carousel**: where the slides don't fill the view evenly, Next stopped short of the end, the last slide cut off. The end is now the last stop, with its own dot.
29
+ - **TabBarItem**, **MenuItem**: with `asChild`, `icon` was dropped; it now goes ahead of your link's content, also when that content is a render function (a NavLink's).
30
+
31
+ ### Docs
32
+
33
+ - The docs ship in the package as markdown, for AI agents: `node_modules/@rinzai/zen/dist/docs` has a page per component with every example's source, and the CHANGELOG is included. The site serves the same pages at `/docs/<component>.md`, with `/llms.txt` and `/llms-full.txt`.
34
+ - Props tables cover parts kept outside a package's `index.tsx`, such as SortableTree, SortableItem, DonutChart and the table's selection and swipe parts.
35
+ - The package's markdown pages have the props tables too, the same as the site's. Their README and `llms.txt` carry the README's guide: setup, the props every component takes, values and forms, languages, theming, overlays and browser support. The README now says Zen exports no icons.
36
+
37
+ ## 0.14.0
38
+
39
+ ### Added to existing components
40
+
41
+ - **NumberField**: `onValueChange` gets a second argument saying how the number changed: `{ reason: 'step', delta }` for − +, ↑↓ and Page Up/Down, `'input'` for typing, `'limit'` for Home and End. `onValueCommit` fires once per finished change: each step, and what's typed on Enter or on leaving the field. So a step can be sent on as a step to whatever keeps the number (see Number Field → Steps).
42
+
43
+ ## 0.13.0
44
+
45
+ Since 0.12.0. This release renames things. Every breaking change is listed first, with what to change.
46
+
47
+ ### Breaking changes
48
+
49
+ #### Callbacks are named for what they change
50
+
51
+ | Before | After |
52
+ | -------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
53
+ | `onChange` | `onValueChange` on Select, Segmented, RadioGroup, RadioCards, Calendar, DatePicker, DateRangePicker, MonthPicker, MoneyInput, ColorPicker, ColorPanel, IconPicker, RepeatPicker and SortableList (Combobox, Keypad, Slider, RangeSlider, TagInput and Tabs already used it) |
54
+ | `onChange(checked)` on Checkbox, Toggle | `onCheckedChange(checked)`; `onChange` is the native event again |
55
+ | Canvas `onChange` / `onChangeEnd` / `onSelect` | `onItemsChange` / `onItemsChangeEnd` / `onSelectedChange` |
56
+ | CalendarHeatmap `onSelect` | `onSelectedChange` |
57
+ | DatePicker, DateRangePicker, MonthPicker `onClear` | `clearable`; clearing calls `onValueChange(null)` |
58
+
59
+ `value` is optional wherever it was required. Leave it out and pass `defaultValue` to let the control keep its own.
60
+
61
+ #### One tone scale: `default`, `positive`, `negative`
62
+
63
+ | Before | After |
64
+ | --------------------------------------------------------------- | ------------------------------------------ |
65
+ | `destructive` (Button, Badge, ChartReference, TableSwipeAction) | `tone="negative"` |
66
+ | `danger` (Meter), `error` (Toast, FormMessage) | `negative` |
67
+ | `success` | `positive` |
68
+ | `info` (Toast, FormMessage), `primary` (Sparkline) | `default` |
69
+ | MenuItem, ConfirmDialog `destructive` | `tone="negative"` |
70
+ | Button `variant="destructive"` | `tone="negative"` (on the default variant) |
71
+ | Button `variant="icon"` | `variant="ghost"` with an icon size |
72
+
73
+ #### Sizes
74
+
75
+ - Icon buttons line up with fields and buttons of the same size: `icon-sm` is 32px, `icon` is 40px (was 36), `icon-lg` is 44px. The old 24px `icon-sm` is now `icon-xs`.
76
+ - ThemeToggle is 40px.
77
+ - Input's `size` is `"sm" | "lg"` (Button's heights). It no longer passes through the native `size` attribute.
78
+
79
+ #### Right to left
80
+
81
+ - Table: `sticky="left"` is `sticky="start"`.
82
+ - TableContainer's `data-under-left` attribute is `data-under-start`.
83
+ - The `.zen__sticky-left` class is `.zen__sticky-start`.
84
+ - Spacing, corners and positions use logical sides, so a class you override may now be `ps-*`/`pe-*`/`start-*`/`end-*`/`text-start` rather than `pl-*`/`left-*`/`text-left`.
85
+
86
+ #### Forms
87
+
88
+ - MoneyInput with `name` submits the amount in minor units, from a hidden input (e.g. `123450` for $1,234.50). It used to submit the visible text.
89
+
90
+ #### Words
91
+
92
+ - `ICON_COLORS` entries no longer have English labels: `IconColor.label` is optional, and the names come from the messages.
93
+ - `ImageCropError` takes a `reason` code first, `new ImageCropError(reason, message)`, so you can show the error in your own words.
94
+ - `describeRepeat` takes the words as an optional last argument.
95
+
96
+ ### New components
97
+
98
+ - **Accordion**: disclosures where one or several (`multiple`) open at a time, with ↑↓, Home and End between headings.
99
+ - **Separator**: across or upright, and `decorative` when it's only visual.
100
+ - **ToggleGroup**: buttons that stay pressed, one or several (`multiple`).
101
+ - **Toolbar**: one Tab stop, with arrow keys through its controls, a ToggleGroup's items included. Runs across or upright.
102
+ - **ButtonGroup**: buttons joined into one control, a split button's menu included.
103
+ - **HoverCard**: a glass preview of what a link points to, on hover or keyboard focus.
104
+ - **NumberField**: a number with − and + buttons and arrow keys (PageUp/PageDown for larger steps). Typed and shown in the locale's format, including percent.
105
+ - **TimeField**: hours, minutes and AM/PM as segments, in the locale's order and clock.
106
+ - **Carousel**: native scroll snapping, with glass previous/next buttons and dots in CarouselControls, and the arrow keys.
107
+ - **Resizable** (ResizablePanelGroup, ResizablePanel, ResizableHandle): drag a handle or use the arrow keys, within each panel's limits. Groups nest, and `onLayout` reports the sizes.
108
+ - **Menubar**: a row of menus with one Tab stop. ←→ move between them; with one open, ←→ open the next and hover switches.
109
+ - **NavigationMenu**: site navigation with panels of links that open on hover or click.
110
+ - **SortableTree**: drag-and-drop for groups and nested items. Drop into another group, into an open sub-item, or under another item; `accepts` says what goes where. Keyboard: ↑↓ through every allowed place, ←→ a level out or in, each step announced. Works on table rows (see Table → Reorder).
111
+ - **LocaleProvider**: the locale and every word Zen shows or announces. Pass a partial `messages` to translate or reword any of them (`ENGLISH` lists them all).
112
+
113
+ ### Added to existing components
114
+
115
+ - **Menu**:
116
+ - Submenus (MenuSub, MenuSubTrigger, MenuSubContent).
117
+ - MenuCheckboxItem, and MenuRadioGroup with MenuRadioItem.
118
+ - ↓ or ↑ on a MenuTrigger opens the menu at its first or last item.
119
+ - **SortableList**: `asChild` on the list and its items, to sort your own elements, such as a TableBody's rows.
120
+ - **Fields** (Input, InputGroup, MoneyInput, Select, Combobox, DatePicker, DateRangePicker, MonthPicker, ColorPicker): `size="sm" | "lg"`, at Button's heights.
121
+ - **Form submission and reset**: custom controls with a `name` are submitted with their form and reset with it. These are Combobox, the date and month pickers, MoneyInput, ColorPicker, TagInput, RepeatPicker, RangeSlider and IconPicker.
122
+ - **Props and refs**: `id`, `data-*`, `aria-*`, `style`, handlers and `ref` reach every component's element. Where a component keeps its own ref, yours is merged with it.
123
+ - **Exported props types**: every component's props type is exported (`ButtonProps`, `TabsProps`…).
124
+ - **Server components**: `'use client'` marks only modules that use React. Money, dates, `cx`, colours and the package entry can be called from server components.
125
+ - **Right to left**: layout, arrow keys, chevrons and slider fills follow `dir`. Calendar, CalendarHeatmap and Pagination use the locale's digits.
126
+ - **Focus**: every control uses the same keyboard focus ring (the `focus-ring` utility).
127
+
128
+ ### Fixes
129
+
130
+ - Calendar marks today only once hydrated, so server and client render the same.
131
+ - Combobox's list is named after its field.
132
+ - Tabs without panels no longer point `aria-controls` at nothing.
133
+ - Closed popovers and dialogs stay hidden whatever `className` lays them out with.
134
+ - HoverCard and Tooltip are placed against their trigger in browsers without CSS anchor positioning.
135
+
136
+ ### Tests
137
+
138
+ - Behaviour tests now run in Chromium, Firefox and WebKit, with new keyboard tests and accessibility checks of open popups in both themes. CI runs them.
package/README.md CHANGED
@@ -57,18 +57,33 @@ export default function App() {
57
57
  }
58
58
  ```
59
59
 
60
+ ## For AI agents
61
+
62
+ Every docs page ships with the package as markdown, matching the installed version: `node_modules/@rinzai/zen/dist/docs` (README.md lists them), with the CHANGELOG at the package root. Point your agent there from your `AGENTS.md` or `CLAUDE.md`:
63
+
64
+ ```md
65
+ UI components come from @rinzai/zen. Its docs, with every example's source, are in node_modules/@rinzai/zen/dist/docs; start at README.md there.
66
+ ```
67
+
68
+ The site has them too: [llms.txt](https://zen.rinzai.dev/llms.txt), [llms-full.txt](https://zen.rinzai.dev/llms-full.txt) (everything in one file) and `/docs/<component>.md`.
69
+
60
70
  ## What's inside
61
71
 
62
72
  - **Surfaces:** Card, Inset, Alert, Stat, Dialog (and sheets from an edge), ConfirmDialog, Popover, Tooltip, HoverCard, Toast (a stacked deck), Disclosure, Accordion, Separator, Resizable panels, Carousel, Backdrop (aurora, contours or dots, and a pointer light)
63
- - **Inputs:** Button, ButtonGroup, Toggle, ToggleGroup, Toolbar, Input, TextArea, InputGroup, NumberField, TimeField, MoneyInput (with currency conversion), Select, Combobox, Calendar, DatePicker, DateRangePicker, MonthPicker, ColorPicker, Checkbox, RadioGroup, RadioCards, Segmented, Slider, Chip, Field
73
+ - **Inputs:** Button, ButtonGroup, Toggle, ToggleGroup, Toolbar, Input, Textarea, InputGroup, NumberField, TimeField, MoneyInput (with currency conversion), Select, Combobox, Calendar, DatePicker, DateRangePicker, MonthPicker, ColorPicker, Checkbox, RadioGroup, RadioCards, Segmented, Slider, Chip, Field
64
74
  - **Data:** Chart (areas, lines, bars) and DonutChart in plain SVG, composable Table with sticky edges, sorting and rows reordered by drag and drop, tree rows, editable cells, virtual rows, SortableList and SortableTree, Meter, ProgressRing, AnimatedNumber, Badge, StatusPill (a live save status), Skeleton, Spinner, Canvas (items placed freely, with snapping)
65
75
  - **Navigation:** Header, NavigationMenu, Pills, Tabs, TabBar, SideNav, TableOfContents, Menu (with submenus, checkbox and radio items, and context menus), Menubar
76
+ - **Media:** PlayerBar (sits on a TabBar), SeekBar, Marquee (cut-off titles that scroll to show the rest), `useMediaPlayer` for your own `<audio>` or `<video>`, `useMediaSession` for the lock screen and media keys, `formatDuration`
66
77
  - **Theming:** dark and light, OKLCH tokens, and a theme generator (presets or any hue) that keeps contrast readable
67
78
  - **Languages:** LocaleProvider for the locale and every word, and right-to-left layout
68
79
  - **Hooks and utilities:** `themeWave` (the theme spreads from the switch like a drop falling into water: ripples, glints and a rainbow edge, drawn on the GPU; `prepareThemeWave` readies it when the pointer reaches your own switch), `useAnchoredPopup`, `useVirtualList`, `useTree`, `useSort`, `useCopyToClipboard`, money, currency, month and date helpers, `hexToOklch` and `oklchToHex`, `cx` (Tailwind-aware class merging), `cva`
69
80
 
81
+ ## Props
82
+
70
83
  Every component takes `className`, and a class you pass always overrides the component's default. Other props (`id`, `aria-*`, `data-*`, handlers) and `ref` reach its element, and parts that render a link or button take `asChild` to use your own (a router's Link, say).
71
84
 
85
+ Zen exports no icons. Use any icon set (the examples use [Heroicons](https://heroicons.com)) and pass icons in as elements, such as `icon={<HomeIcon />}`.
86
+
72
87
  ## Values and forms
73
88
 
74
89
  Controls report changes with `onValueChange` (`onCheckedChange` for Checkbox and Toggle). Pass `value` to control one, or `defaultValue` to let it keep its own. Give a control a `name` and it's submitted with its form, the custom ones (Combobox, the date pickers, MoneyInput, ColorPicker, TagInput…) included, and goes back to its first value when the form is reset.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../packages/carousel/index.tsx"],"names":[],"mappings":"AAMA,OAAO,EACH,cAAc,EASjB,MAAM,OAAO,CAAC;AA8Bf;;;;;;GAMG;AACH,MAAM,CAAC,OAAO,UAAU,QAAQ,CAAC,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAE,EAAE,aAAa,+BAoE/E;AAED,mCAAmC;AACnC,wBAAgB,eAAe,CAAC,EAAE,GAAG,EAAE,SAAS,EAAE,GAAG,IAAI,EAAE,EAAE,cAAc,CAAC,KAAK,CAAC,+BAiBjF;AAED,sDAAsD;AACtD,wBAAgB,YAAY,CAAC,EAAE,GAAG,EAAE,SAAS,EAAE,GAAG,IAAI,EAAE,EAAE,cAAc,CAAC,KAAK,CAAC,+BAoB9E;AAKD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,GAAG,IAAI,EAAE,EAAE,cAAc,CAAC,QAAQ,CAAC,+BAoBzF;AAED,mEAAmE;AACnE,wBAAgB,YAAY,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,GAAG,IAAI,EAAE,EAAE,cAAc,CAAC,QAAQ,CAAC,+BAmBrF;AAED,sGAAsG;AACtG,wBAAgB,YAAY,CAAC,EAAE,SAAS,EAAE,GAAG,IAAI,EAAE,EAAE,cAAc,CAAC,KAAK,CAAC,sCA6BzE;AAED,8GAA8G;AAC9G,wBAAgB,gBAAgB,CAAC,EAAE,SAAS,EAAE,GAAG,IAAI,EAAE,EAAE,cAAc,CAAC,KAAK,CAAC,+BAE7E;AAED,MAAM,MAAM,aAAa,GAAG,cAAc,CAAC,SAAS,CAAC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../packages/carousel/index.tsx"],"names":[],"mappings":"AAMA,OAAO,EACH,cAAc,EASjB,MAAM,OAAO,CAAC;AA8Bf;;;;;;GAMG;AACH,MAAM,CAAC,OAAO,UAAU,QAAQ,CAAC,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAE,EAAE,aAAa,+BA0E/E;AAED,mCAAmC;AACnC,wBAAgB,eAAe,CAAC,EAAE,GAAG,EAAE,SAAS,EAAE,GAAG,IAAI,EAAE,EAAE,cAAc,CAAC,KAAK,CAAC,+BAiBjF;AAED,sDAAsD;AACtD,wBAAgB,YAAY,CAAC,EAAE,GAAG,EAAE,SAAS,EAAE,GAAG,IAAI,EAAE,EAAE,cAAc,CAAC,KAAK,CAAC,+BAoB9E;AAKD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,GAAG,IAAI,EAAE,EAAE,cAAc,CAAC,QAAQ,CAAC,+BAoBzF;AAED,mEAAmE;AACnE,wBAAgB,YAAY,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,GAAG,IAAI,EAAE,EAAE,cAAc,CAAC,QAAQ,CAAC,+BAmBrF;AAED,sGAAsG;AACtG,wBAAgB,YAAY,CAAC,EAAE,SAAS,EAAE,GAAG,IAAI,EAAE,EAAE,cAAc,CAAC,KAAK,CAAC,sCA6BzE;AAED,8GAA8G;AAC9G,wBAAgB,gBAAgB,CAAC,EAAE,SAAS,EAAE,GAAG,IAAI,EAAE,EAAE,cAAc,CAAC,KAAK,CAAC,+BAE7E;AAED,MAAM,MAAM,aAAa,GAAG,cAAc,CAAC,SAAS,CAAC,CAAC"}
@@ -26,11 +26,11 @@ function v({ className: t, children: i, ...a }) {
26
26
  }), g = s(() => {
27
27
  let e = c.current;
28
28
  if (!e) return;
29
- let t = Array.from(e.children), n = Math.abs(e.scrollLeft), r = e.scrollWidth - e.clientWidth, i = t[0] ? _(t[0], e) : 0, a = t.map((t) => _(t, e) - i), o = Math.max(1, a.filter((e) => e <= r + 1).length), s = 0;
29
+ let t = Array.from(e.children), n = Math.abs(e.scrollLeft), r = e.scrollWidth - e.clientWidth, i = t[0] ? _(t[0], e) : 0, a = t.map((t) => _(t, e) - i), o = a.filter((e) => e < r - 1).length + 1, s = (e) => Math.min(a[e], r), l = 0;
30
30
  a.forEach((e, t) => {
31
- t < o && Math.abs(e - n) < Math.abs(a[s] - n) && (s = t);
32
- }), r > 0 && n >= r - 1 && (s = o - 1), u((e) => e.index === s && e.stops === o && e.count === t.length ? e : {
33
- index: s,
31
+ t < o && Math.abs(s(t) - n) < Math.abs(s(l) - n) && (l = t);
32
+ }), r > 0 && n >= r - 1 && (l = o - 1), u((e) => e.index === l && e.stops === o && e.count === t.length ? e : {
33
+ index: l,
34
34
  stops: o,
35
35
  count: t.length
36
36
  });
@@ -55,7 +55,7 @@ function v({ className: t, children: i, ...a }) {
55
55
  let v = s((e) => {
56
56
  let t = c.current, n = t?.children[e];
57
57
  if (!t || !n) return;
58
- let i = t.children[0], a = _(n, t) - _(i, t), o = getComputedStyle(t).direction === "rtl";
58
+ let i = t.children[0], a = Math.min(_(n, t) - _(i, t), t.scrollWidth - t.clientWidth), o = getComputedStyle(t).direction === "rtl";
59
59
  t.scrollTo({
60
60
  left: o ? -a : a,
61
61
  behavior: r() ? "instant" : "smooth"
@@ -0,0 +1,205 @@
1
+ # Zen
2
+
3
+ > React components in dark glass: translucent surfaces, hairline borders that catch a pointer light, one accent colour. Tailwind v4, no runtime dependencies.
4
+
5
+ ## Installation
6
+
7
+ ```sh
8
+ pnpm add @rinzai/zen
9
+ ```
10
+
11
+ React 19 is the only peer dependency.
12
+
13
+ **Using Tailwind CSS v4?** Add Zen's theme after Tailwind in your stylesheet. Your one Tailwind build then generates everything Zen's components use:
14
+
15
+ ```css
16
+ @import 'tailwindcss';
17
+ @import '@rinzai/zen/tailwind.css';
18
+ ```
19
+
20
+ **Not using Tailwind?** Import the complete stylesheet once, near the root of your app. It includes the default theme:
21
+
22
+ ```js
23
+ import '@rinzai/zen/css';
24
+ ```
25
+
26
+ Zen never styles your own elements. For its page setup (the background showing through), heading scale, inline code and quiet scrollbars, also add the optional base:
27
+
28
+ ```css
29
+ @import '@rinzai/zen/base.css';
30
+ ```
31
+
32
+ The look is set in [Geist](https://vercel.com/font); load it however you like.
33
+
34
+ ## Usage
35
+
36
+ ```tsx
37
+ import { Backdrop, Button, Card, CardHeader, CardTitle } from '@rinzai/zen';
38
+
39
+ export default function App() {
40
+ return (
41
+ <>
42
+ <Backdrop />
43
+ <Card>
44
+ <CardHeader>
45
+ <CardTitle>Budget</CardTitle>
46
+ </CardHeader>
47
+ <Button>Save changes</Button>
48
+ </Card>
49
+ </>
50
+ );
51
+ }
52
+ ```
53
+
54
+ ## Props
55
+
56
+ Every component takes `className`, and a class you pass always overrides the component's default. Other props (`id`, `aria-*`, `data-*`, handlers) and `ref` reach its element, and parts that render a link or button take `asChild` to use your own (a router's Link, say).
57
+
58
+ Zen exports no icons. Use any icon set (the examples use [Heroicons](https://heroicons.com)) and pass icons in as elements, such as `icon={<HomeIcon />}`.
59
+
60
+ ## Values and forms
61
+
62
+ Controls report changes with `onValueChange` (`onCheckedChange` for Checkbox and Toggle). Pass `value` to control one, or `defaultValue` to let it keep its own. Give a control a `name` and it's submitted with its form, the custom ones (Combobox, the date pickers, MoneyInput, ColorPicker, TagInput…) included, and goes back to its first value when the form is reset.
63
+
64
+ ## Languages and right to left
65
+
66
+ ```tsx
67
+ import { LocaleProvider } from '@rinzai/zen';
68
+
69
+ <LocaleProvider locale="fr-FR" messages={{ clear: 'Effacer', noResults: 'Aucun résultat' }}>
70
+ <App />
71
+ </LocaleProvider>;
72
+ ```
73
+
74
+ The locale formats dates, numbers and money, and sets the week's first day. `messages` replaces any of the words Zen shows or announces (`ENGLISH` lists them all); the rest stay English. With `dir="rtl"` on the page or any element above, layout, arrow keys and chevrons mirror.
75
+
76
+ ## Theming
77
+
78
+ Dark is the default. Add the `light` class (or `data-theme="light"`) for the light palette. Colours are OKLCH `L C H` triplets; override any token in your own CSS:
79
+
80
+ ```css
81
+ :root {
82
+ --primary: 0.72 0.13 200; /* teal instead of violet */
83
+ --glow: 0.7 0.13 200;
84
+ }
85
+ ```
86
+
87
+ Or generate a whole theme at runtime:
88
+
89
+ ```ts
90
+ import { applyPreset, applyTheme, DEFAULT_THEME } from '@rinzai/zen';
91
+
92
+ applyTheme(applyPreset(DEFAULT_THEME, 'ocean'));
93
+ ```
94
+
95
+ ## Overlays
96
+
97
+ For a page composited over something else, such as an OBS browser source, which never gets a pointer:
98
+
99
+ ```html
100
+ <html data-zen-glow="ambient" data-zen-page="transparent"></html>
101
+ ```
102
+
103
+ `data-zen-glow="ambient"` sends the edge light round each glowing border by itself (on any element, it does so for what's inside). `data-zen-page="transparent"` paints nothing behind the content; it needs the base stylesheet.
104
+
105
+ ## Browser support
106
+
107
+ Chrome and Edge 114+, Safari 17+ and Firefox 128+: the versions with the native Popover API that popups, menus and toasts are built on, and with what Tailwind v4 needs.
108
+
109
+ - **Popups** are placed with CSS anchor positioning where the browser has it (Chrome 129+, Safari 26+, recent Firefox). Elsewhere Zen places them itself, the same way.
110
+ - **Glass** is blurred live where there's a GPU and a mouse. On touch screens, without hardware acceleration, and with the OS's "reduce transparency", surfaces are solid instead.
111
+ - **Motion** such as entry animations is left out where a browser lacks it, and for anyone who prefers reduced motion.
112
+
113
+ Breaking changes and additions by version are in ../../CHANGELOG.md.
114
+
115
+ ## Components
116
+
117
+ - [Accordion](./accordion.md): Sections under headings that open one at a time, or several: questions and answers, or groups of settings.
118
+ - [Alert](./alert.md): A notice, warning or failure beside what it is about: a tinted box, or a plain line in a form or card.
119
+ - [Animated Number](./animated-number.md): Numbers and amounts that count smoothly to each new value.
120
+ - [Avatar](./avatar.md): A round picture, or the name's initial on the glowing accent. With an in-browser crop for uploads.
121
+ - [Backdrop](./backdrop.md): The page background: aurora glows over contours or dots, and a light that follows the pointer and catches card edges, or drifts round them by itself on pages with no pointer.
122
+ - [Badge](./badge.md): A small label for status, counts and tags.
123
+ - [Breadcrumbs](./breadcrumbs.md): Where a page sits: its parents as links, then the page itself. Works with any router.
124
+ - [Button](./button.md): Actions, from the one glowing primary action to quiet ghost buttons.
125
+ - [Button Group](./button-group.md): Buttons joined into one, rounded only at the ends: a split button, or Previous, Today and Next.
126
+ - [Calendar](./calendar.md): A month grid to pick a date or a range, fully keyboard-driven, with limits, blocked days and a year view to jump far.
127
+ - [Calendar Heatmap](./calendar-heatmap.md): A month of days shaded by their value, like spending per day.
128
+ - [Canvas](./canvas.md): A fixed-size surface where items sit anywhere, like the cards of a stream overlay: drag, resize and select several, by pointer, touch or keyboard, with snapping, a grid, undo and a fit-to-width scale.
129
+ - [Card](./card.md): Glass panels for grouping content, and stats for single figures.
130
+ - [Carousel](./carousel.md): Slides in a row that snap as you swipe or scroll, with glass buttons and dots that light the current one beneath, and the arrow keys. Native scrolling, so it follows the finger exactly.
131
+ - [Chart](./chart.md): Areas, lines and bars on one axis, a donut and an allocation bar: plain SVG that draws itself in, with a crosshair tooltip, keyboard reading, a legend and a table for screen readers.
132
+ - [Checkbox](./checkbox.md): A native checkbox whose tick draws itself in, with a label, a description and a mixed state.
133
+ - [Chip](./chip.md): Small pressable buttons for quick picks beside a field, like common tenures.
134
+ - [Code Block](./code-block.md): Code with a copy button; bring your own highlighter for colours. The copy button runs on useCopyToClipboard, which you can use for copy buttons anywhere.
135
+ - [Code Input](./code-input.md): A one-time code, a box per character, that types, pastes and autofills from a text message like any field.
136
+ - [Collapse](./collapse.md): Shows as many items as fit on one line, and the rest behind "+N".
137
+ - [Color Picker](./color-picker.md): Pick a colour by eye or by hex: a saturation and brightness area, hue and opacity, and your own swatches. With hexToOklch to give a colour to the theme engine.
138
+ - [Combobox](./combobox.md): Search and pick one or several items of any shape, composed from parts: field, panel, search, a virtualized list, empty and create rows.
139
+ - [Command Palette](./command-palette.md): Search everything and act on it from the keyboard: a field over a list that narrows as you type, opened with ⌘K.
140
+ - [Date Picker](./date-picker.md): A field that opens a calendar: one date, or a range previewed as you point. Dates are "YYYY-MM-DD" strings, so they never shift with time zones.
141
+ - [Dialog](./dialog.md): Modal dialogs on the native <dialog> element, sheets that slide in from an edge, and a confirmation for destructive actions.
142
+ - [Disclosure](./disclosure.md): A button that shows and hides a section, like finished goals under a list.
143
+ - [Editable Cell](./editable-cell.md): A value edited in place: shows the value, swaps in any editor on click or Enter.
144
+ - [Empty State](./empty-state.md): What a list or page shows when there's nothing in it yet, and the action that fills it.
145
+ - [Field](./field.md): A label, hint and error wired to their control for screen readers, and messages for a whole form.
146
+ - [File Drop](./file-drop.md): Somewhere to drop files, or click to choose them, that lights up as files are dragged over it.
147
+ - [Filter Bar](./filter-bar.md): The filters in effect, each removed with its ×, and Clear all.
148
+ - [Header](./header.md): A sticky glass bar across the top of the page.
149
+ - [Hover Card](./hover-card.md): A preview of what a link points to, like a person or an account: a glass card once the pointer rests on the link, or while it has keyboard focus.
150
+ - [Icon Picker](./icon-picker.md): An icon and a colour for something, like a spending category, from a grid in a popover.
151
+ - [Input](./input.md): Text fields in faint glass that glow on focus.
152
+ - [Input Group](./input-group.md): A field with slots: put symbols, icons, units, buttons or menus before and after the input.
153
+ - [Inset](./inset.md): A faint box set into a card: a tile for one figure, or a bordered panel for a result, tinted for good or bad news.
154
+ - [Kbd](./kbd.md): Keys on the keyboard, for shortcuts in hints and tooltips.
155
+ - [Keypad](./keypad.md): A number pad for typing an amount on a phone.
156
+ - [Load More](./load-more.md): The end of a long list that has more: loads the next page when it scrolls into view, or on a press.
157
+ - [Locale Provider](./locale-provider.md): The language for everything inside: how dates, numbers and money read, and every word components show or announce. Without it, "en-US" and English.
158
+ - [Marquee](./marquee.md): One line of text cut off with an ellipsis, that scrolls to show the rest on hover or focus (of itself or of its row), or all the time. Only text that doesn't fit moves.
159
+ - [Menu](./menu.md): A button that opens a menu, or a context menu from a right-click or long press, with your own trigger, a header, icons, groups, destructive items, checkbox and radio items, submenus, and a menubar of menus.
160
+ - [Meter](./meter.md): A measure against a limit, like spend against a budget: warm near it, red past it.
161
+ - [Money Input](./money-input.md): Amounts in any currency and locale, with shorthand like 1.5L or 10k and sums like 120+45.50, exact to the paisa, and typed in other currencies, converted at your rates. Built on Input Group.
162
+ - [Month Picker](./month-picker.md): A field showing a month that opens a year and a 12-month grid, with optional limits and clearing.
163
+ - [Navigation Menu](./navigation-menu.md): A site’s navigation: links, and labels that open glass panels of links on hover or click, switching at once between them; works with any router.
164
+ - [Number Field](./number-field.md): A number to type or step with − and +, the arrow keys and Page Up and Down: shown the locale’s way, as a percentage or a unit, and kept within bounds. Each change says whether it was a step (and by how much) or typed, and onValueCommit fires once it’s finished.
165
+ - [Page Header](./page-header.md): Eyebrow, aurora title and lead paragraph for the top of a page.
166
+ - [Page Transition](./page-transition.md): The old page fades out as the new one rises in, when your router changes page in a view transition.
167
+ - [Pagination](./pagination.md): The pages a long list is split into, as buttons or links for your router.
168
+ - [Pills](./pills.md): A glass track with a glowing pill that slides to the current page. In a nav, it is site navigation; works with any router.
169
+ - [Player Bar](./player-bar.md): What's playing, along the bottom of the screen, sitting on a TabBar on phones. useMediaPlayer drives your own <audio> or <video>; useMediaSession puts it on the lock screen and the media keys.
170
+ - [Popover](./popover.md): A glass panel that opens from a button, on the native Popover API: small forms, cards and settings. For hints use Tooltip; for actions, Menu.
171
+ - [Progress Ring](./progress-ring.md): Circular progress with room for a label in the middle.
172
+ - [Radio Cards](./radio-cards.md): Choose one of a few cards that each show their option, like a colour palette.
173
+ - [Radio Group](./radio-group.md): Choose one of a list, each option with a label and a description; native radios, so arrow keys work.
174
+ - [Range Slider](./range-slider.md): Two thumbs on one track, for a span like an amount from $50 to $500.
175
+ - [Repeat Picker](./repeat-picker.md): How often something recurs, like a bill, said in words, with the next dates it falls on.
176
+ - [Resizable](./resizable.md): Panels with handles between them to drag, or to move with the arrow keys, within limits you set; groups nest, and the layout is reported to keep.
177
+ - [Seek Bar](./seek-bar.md): Where playback is in a track, and the way to move it: played and loaded on one track, between the time played and the length.
178
+ - [Segmented](./segmented.md): A pill row for a few mutually exclusive options.
179
+ - [Select](./select.md): Pick one value from a short list, fully keyboard-driven. For long or searchable lists, use Combobox.
180
+ - [Separator](./separator.md): A hairline between groups of content, across or upright.
181
+ - [Side Nav](./side-nav.md): Vertical navigation in titled groups, for docs and settings sidebars. Works with any router.
182
+ - [Skeleton & Spinner](./skeleton.md): Placeholders for content on its way.
183
+ - [Slider](./slider.md): A native range input with a glowing thumb and any track you like.
184
+ - [Sortable List](./sortable.md): Put a list in order, or a tree of groups (a table’s rows, say), by dragging items by a grip or with the keyboard, each step announced: into other groups, open sub-items, or under another item.
185
+ - [Sparkline](./sparkline.md): A small line of how a number has moved, with no axes, for beside a figure. It draws itself in.
186
+ - [Spinner](./spinner.md): A small loading spinner in the current text colour, announced to screen readers when labelled.
187
+ - [Status Pill](./status-pill.md): A small status that changes in place, such as Saving…, Saved or Not saved with a Retry, and fades back once the news is old.
188
+ - [Stepper](./stepper.md): The steps of a flow and where you are in it; the line between them fills in as you go.
189
+ - [Tab Bar](./tab-bar.md): Phone navigation fixed to the bottom of the screen. Works with any router.
190
+ - [Table](./table.md): Composable tables: a glass panel that scrolls, sticky headers, first column and totals, sortable columns, rows reordered by drag and drop (into groups and under other rows), and virtual rows for long lists.
191
+ - [Table of Contents](./table-of-contents.md): An "On this page" list that highlights the section being read.
192
+ - [Tabs](./tabs.md): Switch between panels in place, with an underline or a glowing pill that slides to the active tab. To go to another page, use Pills.
193
+ - [Tag Input](./tag-input.md): A field of short values (tags, emails): Enter or a comma adds one, Backspace takes the last back.
194
+ - [Textarea](./textarea.md): Multi-line text, styled like Input.
195
+ - [Theme Toggle](./theme-toggle.md): Switches between the dark and light themes, remembering the choice itself or leaving it to your app.
196
+ - [Theming & styling](./theming.md): Your own colours from a few settings, readable in dark and light for any hue; and className overrides that always win.
197
+ - [Time Field](./time-field.md): A time of day, typed or stepped a part at a time, in the locale’s order, digits and 12- or 24-hour clock.
198
+ - [Timeline](./timeline.md): Events in order, each on a dot joined by a line: an activity feed or a history.
199
+ - [Toast](./toast.md): Short messages that confirm an action or report a problem, with an optional action like Undo. They stack into a deck that fans out when pointed at, and swipe away.
200
+ - [Toggle](./toggle.md): An on/off switch.
201
+ - [Toggle Group](./toggle-group.md): Buttons that stay pressed, in a row: any number of them, like filters, or at most one. One stop in the tab order; the arrow keys go through them.
202
+ - [Toolbar](./toolbar.md): A row of controls for one thing, like a table’s: one stop for Tab, and the arrow keys through its buttons, toggles and fields.
203
+ - [Tooltip](./tooltip.md): A short glass hint on hover or keyboard focus, describing its trigger to screen readers.
204
+ - [Tree Table](./tree.md): Rows that open into child rows, animated, built from useTree and the Table parts. With virtual rows and editable cells, enough for a budget planner.
205
+ - [Trend](./trend.md): A change, like +12% with an arrow, coloured by whether it is good news.
@@ -0,0 +1,122 @@
1
+ # Accordion
2
+
3
+ Sections under headings that open one at a time, or several: questions and answers, or groups of settings.
4
+
5
+ ```tsx
6
+ import { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from '@rinzai/zen';
7
+ ```
8
+
9
+ ## Examples
10
+
11
+ ### Default
12
+
13
+ ```tsx
14
+ import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, Card, CardHeader, CardTitle } from '@rinzai/zen';
15
+
16
+ const QUESTIONS = [
17
+ {
18
+ q: 'How are other currencies converted?',
19
+ a: 'At the day’s rate when you save the entry. The original amount and rate are kept with it, so the conversion never changes later.',
20
+ },
21
+ {
22
+ q: 'Can I split one payment across categories?',
23
+ a: 'Yes: add it once, then split it. Each part counts towards its own category’s budget.',
24
+ },
25
+ {
26
+ q: 'What happens to a budget I go over?',
27
+ a: 'Nothing is blocked. The bar turns red, and the overspend is shown in the month’s summary.',
28
+ },
29
+ ];
30
+
31
+ /** Questions and answers: opening one closes the last. */
32
+ export default function Default() {
33
+ return (
34
+ <Card className="w-full max-w-md gap-2">
35
+ <CardHeader>
36
+ <CardTitle>Questions</CardTitle>
37
+ </CardHeader>
38
+ <Accordion defaultValue="0">
39
+ {QUESTIONS.map(({ q, a }, i) => (
40
+ <AccordionItem key={q} value={String(i)}>
41
+ <AccordionTrigger>{q}</AccordionTrigger>
42
+ <AccordionContent>{a}</AccordionContent>
43
+ </AccordionItem>
44
+ ))}
45
+ </Accordion>
46
+ </Card>
47
+ );
48
+ }
49
+ ```
50
+
51
+ ### Multiple
52
+
53
+ ```tsx
54
+ import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, Toggle } from '@rinzai/zen';
55
+
56
+ /** `multiple`: each section opens and closes on its own. */
57
+ export default function Multiple() {
58
+ return (
59
+ <Accordion multiple defaultValue={['alerts']} className="w-full max-w-sm">
60
+ <AccordionItem value="alerts">
61
+ <AccordionTrigger>Alerts</AccordionTrigger>
62
+ <AccordionContent className="flex flex-col gap-3">
63
+ <label className="flex items-center justify-between font-normal">
64
+ Over budget
65
+ <Toggle defaultChecked />
66
+ </label>
67
+ <label className="flex items-center justify-between font-normal">
68
+ Large payments
69
+ <Toggle />
70
+ </label>
71
+ </AccordionContent>
72
+ </AccordionItem>
73
+ <AccordionItem value="export">
74
+ <AccordionTrigger>Export</AccordionTrigger>
75
+ <AccordionContent>Download every entry as a CSV file, one row each.</AccordionContent>
76
+ </AccordionItem>
77
+ <AccordionItem value="danger">
78
+ <AccordionTrigger>Delete data</AccordionTrigger>
79
+ <AccordionContent>Removes every entry and budget. This can’t be undone.</AccordionContent>
80
+ </AccordionItem>
81
+ </Accordion>
82
+ );
83
+ }
84
+ ```
85
+
86
+ ## API reference
87
+
88
+ ### Accordion
89
+
90
+ Sections under headings that open one at a time (or several, with
91
+ `multiple`), e.g. questions and answers. Each AccordionItem is a Disclosure:
92
+ an AccordionTrigger and an AccordionContent that grows open and folds shut.
93
+ ↑↓ move between the headings, Home and End jump to the first and last.
94
+
95
+ | Prop | Type | Default | Description |
96
+ | --- | --- | --- | --- |
97
+ | `defaultValue` | `string \| readonly string[] \| null` | | |
98
+ | `multiple` | `boolean` | | One section open at a time (the default); opening one closes the other. Any number open at once. |
99
+ | `onValueChange` | `((value: string \| null) => void) \| ((value: readonly string[]) => void)` | | |
100
+ | `value` | `string \| readonly string[] \| null` | | The open section's value, or null for none, when you keep it. |
101
+
102
+ ### AccordionItem
103
+
104
+ One section: an AccordionTrigger and its AccordionContent.
105
+
106
+ | Prop | Type | Default | Description |
107
+ | --- | --- | --- | --- |
108
+ | `value` (required) | `string` | | Names the section in the Accordion's value. |
109
+
110
+ ### AccordionTrigger
111
+
112
+ The heading and its button: your label, and a chevron that turns over while open.
113
+
114
+ | Prop | Type | Default | Description |
115
+ | --- | --- | --- | --- |
116
+ | `level` | `2 \| 3 \| 4 \| 5 \| 6` | `3` | The heading level it sits in, to fit the page's outline. Default 3. |
117
+
118
+ ### AccordionContent
119
+
120
+ What the trigger shows and hides; only in the page while open (or folding shut).
121
+
122
+ No props of its own; it accepts the props of the element it renders.
@@ -0,0 +1,69 @@
1
+ # Alert
2
+
3
+ A notice, warning or failure beside what it is about: a tinted box, or a plain line in a form or card.
4
+
5
+ ```tsx
6
+ import { Alert } from '@rinzai/zen';
7
+ ```
8
+
9
+ ## Examples
10
+
11
+ ### Default
12
+
13
+ ```tsx
14
+ import { Alert } from '@rinzai/zen';
15
+
16
+ /** A tinted box per tone; a `title` leads, and the text under it is quieter. */
17
+ export default function Default() {
18
+ return (
19
+ <div className="flex w-full max-w-md flex-col gap-3">
20
+ <Alert>Changes to this item apply from next month.</Alert>
21
+ <Alert tone="positive">Your data is up to date on every device.</Alert>
22
+ <Alert tone="negative" title="Your data will be deleted for good.">
23
+ Without your password or recovery key, nobody can open it. Starting over gives you a new, empty vault.
24
+ </Alert>
25
+ </div>
26
+ );
27
+ }
28
+ ```
29
+
30
+ ### Plain
31
+
32
+ ```tsx
33
+ import { Alert, Card, CardHeader, CardTitle, Spinner } from '@rinzai/zen';
34
+
35
+ /** `plain`: just the icon and text, for a line inside a card or a form. Any icon, or none. */
36
+ export default function Plain() {
37
+ return (
38
+ <Card className="flex w-full max-w-sm flex-col gap-3">
39
+ <CardHeader className="mb-0">
40
+ <CardTitle>Currency</CardTitle>
41
+ </CardHeader>
42
+ <Alert plain icon={<Spinner className="text-primary" />}>
43
+ Getting today’s rate…
44
+ </Alert>
45
+ <Alert plain tone="negative">
46
+ Couldn’t get today’s rate, so converting isn’t possible right now.
47
+ </Alert>
48
+ </Card>
49
+ );
50
+ }
51
+ ```
52
+
53
+ ## API reference
54
+
55
+ ### Alert
56
+
57
+ A message beside the content it's about: a notice, a warning, a failure.
58
+ A tinted box by default, or with `plain`, just the icon and text in the
59
+ tone's colour. With a `title`, the title takes the tone and the text under
60
+ it is quieter. Bad news is announced at once (role="alert"), anything else
61
+ politely (role="status"); pass `role` to change that, e.g. "note" for a
62
+ message that doesn't change.
63
+
64
+ | Prop | Type | Default | Description |
65
+ | --- | --- | --- | --- |
66
+ | `icon` | `ReactNode` | | Replaces the tone's icon (info, check, warning); `null` for none. |
67
+ | `plain` | `boolean` | `false` | Just the icon and text, no box: for a line inside a form or a card. |
68
+ | `title` | `ReactNode` | | A first line in the tone's colour; the text under it is quieter. |
69
+ | `tone` | `"default" \| "negative" \| "positive"` | `default` | `positive` in the accent colour, `negative` in red for bad news. |