@journ/bloom 0.1.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 (278) hide show
  1. package/DESIGN.md +287 -0
  2. package/LICENSE +21 -0
  3. package/README.md +59 -0
  4. package/dist/accordion-parts.d.ts +9 -0
  5. package/dist/accordion-parts.js +37 -0
  6. package/dist/accordion.d.ts +10 -0
  7. package/dist/accordion.js +10 -0
  8. package/dist/alert-dialog-parts.d.ts +16 -0
  9. package/dist/alert-dialog-parts.js +33 -0
  10. package/dist/alert-dialog.d.ts +15 -0
  11. package/dist/alert-dialog.js +15 -0
  12. package/dist/alert.d.ts +48 -0
  13. package/dist/alert.js +51 -0
  14. package/dist/animated-size.d.ts +6 -0
  15. package/dist/animated-size.js +12 -0
  16. package/dist/avatar-parts.d.ts +10 -0
  17. package/dist/avatar-parts.js +20 -0
  18. package/dist/avatar.d.ts +9 -0
  19. package/dist/avatar.js +9 -0
  20. package/dist/badge.d.ts +38 -0
  21. package/dist/badge.js +22 -0
  22. package/dist/breadcrumbs.d.ts +18 -0
  23. package/dist/breadcrumbs.js +44 -0
  24. package/dist/bulk-bar.d.ts +13 -0
  25. package/dist/bulk-bar.js +27 -0
  26. package/dist/button.d.ts +40 -0
  27. package/dist/button.js +41 -0
  28. package/dist/calendar-parts.d.ts +17 -0
  29. package/dist/calendar-parts.js +90 -0
  30. package/dist/calendar.d.ts +17 -0
  31. package/dist/calendar.js +17 -0
  32. package/dist/card.d.ts +51 -0
  33. package/dist/card.js +63 -0
  34. package/dist/carousel-parts.d.ts +13 -0
  35. package/dist/carousel-parts.js +53 -0
  36. package/dist/carousel.d.ts +11 -0
  37. package/dist/carousel.js +11 -0
  38. package/dist/checkbox-parts.d.ts +8 -0
  39. package/dist/checkbox-parts.js +21 -0
  40. package/dist/checkbox.d.ts +8 -0
  41. package/dist/checkbox.js +8 -0
  42. package/dist/chip.d.ts +45 -0
  43. package/dist/chip.js +55 -0
  44. package/dist/collapsible-parts.d.ts +8 -0
  45. package/dist/collapsible-parts.js +22 -0
  46. package/dist/collapsible.d.ts +9 -0
  47. package/dist/collapsible.js +9 -0
  48. package/dist/combobox-parts.d.ts +22 -0
  49. package/dist/combobox-parts.js +54 -0
  50. package/dist/combobox.d.ts +26 -0
  51. package/dist/combobox.js +21 -0
  52. package/dist/command-palette-parts.d.ts +13 -0
  53. package/dist/command-palette-parts.js +19 -0
  54. package/dist/command-palette.d.ts +17 -0
  55. package/dist/command-palette.js +17 -0
  56. package/dist/composer.d.ts +12 -0
  57. package/dist/composer.js +21 -0
  58. package/dist/context-menu-parts.d.ts +15 -0
  59. package/dist/context-menu-parts.js +10 -0
  60. package/dist/context-menu.d.ts +20 -0
  61. package/dist/context-menu.js +13 -0
  62. package/dist/currency-input-parts.d.ts +8 -0
  63. package/dist/currency-input-parts.js +13 -0
  64. package/dist/currency-input.d.ts +8 -0
  65. package/dist/currency-input.js +8 -0
  66. package/dist/data-table-parts.d.ts +10 -0
  67. package/dist/data-table-parts.js +65 -0
  68. package/dist/data-table.d.ts +12 -0
  69. package/dist/data-table.js +12 -0
  70. package/dist/date-input-parts.d.ts +7 -0
  71. package/dist/date-input-parts.js +12 -0
  72. package/dist/date-input.d.ts +8 -0
  73. package/dist/date-input.js +8 -0
  74. package/dist/date-picker-parts.d.ts +11 -0
  75. package/dist/date-picker-parts.js +8 -0
  76. package/dist/date-picker.d.ts +20 -0
  77. package/dist/date-picker.js +15 -0
  78. package/dist/date-range-picker-parts.d.ts +11 -0
  79. package/dist/date-range-picker-parts.js +8 -0
  80. package/dist/date-range-picker.d.ts +20 -0
  81. package/dist/date-range-picker.js +15 -0
  82. package/dist/description-list.d.ts +12 -0
  83. package/dist/description-list.js +23 -0
  84. package/dist/dialog-parts.d.ts +15 -0
  85. package/dist/dialog-parts.js +32 -0
  86. package/dist/dialog.d.ts +15 -0
  87. package/dist/dialog.js +15 -0
  88. package/dist/divider.d.ts +6 -0
  89. package/dist/divider.js +12 -0
  90. package/dist/drawer-parts.d.ts +13 -0
  91. package/dist/drawer-parts.js +31 -0
  92. package/dist/drawer.d.ts +15 -0
  93. package/dist/drawer.js +15 -0
  94. package/dist/dropdown-menu-parts.d.ts +15 -0
  95. package/dist/dropdown-menu-parts.js +10 -0
  96. package/dist/dropdown-menu.d.ts +20 -0
  97. package/dist/dropdown-menu.js +13 -0
  98. package/dist/empty-state.d.ts +16 -0
  99. package/dist/empty-state.js +40 -0
  100. package/dist/field-parts.d.ts +10 -0
  101. package/dist/field-parts.js +25 -0
  102. package/dist/field.d.ts +11 -0
  103. package/dist/field.js +11 -0
  104. package/dist/file-dropzone-parts.d.ts +12 -0
  105. package/dist/file-dropzone-parts.js +32 -0
  106. package/dist/file-dropzone.d.ts +10 -0
  107. package/dist/file-dropzone.js +10 -0
  108. package/dist/heading.d.ts +31 -0
  109. package/dist/heading.js +26 -0
  110. package/dist/hover-card-parts.d.ts +8 -0
  111. package/dist/hover-card-parts.js +20 -0
  112. package/dist/hover-card.d.ts +9 -0
  113. package/dist/hover-card.js +9 -0
  114. package/dist/icon-button.d.ts +44 -0
  115. package/dist/icon-button.js +39 -0
  116. package/dist/icon.d.ts +30 -0
  117. package/dist/icon.js +27 -0
  118. package/dist/index.d.ts +96 -0
  119. package/dist/index.js +89 -0
  120. package/dist/input.d.ts +7 -0
  121. package/dist/input.js +14 -0
  122. package/dist/lib/cn.d.ts +6 -0
  123. package/dist/lib/cn.js +7 -0
  124. package/dist/lib/icon-tile.d.ts +32 -0
  125. package/dist/lib/icon-tile.js +23 -0
  126. package/dist/lib/interactive.d.ts +11 -0
  127. package/dist/lib/interactive.js +19 -0
  128. package/dist/lib/menu-parts.d.ts +25 -0
  129. package/dist/lib/menu-parts.js +41 -0
  130. package/dist/lib/modal-content.d.ts +10 -0
  131. package/dist/lib/modal-content.js +17 -0
  132. package/dist/lib/modal-parts.d.ts +7 -0
  133. package/dist/lib/modal-parts.js +15 -0
  134. package/dist/lib/overlay.d.ts +41 -0
  135. package/dist/lib/overlay.js +22 -0
  136. package/dist/lib/picker.d.ts +18 -0
  137. package/dist/lib/picker.js +25 -0
  138. package/dist/lib/text-styles.d.ts +25 -0
  139. package/dist/lib/text-styles.js +25 -0
  140. package/dist/lib/theme.d.ts +43 -0
  141. package/dist/lib/theme.js +45 -0
  142. package/dist/lib/tones.d.ts +52 -0
  143. package/dist/lib/tones.js +55 -0
  144. package/dist/lib/tv.d.ts +2 -0
  145. package/dist/lib/tv.js +2 -0
  146. package/dist/lib/use-enter.d.ts +15 -0
  147. package/dist/lib/use-enter.js +54 -0
  148. package/dist/link.d.ts +8 -0
  149. package/dist/link.js +13 -0
  150. package/dist/markdown.d.ts +10 -0
  151. package/dist/markdown.js +70 -0
  152. package/dist/message.d.ts +15 -0
  153. package/dist/message.js +25 -0
  154. package/dist/month-picker-parts.d.ts +2 -0
  155. package/dist/month-picker-parts.js +3 -0
  156. package/dist/month-picker.d.ts +15 -0
  157. package/dist/month-picker.js +15 -0
  158. package/dist/month-stepper-parts.d.ts +11 -0
  159. package/dist/month-stepper-parts.js +37 -0
  160. package/dist/month-stepper.d.ts +10 -0
  161. package/dist/month-stepper.js +10 -0
  162. package/dist/multiple-selector-parts.d.ts +18 -0
  163. package/dist/multiple-selector-parts.js +28 -0
  164. package/dist/multiple-selector.d.ts +28 -0
  165. package/dist/multiple-selector.js +23 -0
  166. package/dist/nav-bar.d.ts +10 -0
  167. package/dist/nav-bar.js +23 -0
  168. package/dist/navigation-menu-parts.d.ts +12 -0
  169. package/dist/navigation-menu-parts.js +47 -0
  170. package/dist/navigation-menu.d.ts +12 -0
  171. package/dist/navigation-menu.js +12 -0
  172. package/dist/otp-input-parts.d.ts +8 -0
  173. package/dist/otp-input-parts.js +17 -0
  174. package/dist/otp-input.d.ts +9 -0
  175. package/dist/otp-input.js +9 -0
  176. package/dist/page-header.d.ts +12 -0
  177. package/dist/page-header.js +23 -0
  178. package/dist/page.d.ts +45 -0
  179. package/dist/page.js +41 -0
  180. package/dist/pagination.d.ts +18 -0
  181. package/dist/pagination.js +49 -0
  182. package/dist/pill-select.d.ts +5 -0
  183. package/dist/pill-select.js +21 -0
  184. package/dist/popover-parts.d.ts +9 -0
  185. package/dist/popover-parts.js +21 -0
  186. package/dist/popover.d.ts +10 -0
  187. package/dist/popover.js +10 -0
  188. package/dist/portal.d.ts +2 -0
  189. package/dist/portal.js +2 -0
  190. package/dist/progress-parts.d.ts +38 -0
  191. package/dist/progress-parts.js +25 -0
  192. package/dist/progress.d.ts +8 -0
  193. package/dist/progress.js +8 -0
  194. package/dist/quantity-input-parts.d.ts +11 -0
  195. package/dist/quantity-input-parts.js +36 -0
  196. package/dist/quantity-input.d.ts +10 -0
  197. package/dist/quantity-input.js +10 -0
  198. package/dist/radio-parts.d.ts +7 -0
  199. package/dist/radio-parts.js +19 -0
  200. package/dist/radio.d.ts +8 -0
  201. package/dist/radio.js +8 -0
  202. package/dist/scroll-area-parts.d.ts +9 -0
  203. package/dist/scroll-area-parts.js +23 -0
  204. package/dist/scroll-area.d.ts +10 -0
  205. package/dist/scroll-area.js +10 -0
  206. package/dist/select-parts.d.ts +13 -0
  207. package/dist/select-parts.js +46 -0
  208. package/dist/select.d.ts +14 -0
  209. package/dist/select.js +14 -0
  210. package/dist/sheet-parts.d.ts +33 -0
  211. package/dist/sheet-parts.js +41 -0
  212. package/dist/sheet.d.ts +16 -0
  213. package/dist/sheet.js +16 -0
  214. package/dist/sidebar.d.ts +24 -0
  215. package/dist/sidebar.js +58 -0
  216. package/dist/skeleton.d.ts +5 -0
  217. package/dist/skeleton.js +11 -0
  218. package/dist/slider-parts.d.ts +9 -0
  219. package/dist/slider-parts.js +26 -0
  220. package/dist/slider.d.ts +10 -0
  221. package/dist/slider.js +10 -0
  222. package/dist/spinner.d.ts +5 -0
  223. package/dist/spinner.js +10 -0
  224. package/dist/stack-parts.d.ts +39 -0
  225. package/dist/stack-parts.js +35 -0
  226. package/dist/stack.d.ts +10 -0
  227. package/dist/stack.js +10 -0
  228. package/dist/stat.d.ts +14 -0
  229. package/dist/stat.js +29 -0
  230. package/dist/step-button.d.ts +10 -0
  231. package/dist/step-button.js +14 -0
  232. package/dist/stepper-parts.d.ts +10 -0
  233. package/dist/stepper-parts.js +33 -0
  234. package/dist/stepper.d.ts +11 -0
  235. package/dist/stepper.js +11 -0
  236. package/dist/styles.css +2 -0
  237. package/dist/switch-parts.d.ts +7 -0
  238. package/dist/switch-parts.js +19 -0
  239. package/dist/switch.d.ts +8 -0
  240. package/dist/switch.js +8 -0
  241. package/dist/table.d.ts +20 -0
  242. package/dist/table.js +48 -0
  243. package/dist/tabs-parts.d.ts +9 -0
  244. package/dist/tabs-parts.js +25 -0
  245. package/dist/tabs.d.ts +10 -0
  246. package/dist/tabs.js +10 -0
  247. package/dist/tag-input-parts.d.ts +15 -0
  248. package/dist/tag-input-parts.js +24 -0
  249. package/dist/tag-input.d.ts +10 -0
  250. package/dist/tag-input.js +10 -0
  251. package/dist/text-morph.d.ts +6 -0
  252. package/dist/text-morph.js +14 -0
  253. package/dist/text-shimmer.d.ts +9 -0
  254. package/dist/text-shimmer.js +12 -0
  255. package/dist/text.d.ts +38 -0
  256. package/dist/text.js +32 -0
  257. package/dist/textarea.d.ts +5 -0
  258. package/dist/textarea.js +12 -0
  259. package/dist/time-range-parts.d.ts +10 -0
  260. package/dist/time-range-parts.js +22 -0
  261. package/dist/time-range.d.ts +10 -0
  262. package/dist/time-range.js +10 -0
  263. package/dist/toast-parts.d.ts +11 -0
  264. package/dist/toast-parts.js +39 -0
  265. package/dist/toast.d.ts +13 -0
  266. package/dist/toast.js +12 -0
  267. package/dist/toggle-group-parts.d.ts +7 -0
  268. package/dist/toggle-group-parts.js +17 -0
  269. package/dist/toggle-group.d.ts +8 -0
  270. package/dist/toggle-group.js +8 -0
  271. package/dist/tokens.css +64 -0
  272. package/dist/tooltip-parts.d.ts +9 -0
  273. package/dist/tooltip-parts.js +21 -0
  274. package/dist/tooltip.d.ts +10 -0
  275. package/dist/tooltip.js +10 -0
  276. package/dist/touch-target.d.ts +7 -0
  277. package/dist/touch-target.js +10 -0
  278. package/package.json +71 -0
package/DESIGN.md ADDED
@@ -0,0 +1,287 @@
1
+ # Bloom design system
2
+
3
+ The design system that `@journ/bloom` implements: token-driven, React 19 and Tailwind v4. It has the same components and API as `@journ/lotus`, with tokens named `bloom-`, and a look inspired by Midday's UI. Behavior, state and accessibility come from `@journ/nude`; everything below is about how things look and move.
4
+
5
+ The rules marked as enforced are checked by `tools/check-design.ts`, which runs with `pnpm lint`. See [Enforced rules](#enforced-rules).
6
+
7
+ ## Design style
8
+
9
+ Dark-first, monochrome, flat. A near-black canvas, hairline borders in place of fills, square corners, one sans face, and monospace for numbers and status labels. Color appears only as status.
10
+
11
+ ### Color
12
+
13
+ Never use raw colors or Tailwind palette colors in components (enforced). Tokens live in `src/styles/tokens.css`.
14
+
15
+ **Palette tokens** are the same in both themes. They are status colors, used as washes far more than as fills:
16
+
17
+ | Token | Role |
18
+ | -------------- | ----------------------------------------------- |
19
+ | `bloom-ink` | The `ink` tone: always dark |
20
+ | `bloom-paper` | Text on `ink` and on the coral and indigo fills |
21
+ | `bloom-coral` | Destructive actions, errors, invalid fields |
22
+ | `bloom-yellow` | Warnings, overdue |
23
+ | `bloom-indigo` | A second informational color |
24
+ | `bloom-mint` | Success, paid, complete |
25
+ | `bloom-sky` | The focus ring, informational labels |
26
+
27
+ **Role tokens** come in a light/dark pair, `bloom-<role>` and `bloom-<role>-dark`:
28
+
29
+ | Role | Used for |
30
+ | ------------ | --------------------------------------------------------- |
31
+ | `canvas` | Page background, the modal backdrop, the switch thumb |
32
+ | `surface` | Cards, sheets, drawers, toasts, menus |
33
+ | `foreground` | Text and icons on the canvas and on surfaces |
34
+ | `line` | Every hairline: card edges, field outlines |
35
+ | `accent` | Accent text on a surface (amber in light, yellow in dark) |
36
+
37
+ Rules:
38
+
39
+ - Blocks are separated by a 1px border, not by a fill: `surface` has the same value as `canvas` in both themes. `surface` and `raised` in `src/lib/theme.ts` carry the border.
40
+ - Fields, selects and picker triggers have no fill: `control` is a transparent box with a hairline outline.
41
+ - Selected, hovered and highlighted states are a wash of the text color (`bg-current/8`, the `dim` pair), never a color. Tabs and toggles sit in a dim track and the selected one takes the canvas color; the current nav item gets a hairline edge.
42
+ - A colored tone on a card, alert, dialog or page is a 10% wash with the edge in the color (`surfaceTones`, `pageTints` in `src/lib/tones.ts`); the text stays `foreground`. Status labels (`Badge`, `Chip`) use `labelTones`: a wash, with the color as text in dark.
43
+ - Solid color is for buttons only (`solidTones`). The primary button is `neutral`, which inverts with the theme.
44
+ - Secondary text is the same color at reduced opacity (`opacity-60`), never a separate gray.
45
+
46
+ ### Themes
47
+
48
+ Components follow Tailwind's `dark:` variant. The package defines it in `tokens.css` so it works both ways:
49
+
50
+ - With no class it follows the OS (`prefers-color-scheme`).
51
+ - `.dark` on `<html>` (or any ancestor) forces dark, `.light` forces light. Themes can nest.
52
+
53
+ The package has no switcher. The playground's `ThemeToggle` (`apps/playground/src/bloom/demos/theme-toggle.tsx`) is the reference: a system/light/dark `ToggleGroup` that sets the class on `<html>` and remembers the choice. Portals render on `<body>`, so the class must be on `<html>`.
54
+
55
+ - Every light/dark pairing is written once in `src/lib/theme.ts` (`canvas`, `surface`, `raised`, `control`, `neutral`, `dim`, `line`, `scrim`, `foreground`, `accentText`). Components import these; do not write `dark:` pairs inline (enforced).
56
+ - To restyle a theme, override the role tokens in your app's `@theme`, for example `--color-bloom-surface-dark`. Light and dark are independent.
57
+ - To follow only a class (never the OS), redefine the variant in your app: `@custom-variant dark (&:where(.dark, .dark *));`.
58
+
59
+ ### Shape
60
+
61
+ - Corners are square. `--radius-bloom` is `0`; buttons, fields, cards, sheets and toasts use `rounded-bloom`, so one token rounds them all.
62
+ - Round is reserved for what is round in Midday: `Badge`, avatars, radios, the switch, the slider, calendar days, carousel arrows and message bubbles. `Chip` is a square tag. The drawer has a rounded top (`rounded-t-xl`).
63
+ - Sheets float from `md` up: inset from the viewport edge, bordered on all sides.
64
+
65
+ ### Typography
66
+
67
+ - One face, `font-bloom-display` (Hedvig Letters Sans, weight 400 and 500). `canvas` and `surfaceFill` set it, so pages and portaled overlays both get it. Titles are sentence case, `font-medium`, never uppercase.
68
+ - Numbers that carry meaning (stat values, OTP) and status labels (badges, chips) are `font-mono`.
69
+ - Body is small (`text-sm/5`, `text-sm/6`).
70
+ - Inputs are 16px on mobile and 14px from `sm` up, to prevent iOS focus zoom.
71
+
72
+ ### Layout
73
+
74
+ - Every screen is a `Page.Root` with its own `tone`. Each page can pick a different background, as in the references. `canvas` is the default and follows the theme; the colored tones wash the canvas and keep its text.
75
+ - Controls that must adapt to the page (`IconButton` and `Button` with `ghost` or `outline`, `Composer`) inherit the page text color and use `currentColor` for their tint, so they stay legible on any page tone. Cards keep their own tone and do not depend on the page.
76
+ - `Page.Content` is the column (safe-area padding included). `Page.Footer` pins its content, usually the `NavBar`, to the bottom.
77
+ - Single column, content width capped at `max-w-sm`, `px-4` gutters.
78
+ - Page structure: `Page.Root` > `Page.Content` > `PageHeader` (back button, title, actions), optional filter row of `PillSelect`, a stack of cards, `NavBar` inside `Page.Footer`.
79
+ - Spacing between blocks is `gap-4`. Inside cards, `gap-3`.
80
+ - Icons come from `lucide-react`, sized by the parent (`size-5`, `size-4` in small buttons).
81
+
82
+ ### Interaction
83
+
84
+ Every pressable thing shares `src/lib/interactive.ts`:
85
+
86
+ - Keyboard-only focus ring (`focus-visible`), never on tap.
87
+ - Hover and press overlay using `currentColor` (`/10` hover, `/15` press), layered between background and content.
88
+ - Disabled and `aria-disabled` dim to 50% and ignore pointer events.
89
+ - `TouchTarget` expands the hit area to at least 44x44px on touch devices only.
90
+
91
+ Use `Button`, `IconButton` or `NavBar.Item` for anything pressable. Do not hand-roll pressable elements.
92
+
93
+ ### Shared building blocks
94
+
95
+ Before writing a class string or an animation, look in `src/lib` first:
96
+
97
+ - `theme.ts`: every light/dark pairing written with `dark:`.
98
+ - `tones.ts`: `fills`, `solidTones`, `labelTones`, `inkTone`, `surfaceTones`, `pageTints`. Every tone map is built from these.
99
+ - `text-styles.ts`: `displayTitle`, `displayHeading`, `displayLabel`, `mutedText`, `controlSize`, `fieldText`, `fieldRing` and the `selectedWhen*` classes.
100
+ - `overlay.ts`: floating surface/item/card styles, the modal overlay and the centered panel (Dialog, AlertDialog).
101
+ - `modal-content.tsx`: `ModalContent` gives a nude modal content (Dialog, AlertDialog, Sheet) its fading overlay and animated panel.
102
+ - `menu-parts.tsx`: `styleMenuParts(parts)` styles the nude `Content`, `Item`, `Label` and `Separator` of a menu. Used by DropdownMenu and ContextMenu.
103
+ - `picker.tsx`: `stylePicker(parts)` styles a nude picker's `Trigger` and `Value`. Used by Combobox, DatePicker, DateRangePicker and MultipleSelector.
104
+ - `step-button.tsx`: `stepButton(Icon, style)` gives the ghost icon button look to a nude previous, next, minus or plus part (Calendar, Carousel, MonthStepper, QuantityInput).
105
+ - `button.tsx`, `input.tsx`, `composer.tsx`, `combobox-parts.tsx`: besides the component, each exports its class builder (`button`, `input`, `composerRoot`, `composerInput`, `option`) for parts that bind a nude element to their own state.
106
+ - `icon-tile.ts`: the bordered icon square used by Card.Icon, EmptyState.Icon and FileDropzone.Icon.
107
+ - `interactive.ts`: `interactive` and `pressReset` for anything pressable.
108
+ - `use-enter.ts`: `useEnter`, `usePopEnter`, `useDropEnter` and `useExpand`.
109
+ - `touch-target.tsx`: the 44px touch area, never re-implement it with classes.
110
+
111
+ State and parsing helpers are not here: `useControllableState`, `useDraft`, `createRequiredContext` and `prefersReducedMotion` live in `@journ/lift`; `clamp`, the number, date, month and time helpers in `@journ/fitter-happier`.
112
+
113
+ ### Scrollbars
114
+
115
+ - Native scroll containers inside bloom use the `scrollbar-bloom` utility (enforced) (defined in `tokens.css`): thin, transparent track, thumb in the current text color at 30%.
116
+ - For a fully custom, overlay scrollbar use `ScrollArea` (Radix). It shows on hover by default and has a 44px touch hit area.
117
+
118
+ ### Motion and touch
119
+
120
+ Rules from the design style guide, applied across the package:
121
+
122
+ - Interactions stay at or under 200ms. Frequent surfaces (menus, popovers, tooltips) are quiet: small scale, short fade.
123
+ - Dialogs scale in from 0.97 in 100ms. Nothing moves on press. Never animate from 0.
124
+ - Animate `transform` and `opacity` only. Skip animation when `prefers-reduced-motion` is set.
125
+ - Pressable controls share `interactive`/`pressReset` from `src/lib/interactive.ts`: no text selection, no iOS tap flash, keyboard-only focus ring.
126
+ - Numbers that change or line up (stats, table cells, badges, pagination, calendar days) use `tabular-nums`.
127
+ - Inputs are 16px on mobile. Hover styles come from Tailwind, which already gates them behind `(hover: hover)`.
128
+ - Focus rings use `outline` with an offset. Modern browsers round it to the element radius, so we do not use `box-shadow` rings (enforced).
129
+
130
+ ## Composition rules
131
+
132
+ Follow the composable pattern (`folio kb get react-composition-patterns`).
133
+
134
+ - Compound components: `Card.Root`, `Card.Header`, `Card.Title`. Consumers pick the parts they need.
135
+ - Pass `children`, not `renderX` props. No boolean props like `showFooter`.
136
+ - Variants are string unions (`tone`, `from`), never combinations of booleans.
137
+ - Each part sets a `data-slot` attribute and merges `className` through `cn`, so consumers can override.
138
+ - React 19 style: `ref` is a regular prop, no `forwardRef`.
139
+ - Share state through a provider only when parts truly share state. Stateless parts need no context.
140
+ - Shared context is `{ state, actions, meta }`. `X.Provider` takes all three, so the state can come from anywhere (local, global store, server). `X.Root` is the default provider that owns local state with `value`, `defaultValue` and `onValueChange`. Neither renders DOM; `X.Frame` does.
141
+ - Export a `useX` hook next to the namespace, so custom UI inside the provider can read state and call actions without being nested in the frame.
142
+
143
+ ## Code conventions
144
+
145
+ - Every component wraps the `@journ/nude` part of the same name and merges `className` through `cn` or a `tv` recipe. Never import Radix, vaul, cmdk, sonner, Embla, TanStack Table or react-markdown here; they belong to nude. (enforced)
146
+ - Inner elements that nude renders are styled through its `classNames` prop. Composites take bloom components through nude's `components` prop.
147
+ - Icons (`lucide-react`), the touch target and the spinner are passed as children.
148
+ - Components are arrow functions.
149
+ - Export only at the end of the file, in one `export { ... }` block, so unused code is easy to spot.
150
+ - A compound object (`const Card = { Root, ... }`) is the public API of a component file.
151
+ - Client-only parts live in `*-parts.tsx` with `'use client'`. The sibling file without the directive builds the `Drawer.X` or `Toast.X` object, because a namespace object exported from a client file cannot be read from a server component.
152
+ - Tests only for components with real logic or state (switchers, accordions, slides, `Stack`). Stateless presentational components are not tested. Run with `pnpm nx test @journ/bloom`; `animejs` is mocked.
153
+
154
+ ## Enforced rules
155
+
156
+ `tools/check-design.ts` reads every class string in `src` (tests excluded) and fails `pnpm lint` on:
157
+
158
+ | Rule | What fails |
159
+ | --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
160
+ | `color` | A Tailwind palette color (`bg-red-500`, `text-white`) or a raw value (`bg-[#fff]`, `text-[oklch(...)]`). Use `bloom-*` tokens, `current`, `transparent` or `inherit` |
161
+ | `dark` | A `dark:` variant outside `src/lib`. Light and dark pairings are written once, in `src/lib/theme.ts` |
162
+ | `font` | A font family other than `font-bloom-display` and `font-mono` |
163
+ | `focus` | A `ring` utility under a focus variant. Focus uses `outline` with an offset |
164
+ | `scroll` | `overflow-auto`, `overflow-x-auto`, `overflow-y-auto` or the `scroll` forms without `scrollbar-bloom` in the same class string |
165
+ | `spacing` | An arbitrary gap, padding or margin (`gap-[13px]`) or `space-x`/`space-y`. Use the scale and `gap`. Safe-area `env()` values are allowed |
166
+ | `radius` | An arbitrary radius. Use `rounded-bloom`, `rounded-full` or the scale; `rounded-[inherit]` is allowed |
167
+ | `import` | A direct import of a library that belongs to nude |
168
+
169
+ The check cannot judge intent, so these stay review rules: `gap-4` between blocks and `gap-3` inside cards, `bloom-ink` text on colored fills, `tabular-nums` on numbers, the 200ms motion budget, and using `Button`, `IconButton` or `NavBar.Item` for anything pressable.
170
+
171
+ ## Theming
172
+
173
+ Import the tokens, then override any of them in your own `@theme` block after it:
174
+
175
+ ```css
176
+ @import 'tailwindcss';
177
+ @import '@journ/bloom/tokens.css';
178
+ @source '../node_modules/@journ/bloom/dist';
179
+
180
+ @theme {
181
+ --color-bloom-coral: oklch(65% 0.2 20);
182
+ --radius-bloom: 1rem;
183
+ }
184
+ ```
185
+
186
+ Alternatively `import '@journ/bloom/styles.css'` loads the precompiled stylesheet, tokens included, and the app needs no Tailwind. The tokens cannot be overridden through `@theme` that way; override the CSS variables (`--color-bloom-coral`) instead.
187
+
188
+ The typeface is `--font-bloom-display`. Load Hedvig Letters Sans (or your own face) and set that variable. In Next.js, `next/font` with `variable: '--font-bloom-display'` does it.
189
+
190
+ ## Components
191
+
192
+ Import from `@journ/bloom` or per file (`@journ/bloom/card`). The library named in parentheses is what the nude part is built on.
193
+
194
+ | Component | Parts |
195
+ | ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
196
+ | `Accordion` (Radix, animejs) | `Root` (`type`), `Item`, `Trigger`, `Content` |
197
+ | `AlertDialog` (Radix) | `Root`, `Trigger`, `Content` (`tone`), `Title`, `Description`, `Actions`, `Cancel`, `Action` |
198
+ | `Alert` | `Root` (`tone`), `Icon`, `Body`, `Title`, `Description`, `Actions` |
199
+ | `AnimatedSize` (animejs) | single element, `width`, `height`. Glides to its content's size |
200
+ | `Avatar` (Radix) | `Root`, `Image`, `Fallback` |
201
+ | `Badge` | single element, `tone` |
202
+ | `Breadcrumbs` | `Root`, `List`, `Item`, `Link`, `Page`, `Separator` |
203
+ | `BulkBar` | `Root`, `Count`, `Actions` |
204
+ | `Button` | single element, `tone`, `pending`, renders `<a>` with `href` |
205
+ | `Calendar` | `Provider`, `Root`, `Frame`, `Header`, `Heading`, `Previous`, `Next`, `Weekdays`, `Days`, `Months`, `Years`, `useCalendar` |
206
+ | `Card` | `Root` (`tone`), `Icon`, `Header`, `Title`, `Description`, `Content`, `Footer` |
207
+ | `Checkbox` (Radix) | `Root` (supports `checked="indeterminate"`), `Indicator` |
208
+ | `Carousel` (Embla) | `Root` (`opts`, `orientation`), `Content`, `Item`, `Previous`, `Next` |
209
+ | `Chip` | `Root` (`tone`), `Dot`, `Remove`, `Tag` |
210
+ | `Collapsible` (Radix, animejs) | `Root`, `Trigger`, `Content` |
211
+ | `ContextMenu` (Radix) | `Root`, `Trigger`, `Content`, `Group`, `Label`, `Item`, `Separator` |
212
+ | `Combobox` (cmdk) | `Provider`, `Root`, `Trigger`, `Value`, `Content`, `Title`, `Description`, `Frame`, `Input`, `List`, `Empty`, `Group`, `Item`, `ItemIndicator`, `useCombobox` |
213
+ | `CommandPalette` (cmdk, Radix) | `Root` (`onSelect`, `shortcut`), `Trigger`, `Content`, plus the Combobox list parts `Frame`, `Input`, `List`, `Empty`, `Group`, `Item`, `ItemIndicator` |
214
+ | `Composer` | `Root`, `Input` |
215
+ | `DatePicker` | `Provider`, `Root`, `Trigger`, `Value`, `Content`, `Title`, `Description`, `Calendar` (wraps `Calendar.Root`), `useDatePicker` |
216
+ | `DescriptionList` | `Root`, `Term`, `Details` |
217
+ | `DateRangePicker` | `Root` (`value`, `locale`), `Trigger`, `Value`, `Content`, `Title`, `Description`, `Calendar`. Two clicks make a range |
218
+ | `CurrencyInput` | `Root` (`value`, `currency`, `locale`, `min`, `max`), `Field` |
219
+ | `DataTable` (TanStack Table v9) | `Root` (`data`, `columns`, `pageSize`, `onSelectionChange`), `Toolbar`, `ColumnPicker`, `Table`, `Pager`, `SelectionBar`. Helpers: `dataTableColumns`, `selectColumn`, `useDataTable` |
220
+ | `DateInput` | `Root` (`value`, `locale`), `Field`. Parses typed dates in the locale's order |
221
+ | `Dialog` (Radix) | `Root`, `Trigger`, `Close`, `Content` (`tone`), `Title`, `Description`, `Body`, `Actions` |
222
+ | `FileDropzone` | `Root` (`accept`, `maxSize`, `multiple`, `onFilesAccepted`, `onFilesRejected`), `Icon`, `Title`, `Description` |
223
+ | `Divider` (Radix) | single element, `orientation` |
224
+ | `Drawer` (vaul) | `Root`, `Trigger`, `Close`, `Content`, `Title`, `Description`, `Body`, `Actions` |
225
+ | `DropdownMenu` (Radix) | `Root`, `Trigger`, `Content`, `Group`, `Label`, `Item`, `Separator` |
226
+ | `EmptyState` | `Root`, `Icon`, `Title`, `Description`, `Actions` |
227
+ | `Field` (Radix) | `Root` (`invalid`), `Label`, `Control`, `Description`, `Error` |
228
+ | `Heading` | single element, `level` (outline) and `size` (look) |
229
+ | `Icon` | sizes any svg child, `size` |
230
+ | `HoverCard` (Radix) | `Root`, `Trigger`, `Content` |
231
+ | `IconButton` | single element, `tone`, `size` |
232
+ | `Input` | single element, native `<input>` |
233
+ | `Link` | single element, `asChild` styles a router link |
234
+ | `Markdown` (react-markdown) | single element, `components` overrides |
235
+ | `MonthStepper` | `Root` (`value`, `locale`), `Previous`, `Label`, `Next` |
236
+ | `Message` | `Root` (`from`), `Bubble`, `Highlight` |
237
+ | `NavBar` | `Root`, `Item` (`aria-current="page"` marks active) |
238
+ | `OtpInput` (Radix) | `Root`, `Slot` (one per character), `Hidden` |
239
+ | `MultipleSelector` (cmdk) | `Root` (`value`, `fixed`, `max`), `Trigger`, `Value`, `Tags`, `Content`, then Combobox's `Frame`, `Input`, `List`, `Empty`, `Group`, `Item`, `ItemIndicator`, plus `Create` |
240
+ | `NavigationMenu` (Radix) | `Root` (includes the viewport), `List`, `Item`, `Trigger`, `Content`, `Link` |
241
+ | `Page` | `Root` (`tone`), `Content`, `Footer` |
242
+ | `MonthPicker` | `Root` (`value`, `locale`), then the Calendar parts `Frame`, `Header`, `Heading`, `Previous`, `Next`, `Months`, `Years` |
243
+ | `PageHeader` | `Root`, `Title`, `Actions` |
244
+ | `Pagination` | `Root`, `Previous`, `List`, `Page`, `Gap`, `Next` |
245
+ | `PillSelect` | single element, native `<select>` |
246
+ | `Popover` (Radix) | `Root`, `Trigger`, `Content`, `Close` |
247
+ | `Portal` | single element, `container`. Renders children outside the tree (copied from midday). Radix overlays already portal themselves |
248
+ | `Progress` (Radix) | `Root` (`value`, `max`), `Indicator` (`tone`) |
249
+ | `Radio` (Radix) | `Group`, `Item` |
250
+ | `ScrollArea` (Radix) | `Root` (`type`), `Viewport`, `Scrollbar` (`orientation`), `Corner` |
251
+ | `QuantityInput` | `Root` (`value`, `min`, `max`, `step`), `Decrement`, `Field`, `Increment` |
252
+ | `Select` (Radix) | `Root`, `Trigger`, `Value`, `Content`, `Group`, `Label`, `Item`, `Separator` |
253
+ | `Sidebar` | `Layout`, `Root`, `Header`, `Body`, `Section`, `Heading`, `Item`, `Footer`, `Content` (desktop only) |
254
+ | `Skeleton` | single element |
255
+ | `Spinner` | single element |
256
+ | `Stepper` | `Root` (`value`), `Item` (`step`), `Indicator`, `Label`, `Description` |
257
+ | `Sheet` (Radix) | `Root`, `Trigger`, `Close`, `Content` (`side`: right, left, top, bottom), `Title`, `Description`, `Body`, `Actions` |
258
+ | `Slider` (Radix) | `Root`, `Track`, `Range`, `Thumb` (one per value) |
259
+ | `Stack` (animejs) | `Root` (`defaultValue`), `Item` (`value`, `tone`), `Trigger`, `Content` |
260
+ | `Stat` | `Root`, `Label`, `Value`, `Hint` |
261
+ | `Switch` (Radix) | `Root`, `Thumb` |
262
+ | `Table` | `Root`, `Caption`, `Head`, `Body`, `Row`, `Header`, `Cell` |
263
+ | `TagInput` | `Root` (`validate`), `Tags`, `Tag`, `Field`. Enter or comma adds, Backspace removes, paste splits |
264
+ | `Tabs` (Radix) | `Root`, `List`, `Trigger`, `Content` |
265
+ | `Text` | single element, `size`, `tone` |
266
+ | `Textarea` | single element, native `<textarea>` |
267
+ | `ToggleGroup` (Radix) | `Root` (`type`), `Item` |
268
+ | `TextMorph` (animejs) | single element, `as`. Only changed characters animate |
269
+ | `TextShimmer` | single element, `as`. Loading text, CSS only |
270
+ | `TimeRange` | `Root` (`value` as `{ start, stop }`), `Start`, `Stop`, `Duration` |
271
+ | `Toast` (sonner) | `Provider`, `show`, `Root`, `Title`, `Description`, `Action` |
272
+ | `Tooltip` (Radix) | `Root` (includes its provider), `Trigger`, `Content`, `Provider` |
273
+
274
+ Card tones: `surface` (default, follows the theme), `coral`, `yellow`, `indigo`, `mint`, `sky`.
275
+
276
+ Page tones: `canvas` (default, follows the theme), `ink` (always dark), `indigo`, `mint`, `sky`, `yellow`, `coral`.
277
+
278
+ Button, IconButton and Badge tones: `neutral` (default, inverts with the theme), `coral`, `yellow`, `ink`, and for Button also `field`, `ghost` and `outline`. IconButton has `ghost` (bare, a wash on hover) and `outline` (hairline edge); `outline` is the one addition to the lotus API. `field` is the outlined form-control look used by the select and picker triggers. Use it for any trigger that reads as a field, such as a "Search" bar that opens the command palette.
279
+
280
+ ## Adding a component
281
+
282
+ 1. Build the behavior in `@journ/nude` first: `packages/nude/src/<name>.tsx` as a compound object of unstyled parts with `data-slot`.
283
+ 2. Create `packages/bloom/src/<name>.tsx`: wrap each nude part and add classes through `cn` or `tv`.
284
+ 3. Use only `bloom-*` tokens. Add a token before adding a raw value.
285
+ 4. Reuse `interactive` and `TouchTarget` for anything pressable.
286
+ 5. Export at the end of the file, then add it to `src/index.ts`. Any `src/*.tsx` is also exposed as `@journ/bloom/<name>`.
287
+ 6. Add a usage to a story in `apps/playground/src` and check it on a phone-width viewport.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Journ
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,59 @@
1
+ # @journ/bloom
2
+
3
+ Styled UI library A. Wraps nude primitives with a Tailwind style layer and ships precompiled, prefixed CSS. Sibling of lotus, web only.
4
+
5
+ Every component is the `@journ/nude` primitive of the same name plus a style layer. Behavior, state and accessibility come from nude; bloom adds classes, icons and enter animations. The API matches nude's, plus variant props such as `tone` and `size`.
6
+
7
+ ## Install
8
+
9
+ ```sh
10
+ pnpm add @journ/bloom react react-dom
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ Import the stylesheet once, at the root of the app. It is precompiled, so the app does not need Tailwind.
16
+
17
+ ```ts
18
+ import '@journ/bloom/styles.css'
19
+ ```
20
+
21
+ ```tsx
22
+ import { Button } from '@journ/bloom/button'
23
+ import { Dialog } from '@journ/bloom/dialog'
24
+
25
+ function Example() {
26
+ return (
27
+ <Dialog.Root>
28
+ <Dialog.Trigger asChild>
29
+ <Button tone='coral'>Delete</Button>
30
+ </Dialog.Trigger>
31
+ <Dialog.Content>
32
+ <Dialog.Title>Delete file</Dialog.Title>
33
+ <Dialog.Description>This cannot be undone.</Dialog.Description>
34
+ <Dialog.Actions>
35
+ <Dialog.Close asChild>
36
+ <Button tone='ghost'>Cancel</Button>
37
+ </Dialog.Close>
38
+ </Dialog.Actions>
39
+ </Dialog.Content>
40
+ </Dialog.Root>
41
+ )
42
+ }
43
+ ```
44
+
45
+ Every component has its own entry (`@journ/bloom/button`, `@journ/bloom/dialog`, ...). `@journ/bloom` exports all of them.
46
+
47
+ ### With your own Tailwind
48
+
49
+ Apps that already run Tailwind v4 can skip `styles.css`, import the tokens and scan the package instead. The class names are plain, unprefixed utilities and the tokens are named `bloom-*`.
50
+
51
+ ```css
52
+ @import 'tailwindcss';
53
+ @import '@journ/bloom/tokens.css';
54
+ @source '../node_modules/@journ/bloom/dist';
55
+ ```
56
+
57
+ ## Design system
58
+
59
+ [DESIGN.md](./DESIGN.md) describes the look: color and tones, themes, shape, typography, layout, motion, the component list, and the rules that `pnpm lint` enforces. Read it before adding or changing a component.
@@ -0,0 +1,9 @@
1
+ import * as Nude from "@journ/nude/accordion-parts";
2
+ import { ComponentPropsWithRef } from "react";
3
+ //#region src/accordion-parts.d.ts
4
+ declare const Root: any;
5
+ declare const Item: ({ className, ...props }: ComponentPropsWithRef<typeof Nude.Item>) => import("react").JSX.Element;
6
+ declare const Trigger: ({ className, children, ...props }: ComponentPropsWithRef<typeof Nude.Trigger>) => import("react").JSX.Element;
7
+ declare const Content: ({ className, children, ...props }: ComponentPropsWithRef<typeof Nude.Content>) => import("react").JSX.Element;
8
+ //#endregion
9
+ export { Content, Item, Root, Trigger };
@@ -0,0 +1,37 @@
1
+ "use client";
2
+ import { cn } from "./lib/cn.js";
3
+ import { pressReset } from "./lib/interactive.js";
4
+ import { useExpand } from "./lib/use-enter.js";
5
+ import { displayHeading } from "./lib/text-styles.js";
6
+ import * as Nude from "@journ/nude/accordion-parts";
7
+ import { ChevronDown } from "lucide-react";
8
+ import { jsx, jsxs } from "react/jsx-runtime";
9
+ //#region src/accordion-parts.tsx
10
+ const Root = Nude.Root;
11
+ const Item = ({ className, ...props }) => /* @__PURE__ */ jsx(Nude.Item, {
12
+ ...props,
13
+ className: cn("border-b border-current/15 last:border-b-0", className)
14
+ });
15
+ const Trigger = ({ className, children, ...props }) => /* @__PURE__ */ jsxs(Nude.Trigger, {
16
+ ...props,
17
+ classNames: { header: "flex" },
18
+ className: cn(pressReset, displayHeading, "group flex flex-1 cursor-default items-center justify-between gap-4 py-4 text-left outline-none", "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-bloom-sky disabled:opacity-50", className),
19
+ children: [children, /* @__PURE__ */ jsx(ChevronDown, {
20
+ "aria-hidden": true,
21
+ className: "size-4 shrink-0 transition-transform group-data-[state=open]:rotate-180"
22
+ })]
23
+ });
24
+ const Content = ({ className, children, ...props }) => {
25
+ const element = useExpand();
26
+ return /* @__PURE__ */ jsx(Nude.Content, {
27
+ ref: element,
28
+ ...props,
29
+ className: "overflow-hidden",
30
+ children: /* @__PURE__ */ jsx("div", {
31
+ className: cn("pb-4 text-sm/5", className),
32
+ children
33
+ })
34
+ });
35
+ };
36
+ //#endregion
37
+ export { Content, Item, Root, Trigger };
@@ -0,0 +1,10 @@
1
+ import { Content, Item, Trigger } from "./accordion-parts.js";
2
+ //#region src/accordion.d.ts
3
+ declare const Accordion: {
4
+ Root: any;
5
+ Item: typeof Item;
6
+ Trigger: typeof Trigger;
7
+ Content: typeof Content;
8
+ };
9
+ //#endregion
10
+ export { Accordion };
@@ -0,0 +1,10 @@
1
+ import { Content, Item, Root, Trigger } from "./accordion-parts.js";
2
+ //#region src/accordion.tsx
3
+ const Accordion = {
4
+ Root,
5
+ Item,
6
+ Trigger,
7
+ Content
8
+ };
9
+ //#endregion
10
+ export { Accordion };
@@ -0,0 +1,16 @@
1
+ import { Actions } from "./lib/modal-parts.js";
2
+ import { centeredPanel } from "./lib/overlay.js";
3
+ import { VariantProps } from "./lib/tv.js";
4
+ import { ComponentPropsWithRef } from "react";
5
+ import * as Nude from "@journ/nude/alert-dialog-parts";
6
+ //#region src/alert-dialog-parts.d.ts
7
+ declare const Root: any;
8
+ declare const Trigger: any;
9
+ declare const Cancel: any;
10
+ declare const Action: any;
11
+ type ContentProps = Omit<ComponentPropsWithRef<typeof Nude.Content>, 'classNames' | 'overlayRef'> & VariantProps<typeof centeredPanel>;
12
+ declare const Content: ({ tone, className, ...props }: ContentProps) => import("react").JSX.Element;
13
+ declare const Title: ({ className, ...props }: ComponentPropsWithRef<typeof Nude.Title>) => import("react").JSX.Element;
14
+ declare const Description: ({ className, ...props }: ComponentPropsWithRef<typeof Nude.Description>) => import("react").JSX.Element;
15
+ //#endregion
16
+ export { Action, Actions, Cancel, Content, Description, Root, Title, Trigger };
@@ -0,0 +1,33 @@
1
+ "use client";
2
+ import { cn } from "./lib/cn.js";
3
+ import { modalPanelEnter } from "./lib/use-enter.js";
4
+ import { displayTitle, mutedText } from "./lib/text-styles.js";
5
+ import { Actions } from "./lib/modal-parts.js";
6
+ import { centeredPanel } from "./lib/overlay.js";
7
+ import { ModalContent } from "./lib/modal-content.js";
8
+ import { jsx } from "react/jsx-runtime";
9
+ import * as Nude from "@journ/nude/alert-dialog-parts";
10
+ //#region src/alert-dialog-parts.tsx
11
+ const Root = Nude.Root;
12
+ const Trigger = Nude.Trigger;
13
+ const Cancel = Nude.Cancel;
14
+ const Action = Nude.Action;
15
+ const Content = ({ tone, className, ...props }) => /* @__PURE__ */ jsx(ModalContent, {
16
+ content: Nude.Content,
17
+ enter: modalPanelEnter,
18
+ ...props,
19
+ className: centeredPanel({
20
+ tone,
21
+ class: className
22
+ })
23
+ });
24
+ const Title = ({ className, ...props }) => /* @__PURE__ */ jsx(Nude.Title, {
25
+ ...props,
26
+ className: cn(displayTitle, className)
27
+ });
28
+ const Description = ({ className, ...props }) => /* @__PURE__ */ jsx(Nude.Description, {
29
+ ...props,
30
+ className: cn(mutedText, className)
31
+ });
32
+ //#endregion
33
+ export { Action, Actions, Cancel, Content, Description, Root, Title, Trigger };
@@ -0,0 +1,15 @@
1
+ import { Actions } from "./lib/modal-parts.js";
2
+ import { Content, Description, Title } from "./alert-dialog-parts.js";
3
+ //#region src/alert-dialog.d.ts
4
+ declare const AlertDialog: {
5
+ Root: any;
6
+ Trigger: any;
7
+ Content: typeof Content;
8
+ Title: typeof Title;
9
+ Description: typeof Description;
10
+ Actions: typeof Actions;
11
+ Cancel: any;
12
+ Action: any;
13
+ };
14
+ //#endregion
15
+ export { AlertDialog };
@@ -0,0 +1,15 @@
1
+ import { Actions } from "./lib/modal-parts.js";
2
+ import { Action, Cancel, Content, Description, Root, Title, Trigger } from "./alert-dialog-parts.js";
3
+ //#region src/alert-dialog.tsx
4
+ const AlertDialog = {
5
+ Root,
6
+ Trigger,
7
+ Content,
8
+ Title,
9
+ Description,
10
+ Actions,
11
+ Cancel,
12
+ Action
13
+ };
14
+ //#endregion
15
+ export { AlertDialog };
@@ -0,0 +1,48 @@
1
+ import { VariantProps } from "./lib/tv.js";
2
+ import { ComponentPropsWithRef } from "react";
3
+ //#region src/alert.d.ts
4
+ declare const alertRoot: import("tailwind-variants").TVReturnType<{
5
+ tone: {
6
+ readonly surface: "bg-bloom-surface dark:bg-bloom-surface-dark font-bloom-display text-bloom-foreground dark:text-bloom-foreground-dark border border-bloom-line dark:border-bloom-line-dark";
7
+ readonly coral: string;
8
+ readonly yellow: string;
9
+ readonly indigo: string;
10
+ readonly mint: string;
11
+ readonly sky: string;
12
+ };
13
+ }, undefined, "rounded-bloom flex items-start gap-3 p-4", {
14
+ tone: {
15
+ readonly surface: "bg-bloom-surface dark:bg-bloom-surface-dark font-bloom-display text-bloom-foreground dark:text-bloom-foreground-dark border border-bloom-line dark:border-bloom-line-dark";
16
+ readonly coral: string;
17
+ readonly yellow: string;
18
+ readonly indigo: string;
19
+ readonly mint: string;
20
+ readonly sky: string;
21
+ };
22
+ }, undefined, import("tailwind-variants").TVReturnTypeLike<{
23
+ tone: {
24
+ readonly surface: "bg-bloom-surface dark:bg-bloom-surface-dark font-bloom-display text-bloom-foreground dark:text-bloom-foreground-dark border border-bloom-line dark:border-bloom-line-dark";
25
+ readonly coral: string;
26
+ readonly yellow: string;
27
+ readonly indigo: string;
28
+ readonly mint: string;
29
+ readonly sky: string;
30
+ };
31
+ }, undefined>>;
32
+ type RootProps = ComponentPropsWithRef<'div'> & VariantProps<typeof alertRoot>;
33
+ declare const Root: ({ tone, className, ...props }: RootProps) => import("react").JSX.Element;
34
+ declare const Icon: ({ className, ...props }: ComponentPropsWithRef<'span'>) => import("react").JSX.Element;
35
+ declare const Body: ({ className, ...props }: ComponentPropsWithRef<'div'>) => import("react").JSX.Element;
36
+ declare const Title: ({ className, ...props }: ComponentPropsWithRef<'p'>) => import("react").JSX.Element;
37
+ declare const Description: ({ className, ...props }: ComponentPropsWithRef<'p'>) => import("react").JSX.Element;
38
+ declare const Actions: ({ className, ...props }: ComponentPropsWithRef<'div'>) => import("react").JSX.Element;
39
+ declare const Alert: {
40
+ Root: typeof Root;
41
+ Icon: typeof Icon;
42
+ Body: typeof Body;
43
+ Title: typeof Title;
44
+ Description: typeof Description;
45
+ Actions: typeof Actions;
46
+ };
47
+ //#endregion
48
+ export { Alert, type RootProps as AlertProps };