@c9up/nebula 0.1.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 (327) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +256 -0
  3. package/dist/adapters/css.d.ts +33 -0
  4. package/dist/adapters/css.js +67 -0
  5. package/dist/adapters/index.d.ts +16 -0
  6. package/dist/adapters/index.js +27 -0
  7. package/dist/adapters/tailwind.d.ts +25 -0
  8. package/dist/adapters/tailwind.js +122 -0
  9. package/dist/adapters/types.d.ts +49 -0
  10. package/dist/adapters/types.js +21 -0
  11. package/dist/adapters/unocss.d.ts +17 -0
  12. package/dist/adapters/unocss.js +107 -0
  13. package/dist/atoms/AspectRatio.d.ts +17 -0
  14. package/dist/atoms/AspectRatio.js +19 -0
  15. package/dist/atoms/Avatar.d.ts +23 -0
  16. package/dist/atoms/Avatar.js +46 -0
  17. package/dist/atoms/Badge.d.ts +25 -0
  18. package/dist/atoms/Badge.js +28 -0
  19. package/dist/atoms/Button.d.ts +58 -0
  20. package/dist/atoms/Button.js +67 -0
  21. package/dist/atoms/Checkbox.d.ts +41 -0
  22. package/dist/atoms/Checkbox.js +47 -0
  23. package/dist/atoms/Input.d.ts +36 -0
  24. package/dist/atoms/Input.js +50 -0
  25. package/dist/atoms/Kbd.d.ts +14 -0
  26. package/dist/atoms/Kbd.js +17 -0
  27. package/dist/atoms/Label.d.ts +24 -0
  28. package/dist/atoms/Label.js +26 -0
  29. package/dist/atoms/Marker.d.ts +42 -0
  30. package/dist/atoms/Marker.js +55 -0
  31. package/dist/atoms/NativeSelect.d.ts +43 -0
  32. package/dist/atoms/NativeSelect.js +81 -0
  33. package/dist/atoms/Progress.d.ts +21 -0
  34. package/dist/atoms/Progress.js +48 -0
  35. package/dist/atoms/ScrollArea.d.ts +28 -0
  36. package/dist/atoms/ScrollArea.js +37 -0
  37. package/dist/atoms/Separator.d.ts +16 -0
  38. package/dist/atoms/Separator.js +21 -0
  39. package/dist/atoms/Skeleton.d.ts +13 -0
  40. package/dist/atoms/Skeleton.js +18 -0
  41. package/dist/atoms/Slider.d.ts +30 -0
  42. package/dist/atoms/Slider.js +40 -0
  43. package/dist/atoms/Spinner.d.ts +17 -0
  44. package/dist/atoms/Spinner.js +20 -0
  45. package/dist/atoms/Switch.d.ts +24 -0
  46. package/dist/atoms/Switch.js +41 -0
  47. package/dist/atoms/Textarea.d.ts +28 -0
  48. package/dist/atoms/Textarea.js +36 -0
  49. package/dist/atoms/Toggle.d.ts +38 -0
  50. package/dist/atoms/Toggle.js +45 -0
  51. package/dist/atoms/index.d.ts +32 -0
  52. package/dist/atoms/index.js +32 -0
  53. package/dist/cli/add.d.ts +44 -0
  54. package/dist/cli/add.js +99 -0
  55. package/dist/cli/index.d.ts +10 -0
  56. package/dist/cli/index.js +10 -0
  57. package/dist/cli/registry.d.ts +31 -0
  58. package/dist/cli/registry.js +83 -0
  59. package/dist/config.d.ts +81 -0
  60. package/dist/config.js +43 -0
  61. package/dist/configure.d.ts +34 -0
  62. package/dist/configure.js +85 -0
  63. package/dist/index.d.ts +41 -0
  64. package/dist/index.js +41 -0
  65. package/dist/lib/children.d.ts +28 -0
  66. package/dist/lib/children.js +27 -0
  67. package/dist/lib/cn.d.ts +13 -0
  68. package/dist/lib/cn.js +13 -0
  69. package/dist/lib/cva.d.ts +83 -0
  70. package/dist/lib/cva.js +178 -0
  71. package/dist/lib/icons.d.ts +44 -0
  72. package/dist/lib/icons.js +79 -0
  73. package/dist/lib/id.d.ts +31 -0
  74. package/dist/lib/id.js +41 -0
  75. package/dist/lib/index.d.ts +12 -0
  76. package/dist/lib/index.js +12 -0
  77. package/dist/lib/motion.d.ts +31 -0
  78. package/dist/lib/motion.js +39 -0
  79. package/dist/lib/props.d.ts +48 -0
  80. package/dist/lib/props.js +59 -0
  81. package/dist/lib/styled.d.ts +31 -0
  82. package/dist/lib/styled.js +39 -0
  83. package/dist/molecules/Accordion.d.ts +35 -0
  84. package/dist/molecules/Accordion.js +117 -0
  85. package/dist/molecules/Alert.d.ts +30 -0
  86. package/dist/molecules/Alert.js +35 -0
  87. package/dist/molecules/Attachment.d.ts +39 -0
  88. package/dist/molecules/Attachment.js +103 -0
  89. package/dist/molecules/Breadcrumb.d.ts +29 -0
  90. package/dist/molecules/Breadcrumb.js +73 -0
  91. package/dist/molecules/Bubble.d.ts +56 -0
  92. package/dist/molecules/Bubble.js +92 -0
  93. package/dist/molecules/ButtonGroup.d.ts +23 -0
  94. package/dist/molecules/ButtonGroup.js +30 -0
  95. package/dist/molecules/Card.d.ts +17 -0
  96. package/dist/molecules/Card.js +17 -0
  97. package/dist/molecules/Collapsible.d.ts +40 -0
  98. package/dist/molecules/Collapsible.js +71 -0
  99. package/dist/molecules/Empty.d.ts +16 -0
  100. package/dist/molecules/Empty.js +16 -0
  101. package/dist/molecules/Field.d.ts +54 -0
  102. package/dist/molecules/Field.js +82 -0
  103. package/dist/molecules/InputGroup.d.ts +33 -0
  104. package/dist/molecules/InputGroup.js +35 -0
  105. package/dist/molecules/InputOTP.d.ts +33 -0
  106. package/dist/molecules/InputOTP.js +140 -0
  107. package/dist/molecules/Item.d.ts +41 -0
  108. package/dist/molecules/Item.js +48 -0
  109. package/dist/molecules/Message.d.ts +43 -0
  110. package/dist/molecules/Message.js +82 -0
  111. package/dist/molecules/Pagination.d.ts +41 -0
  112. package/dist/molecules/Pagination.js +144 -0
  113. package/dist/molecules/RadioGroup.d.ts +32 -0
  114. package/dist/molecules/RadioGroup.js +65 -0
  115. package/dist/molecules/Resizable.d.ts +37 -0
  116. package/dist/molecules/Resizable.js +136 -0
  117. package/dist/molecules/Table.d.ts +37 -0
  118. package/dist/molecules/Table.js +78 -0
  119. package/dist/molecules/Tabs.d.ts +37 -0
  120. package/dist/molecules/Tabs.js +109 -0
  121. package/dist/molecules/ToggleGroup.d.ts +37 -0
  122. package/dist/molecules/ToggleGroup.js +68 -0
  123. package/dist/molecules/Typography.d.ts +26 -0
  124. package/dist/molecules/Typography.js +67 -0
  125. package/dist/molecules/index.d.ts +37 -0
  126. package/dist/molecules/index.js +37 -0
  127. package/dist/organisms/AlertDialog.d.ts +40 -0
  128. package/dist/organisms/AlertDialog.js +104 -0
  129. package/dist/organisms/Calendar.d.ts +96 -0
  130. package/dist/organisms/Calendar.js +353 -0
  131. package/dist/organisms/Carousel.d.ts +29 -0
  132. package/dist/organisms/Carousel.js +128 -0
  133. package/dist/organisms/Chart.d.ts +55 -0
  134. package/dist/organisms/Chart.js +189 -0
  135. package/dist/organisms/Combobox.d.ts +41 -0
  136. package/dist/organisms/Combobox.js +102 -0
  137. package/dist/organisms/Command.d.ts +46 -0
  138. package/dist/organisms/Command.js +171 -0
  139. package/dist/organisms/CommandDialog.d.ts +42 -0
  140. package/dist/organisms/CommandDialog.js +116 -0
  141. package/dist/organisms/ContextMenu.d.ts +29 -0
  142. package/dist/organisms/ContextMenu.js +102 -0
  143. package/dist/organisms/DataTable.d.ts +50 -0
  144. package/dist/organisms/DataTable.js +257 -0
  145. package/dist/organisms/DatePicker.d.ts +34 -0
  146. package/dist/organisms/DatePicker.js +115 -0
  147. package/dist/organisms/DateRangePicker.d.ts +34 -0
  148. package/dist/organisms/DateRangePicker.js +122 -0
  149. package/dist/organisms/Dialog.d.ts +50 -0
  150. package/dist/organisms/Dialog.js +116 -0
  151. package/dist/organisms/Drawer.d.ts +40 -0
  152. package/dist/organisms/Drawer.js +131 -0
  153. package/dist/organisms/DropdownMenu.d.ts +30 -0
  154. package/dist/organisms/DropdownMenu.js +91 -0
  155. package/dist/organisms/Form.d.ts +114 -0
  156. package/dist/organisms/Form.js +151 -0
  157. package/dist/organisms/HoverCard.d.ts +30 -0
  158. package/dist/organisms/HoverCard.js +68 -0
  159. package/dist/organisms/Menubar.d.ts +28 -0
  160. package/dist/organisms/Menubar.js +147 -0
  161. package/dist/organisms/MessageScroller.d.ts +36 -0
  162. package/dist/organisms/MessageScroller.js +124 -0
  163. package/dist/organisms/NavigationMenu.d.ts +41 -0
  164. package/dist/organisms/NavigationMenu.js +139 -0
  165. package/dist/organisms/Popover.d.ts +33 -0
  166. package/dist/organisms/Popover.js +58 -0
  167. package/dist/organisms/Questionnaire.d.ts +74 -0
  168. package/dist/organisms/Questionnaire.js +235 -0
  169. package/dist/organisms/Select.d.ts +47 -0
  170. package/dist/organisms/Select.js +250 -0
  171. package/dist/organisms/Sheet.d.ts +34 -0
  172. package/dist/organisms/Sheet.js +107 -0
  173. package/dist/organisms/Sidebar.d.ts +100 -0
  174. package/dist/organisms/Sidebar.js +228 -0
  175. package/dist/organisms/Toaster.d.ts +67 -0
  176. package/dist/organisms/Toaster.js +189 -0
  177. package/dist/organisms/Tooltip.d.ts +39 -0
  178. package/dist/organisms/Tooltip.js +94 -0
  179. package/dist/organisms/index.d.ts +40 -0
  180. package/dist/organisms/index.js +40 -0
  181. package/dist/primitives/controllable.d.ts +38 -0
  182. package/dist/primitives/controllable.js +43 -0
  183. package/dist/primitives/dismissable.d.ts +53 -0
  184. package/dist/primitives/dismissable.js +158 -0
  185. package/dist/primitives/floating.d.ts +116 -0
  186. package/dist/primitives/floating.js +289 -0
  187. package/dist/primitives/floatingSurface.d.ts +56 -0
  188. package/dist/primitives/floatingSurface.js +142 -0
  189. package/dist/primitives/focusTrap.d.ts +47 -0
  190. package/dist/primitives/focusTrap.js +101 -0
  191. package/dist/primitives/focusable.d.ts +53 -0
  192. package/dist/primitives/focusable.js +109 -0
  193. package/dist/primitives/index.d.ts +27 -0
  194. package/dist/primitives/index.js +27 -0
  195. package/dist/primitives/menu.d.ts +102 -0
  196. package/dist/primitives/menu.js +356 -0
  197. package/dist/primitives/modalSurface.d.ts +54 -0
  198. package/dist/primitives/modalSurface.js +146 -0
  199. package/dist/primitives/portal.d.ts +34 -0
  200. package/dist/primitives/portal.js +42 -0
  201. package/dist/primitives/presence.d.ts +45 -0
  202. package/dist/primitives/presence.js +152 -0
  203. package/dist/primitives/rovingFocus.d.ts +47 -0
  204. package/dist/primitives/rovingFocus.js +176 -0
  205. package/dist/primitives/scrollLock.d.ts +21 -0
  206. package/dist/primitives/scrollLock.js +75 -0
  207. package/dist/primitives/typeahead.d.ts +39 -0
  208. package/dist/primitives/typeahead.js +123 -0
  209. package/dist/templates/AppShell.d.ts +30 -0
  210. package/dist/templates/AppShell.js +42 -0
  211. package/dist/templates/AuthLayout.d.ts +23 -0
  212. package/dist/templates/AuthLayout.js +42 -0
  213. package/dist/templates/SettingsLayout.d.ts +27 -0
  214. package/dist/templates/SettingsLayout.js +42 -0
  215. package/dist/templates/index.d.ts +16 -0
  216. package/dist/templates/index.js +16 -0
  217. package/nebula.css +2 -0
  218. package/package.json +92 -0
  219. package/registry.json +1107 -0
  220. package/src/adapters/css.ts +75 -0
  221. package/src/adapters/index.ts +40 -0
  222. package/src/adapters/tailwind.ts +134 -0
  223. package/src/adapters/types.ts +53 -0
  224. package/src/adapters/unocss.ts +116 -0
  225. package/src/atoms/AspectRatio.ts +28 -0
  226. package/src/atoms/Avatar.ts +67 -0
  227. package/src/atoms/Badge.ts +46 -0
  228. package/src/atoms/Button.ts +96 -0
  229. package/src/atoms/Checkbox.ts +76 -0
  230. package/src/atoms/Input.ts +76 -0
  231. package/src/atoms/Kbd.ts +28 -0
  232. package/src/atoms/Label.ts +38 -0
  233. package/src/atoms/Marker.ts +79 -0
  234. package/src/atoms/NativeSelect.ts +115 -0
  235. package/src/atoms/Progress.ts +64 -0
  236. package/src/atoms/ScrollArea.ts +53 -0
  237. package/src/atoms/Separator.ts +36 -0
  238. package/src/atoms/Skeleton.ts +24 -0
  239. package/src/atoms/Slider.ts +65 -0
  240. package/src/atoms/Spinner.ts +28 -0
  241. package/src/atoms/Switch.ts +56 -0
  242. package/src/atoms/Textarea.ts +58 -0
  243. package/src/atoms/Toggle.ts +67 -0
  244. package/src/atoms/index.ts +62 -0
  245. package/src/cli/add.ts +126 -0
  246. package/src/cli/index.ts +22 -0
  247. package/src/cli/registry.ts +116 -0
  248. package/src/config.ts +102 -0
  249. package/src/configure.ts +127 -0
  250. package/src/index.ts +53 -0
  251. package/src/lib/children.ts +43 -0
  252. package/src/lib/cn.ts +14 -0
  253. package/src/lib/cva.ts +249 -0
  254. package/src/lib/icons.ts +138 -0
  255. package/src/lib/id.ts +44 -0
  256. package/src/lib/index.ts +21 -0
  257. package/src/lib/motion.ts +48 -0
  258. package/src/lib/props.ts +83 -0
  259. package/src/lib/styled.ts +60 -0
  260. package/src/molecules/Accordion.ts +161 -0
  261. package/src/molecules/Alert.ts +58 -0
  262. package/src/molecules/Attachment.ts +132 -0
  263. package/src/molecules/Breadcrumb.ts +107 -0
  264. package/src/molecules/Bubble.ts +134 -0
  265. package/src/molecules/ButtonGroup.ts +55 -0
  266. package/src/molecules/Card.ts +42 -0
  267. package/src/molecules/Collapsible.ts +89 -0
  268. package/src/molecules/Empty.ts +43 -0
  269. package/src/molecules/Field.ts +131 -0
  270. package/src/molecules/InputGroup.ts +62 -0
  271. package/src/molecules/InputOTP.ts +166 -0
  272. package/src/molecules/Item.ts +89 -0
  273. package/src/molecules/Message.ts +127 -0
  274. package/src/molecules/Pagination.ts +197 -0
  275. package/src/molecules/RadioGroup.ts +104 -0
  276. package/src/molecules/Resizable.ts +166 -0
  277. package/src/molecules/Table.ts +118 -0
  278. package/src/molecules/Tabs.ts +148 -0
  279. package/src/molecules/ToggleGroup.ts +109 -0
  280. package/src/molecules/Typography.ts +94 -0
  281. package/src/molecules/index.ts +144 -0
  282. package/src/organisms/AlertDialog.ts +133 -0
  283. package/src/organisms/Calendar.ts +470 -0
  284. package/src/organisms/Carousel.ts +161 -0
  285. package/src/organisms/Chart.ts +257 -0
  286. package/src/organisms/Combobox.ts +140 -0
  287. package/src/organisms/Command.ts +211 -0
  288. package/src/organisms/CommandDialog.ts +149 -0
  289. package/src/organisms/ContextMenu.ts +125 -0
  290. package/src/organisms/DataTable.ts +328 -0
  291. package/src/organisms/DatePicker.ts +141 -0
  292. package/src/organisms/DateRangePicker.ts +149 -0
  293. package/src/organisms/Dialog.ts +162 -0
  294. package/src/organisms/Drawer.ts +167 -0
  295. package/src/organisms/DropdownMenu.ts +112 -0
  296. package/src/organisms/Form.ts +217 -0
  297. package/src/organisms/HoverCard.ts +92 -0
  298. package/src/organisms/Menubar.ts +167 -0
  299. package/src/organisms/MessageScroller.ts +145 -0
  300. package/src/organisms/NavigationMenu.ts +184 -0
  301. package/src/organisms/Popover.ts +84 -0
  302. package/src/organisms/Questionnaire.ts +322 -0
  303. package/src/organisms/Select.ts +286 -0
  304. package/src/organisms/Sheet.ts +144 -0
  305. package/src/organisms/Sidebar.ts +336 -0
  306. package/src/organisms/Toaster.ts +246 -0
  307. package/src/organisms/Tooltip.ts +124 -0
  308. package/src/organisms/index.ts +134 -0
  309. package/src/primitives/controllable.ts +64 -0
  310. package/src/primitives/dismissable.ts +195 -0
  311. package/src/primitives/floating.ts +453 -0
  312. package/src/primitives/floatingSurface.ts +209 -0
  313. package/src/primitives/focusTrap.ts +132 -0
  314. package/src/primitives/focusable.ts +108 -0
  315. package/src/primitives/index.ts +88 -0
  316. package/src/primitives/menu.ts +491 -0
  317. package/src/primitives/modalSurface.ts +199 -0
  318. package/src/primitives/portal.ts +61 -0
  319. package/src/primitives/presence.ts +185 -0
  320. package/src/primitives/rovingFocus.ts +212 -0
  321. package/src/primitives/scrollLock.ts +79 -0
  322. package/src/primitives/typeahead.ts +156 -0
  323. package/src/templates/AppShell.ts +56 -0
  324. package/src/templates/AuthLayout.ts +66 -0
  325. package/src/templates/SettingsLayout.ts +63 -0
  326. package/src/templates/index.ts +21 -0
  327. package/theme.css +151 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 C9up
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,256 @@
1
+ # @c9up/nebula
2
+
3
+ > shadcn/ui, ported to Aurora, organised as atomic design. Zero runtime dependencies, copy-the-source registry, swappable CSS engine.
4
+
5
+ Part of **[Ream](https://github.com/C9up/ream)** — a Rust-powered, AdonisJS-compatible Node.js framework. Independent, publishable package.
6
+
7
+ ## What this is
8
+
9
+ shadcn/ui is React. [Aurora](https://github.com/C9up/aurora) is a tagged-template DOM runtime with signals and no build step. nebula is the shadcn component set — the same markup, the same Tailwind classes, the same behaviour — written for Aurora.
10
+
11
+ Sixty-nine components across four atomic layers, plus the headless behaviour layer Radix would otherwise provide. Every component in shadcn's registry has a counterpart; several are deliberately narrower, and the [parity section](#parity-with-shadcn) says exactly which.
12
+
13
+ ## Installation
14
+
15
+ ```bash
16
+ ream add @c9up/nebula --adapter tailwind
17
+ ```
18
+
19
+ The same one-step install as every other ream package: it installs the package and dispatches to nebula's `configure()` hook, which writes `config/nebula.ts` and the stylesheet for your chosen engine, then prints the packages to install and the build command to register. It installs nothing itself and edits no `package.json` of yours.
20
+
21
+ Changing the adapter later is `ream configure @c9up/nebula --adapter unocss`.
22
+
23
+ ## Two ways to use it
24
+
25
+ ```ts
26
+ // Import it — quickest to try
27
+ import { Button, Card, CardHeader } from '@c9up/nebula'
28
+
29
+ // Or take the source — what the library is really for
30
+ // $ ream nebula:add button card
31
+ import { Button } from '#pages/atoms/Button.js'
32
+ ```
33
+
34
+ `ream nebula:add` copies the component's source into your project and hands it over. No version, no upgrade path, no wrapper to fight when a design needs one class changed. That is shadcn's premise and nebula keeps it.
35
+
36
+ ```bash
37
+ ream nebula:list # everything in the registry
38
+ ream nebula:list --layer organisms
39
+ ream nebula:add dialog data-table # copies both, plus what they depend on
40
+ ream nebula:add button --force # overwrite your edited copy
41
+ ```
42
+
43
+ Subcommands on the ream binary, not a CLI of nebula's own — the same place
44
+ `ream nova:vapid:generate` lives. The registry logic stays in this package; the
45
+ binary dispatches to it.
46
+
47
+ Copies mirror the package's own layout, so `atoms/Button` finds `../lib/cva.js` for the same reason it does inside nebula. **No import is ever rewritten** — that is where a copy-the-source CLI usually accumulates its edge cases.
48
+
49
+ **JavaScript by default.** An Aurora app serves `resources/pages` to the browser unbuilt — that zero-build-step promise is the framework's premise — so TypeScript dropped into that tree does not run. `ream nebula:add` copies the compiled output instead: valid ESM, `.js` specifiers already correct, and every doc comment intact, so what you own is still readable source. The language is inferred from what your components directory already holds; `--ts` and `--js` override it.
50
+
51
+ ## Zero runtime dependencies
52
+
53
+ shadcn stands on Radix, `clsx`, `tailwind-merge`, `class-variance-authority`, `lucide-react`, `@floating-ui/dom`, `cmdk`, `sonner`, `recharts`, `@tanstack/react-table` and `react-day-picker`. None are React-agnostic, and this workspace had already decided `cn` was worth writing by hand rather than installing two packages. So:
54
+
55
+ | shadcn dependency | nebula |
56
+ | --- | --- |
57
+ | `clsx` + `tailwind-merge` | `cn` from `@c9up/aurora` — already written from scratch there |
58
+ | `class-variance-authority` | `lib/cva.ts` — reimplemented, and it runs its result through `cn` |
59
+ | Radix UI | `primitives/` — focus trap, dismissable layers, roving focus, type-ahead, presence, portals |
60
+ | `@floating-ui/dom` | `primitives/floating.ts` — offset, flip, shift, arrow, available height |
61
+ | `lucide-react` | `lib/icons.ts` — the eighteen glyphs the set needs, inlined |
62
+ | `cmdk` | `organisms/Command.ts` |
63
+ | `sonner` | `organisms/Toaster.ts` |
64
+ | `react-day-picker` + `date-fns` | `organisms/Calendar.ts` — `Date` and `Intl` |
65
+ | `@tanstack/react-table` | `organisms/DataTable.ts` — sort, filter, page, select |
66
+ | `recharts` | `organisms/Chart.ts` — line, area and bar, as inline SVG |
67
+ | `react-hook-form` | `form()` from `@c9up/aurora`, bound by `organisms/Form.ts` |
68
+ | `tw-animate-css` | four keyframes in `theme.css` |
69
+
70
+ Several of those are narrower than what they replace — see [parity with shadcn](#parity-with-shadcn) for the full list rather than a reassuring summary.
71
+
72
+ ## Parity with shadcn
73
+
74
+ Checked against shadcn's published component list, not from memory. Every one of its components has a counterpart here — including the conversational set (`Bubble`, `Message`, `MessageScroller`, `Attachment`, `Marker`), `NativeSelect` and `Questionnaire` — and the ~40 simple ones are faithful down to the class strings, the variants and the ARIA attributes.
75
+
76
+ Two of shadcn's entries have no direct counterpart on purpose. `DirectionProvider` is React context; Aurora has none, and the direction belongs on `<html dir>` — what it was really buying is [RTL support](#right-to-left), which is handled in the placement engine instead. `Form` has been folded into `Field` upstream; nebula ships both, with `Form` binding Aurora's own form controller. The components shadcn builds by wrapping a third-party library are reimplementations, and they are narrower. Stated plainly, because "complete port" would not be true:
77
+
78
+ | Component | shadcn | nebula |
79
+ | --- | --- | --- |
80
+ | Chart | Recharts, in full | line, area and bar over one categorical axis |
81
+ | DataTable | TanStack Table (column grouping, virtualisation, pinning, faceted filters, server-side) | sort, filter, page, select — in memory |
82
+ | Sidebar | ~15 parts | the parts that are not re-skinned atoms — see below |
83
+ | Carousel | embla (loop, autoplay, N slides per view) | scroll-snap, one slide per view, no loop or autoplay |
84
+ | Toaster | sonner (promise toasts, arbitrary JSX, multiple positions) | four variants, action, pause on hover |
85
+ | Resizable | arbitrary nesting, persisted layouts, collapse-to-zero | two panes, one handle |
86
+ | Combobox | single, multi-select and creatable recipes | single-select |
87
+ | ScrollArea | scrollbars redrawn by Radix | native scrollbars, styled |
88
+ | Calendar | react-day-picker, every selection mode | single date and range; no multi-month, no multi-select |
89
+ | Questionnaire | branching logic, validation schemas | linear steps; single, multiple, freeform, skippable |
90
+ | Bubble / Message | rich composition slots | the parts nebula's own layout needs |
91
+
92
+ Two API-wide differences, both consequences of the runtime rather than choices about scope: there is no `asChild` (a compiled template has no element to clone), and compound components take data rather than children (Aurora has no React context).
93
+
94
+ **The Sidebar deserves its own note**, because porting it part-for-part would have fought the atomic taxonomy rather than following it. `SidebarInput`, `SidebarSeparator` and `SidebarMenuSkeleton` are the existing `Input`, `Separator` and `Skeleton` atoms with a prefix — redeclaring them would break the composition rule the whole library is organised on. `SidebarProvider` is React context, and nebula's sidebar owns its own shared signal instead. `SidebarInset` is the content column beside the rail, which is `AppShell`, a template. What was genuinely missing and has been added: `SidebarMenuSub`, `SidebarMenuSubItem`, `SidebarMenuAction`, a badge slot, and tooltips when the rail is collapsed.
95
+
96
+ ## Right to left
97
+
98
+ The placement engine mirrors itself. Placements are written physically —
99
+ `"right-start"` for a submenu — because that is what reads clearly at the call
100
+ site, and `resolvePosition` flips them when the anchor computes to
101
+ `direction: rtl`. `autoPosition` reads that off the anchor on every update, so
102
+ no component passes a flag and a language switcher flipped mid-session moves
103
+ open surfaces with it.
104
+
105
+ The mirror is not symmetric, which is the part worth knowing: for `left`/`right`
106
+ the *side* swaps and the alignment is untouched; for `top`/`bottom` the side
107
+ stays and the *alignment* swaps. Mirroring both halves of `bottom-start` would
108
+ land it back where it started.
109
+
110
+ Components use logical properties (`ms-*`, `me-*`, `start-*`, `end-*`) wherever
111
+ a side is meant relative to the text, so a `Bubble` aligned to the end sits
112
+ right in English and left in Arabic. Where a side is a genuine layout choice —
113
+ which edge a `Sheet` enters from, which side a `Sidebar` occupies — it stays
114
+ physical, because that is what the caller means.
115
+
116
+ ## Choose your CSS engine
117
+
118
+ nebula declares **no CSS dependency at all**, not even a peer one. You install the engine you want; `config/nebula.ts` names it; nebula generates the matching stubs and build command. Same arrangement AdonisJS uses for its asset bundler.
119
+
120
+ ```ts
121
+ // config/nebula.ts
122
+ import { defineConfig } from '@c9up/nebula'
123
+
124
+ export default defineConfig({
125
+ adapter: 'tailwind', // 'tailwind' | 'unocss' | 'css'
126
+ paths: {
127
+ components: 'resources/pages',
128
+ css: 'resources/css/app.css',
129
+ output: 'public/app.css',
130
+ },
131
+ })
132
+ ```
133
+
134
+ | Adapter | What it does | You install |
135
+ | --- | --- | --- |
136
+ | `tailwind` | Tailwind v4, configured in CSS. What shadcn itself targets. | `tailwindcss @tailwindcss/cli` |
137
+ | `unocss` | `presetWind4` — same class syntax, no PostCSS, faster. | `unocss @unocss/cli` |
138
+ | `css` | Nothing. nebula ships a prebuilt stylesheet. | — |
139
+
140
+ All three consume the same class names, which is what lets one set of components serve all of them. Switching is a one-word change plus `ream configure @c9up/nebula`.
141
+
142
+ **The `css` adapter's limit, stated plainly.** `nebula.css` is compiled at nebula's release time and covers the components as published. Edit a copied component to add a utility nebula never used and nothing emits it — the class silently does nothing. Use it when you take the components as they are; use `tailwind` or `unocss` when you intend to retune them.
143
+
144
+ An engine with a different authoring model — Panda's recipes, StyleX — cannot go behind this interface. It would need a second version of every component.
145
+
146
+ ## Atomic design
147
+
148
+ shadcn is a flat `ui/` directory. nebula sorts the same components into layers, and the layer is a property of the component: `nebula add button` knows Button is an atom.
149
+
150
+ ```
151
+ resources/pages/
152
+ ├── lib/ cn, cva, icons, ids, reactive props
153
+ ├── primitives/ the headless layer — focus, dismissal, placement, presence
154
+ ├── atoms/ one element, composing nothing from nebula
155
+ ├── molecules/ assembles atoms, or owns state across several elements
156
+ ├── organisms/ portals, traps focus, floats, or coordinates molecules
157
+ └── templates/ page skeletons
158
+ ```
159
+
160
+ The rule is composition, not complexity. Slider is an atom though it is interactive, because it is one input. Card is a molecule though it is trivial, because it assembles parts.
161
+
162
+ <details>
163
+ <summary><strong>All 69 components</strong></summary>
164
+
165
+ **atoms (19)** — AspectRatio, Avatar, Badge, Button, Checkbox, Input, Kbd, Label, Marker, NativeSelect, Progress, ScrollArea, Separator, Skeleton, Slider, Spinner, Switch, Textarea, Toggle
166
+
167
+ **molecules (21)** — Accordion, Alert, Attachment, Breadcrumb, Bubble, ButtonGroup, Card, Collapsible, Empty, Field, InputGroup, InputOTP, Item, Message, Pagination, RadioGroup, Resizable, Table, Tabs, ToggleGroup, Typography
168
+
169
+ **organisms (26)** — AlertDialog, Calendar, Carousel, Chart, Combobox, Command, CommandDialog, ContextMenu, DataTable, DatePicker, DateRangePicker, Dialog, Drawer, DropdownMenu, Form, HoverCard, Menubar, MessageScroller, NavigationMenu, Popover, Questionnaire, Select, Sheet, Sidebar, Toaster, Tooltip
170
+
171
+ **templates (3)** — AppShell, AuthLayout, SettingsLayout
172
+
173
+ </details>
174
+
175
+ ## The API difference
176
+
177
+ shadcn composes through React context:
178
+
179
+ ```tsx
180
+ <Tabs defaultValue="account">
181
+ <TabsList><TabsTrigger value="account">Account</TabsTrigger></TabsList>
182
+ <TabsContent value="account">…</TabsContent>
183
+ </Tabs>
184
+ ```
185
+
186
+ Aurora has no context, and the workarounds — a factory returning bound parts, a handle threaded through props — are more machinery for less clarity. So compound components take data:
187
+
188
+ ```ts
189
+ Tabs({
190
+ defaultValue: 'account',
191
+ items: [
192
+ { value: 'account', label: 'Account', content: html`…` },
193
+ { value: 'password', label: 'Password', content: html`…` },
194
+ ],
195
+ })
196
+ ```
197
+
198
+ The rendered markup is unchanged, so shadcn's CSS and its examples still read across. Free-form containers take named slots instead:
199
+
200
+ ```ts
201
+ Dialog({
202
+ trigger: 'Edit profile',
203
+ title: 'Edit profile',
204
+ description: "Make changes here. Click save when you're done.",
205
+ children: [TextField({ bind: bind(profile, 'name'), label: 'Name' })],
206
+ footer: SubmitButton({ form: profile, label: 'Save' }),
207
+ })
208
+ ```
209
+
210
+ There is no `asChild`. React's Slot clones an element and merges props into it; Aurora templates are compiled markup with nothing to clone. Where shadcn writes `<Button asChild><a/></Button>`, nebula exports the variants:
211
+
212
+ ```ts
213
+ html`<a href="/docs" class="${buttonVariants({ variant: 'outline' })}">Docs</a>`
214
+ ```
215
+
216
+ ## Reactive props
217
+
218
+ Aurora never re-renders. Any prop that can change is `Reactive<T>` — pass a constant when it never moves, an accessor when it does:
219
+
220
+ ```ts
221
+ Button({ disabled: true }) // static
222
+ Button({ disabled: () => form.submitting() }) // live
223
+ ```
224
+
225
+ A value read once at setup is frozen for the lifetime of the node, so `disabled: form.submitting()` is a bug that only shows after the first submit.
226
+
227
+ ## Accessibility
228
+
229
+ The headless layer is most of this package, and it is where shadcn's behaviour actually lives. What is implemented, rather than approximated:
230
+
231
+ - **Focus trap** — Tab wraps, focus returns to the trigger, and a `focusin` handler catches focus arriving by any other route.
232
+ - **Modal** — the page behind is `aria-hidden`, not merely unreachable by Tab. Trapping keyboard focus does nothing for a reader navigating by landmark.
233
+ - **Dismissable layers** — one stack. Escape reaches the topmost layer that accepts it; a pointer outside closes layers above the one it landed in and no further.
234
+ - **Roving focus** — a menu, tab list or toolbar is one tab stop.
235
+ - **Type-ahead** — accumulating buffer, and a repeated letter cycles.
236
+ - **Charts** — the same data is emitted as a visually hidden `<table>`. No ARIA makes an SVG readable.
237
+ - **Live regions** — `polite` for toasts, `assertive` for errors, mounted empty before anything arrives.
238
+
239
+ ## Development
240
+
241
+ ```bash
242
+ pnpm test # 391 unit tests
243
+ pnpm typecheck
244
+ pnpm lint
245
+ pnpm registry # regenerate registry.json from the source tree
246
+ pnpm css # freeze nebula.css for the `css` adapter
247
+ pnpm build
248
+ ```
249
+
250
+ `registry.json` is derived from the imports rather than maintained by hand, and a test asserts that every file an item ships actually resolves — the failure it guards is otherwise silent, showing up in a user's build rather than here.
251
+
252
+ Coverage sits around 83% of statements. The shape matters more than the number: every component is mounted and unmounted by `render-smoke.test.ts`, the shared surfaces and the headless primitives are tested directly, and the overlays are opened rather than only rendered closed. What the suite cannot reach is pointer-drag — happy-dom has no `setPointerCapture`, so Drawer's swipe-to-dismiss and Resizable's drag are covered by their keyboard paths only.
253
+
254
+ ## Licence
255
+
256
+ MIT
@@ -0,0 +1,33 @@
1
+ /**
2
+ * The engine-free adapter.
3
+ *
4
+ * No Tailwind, no UnoCSS, no build step in the app at all: nebula ships a
5
+ * prebuilt `nebula.css` holding exactly the utilities its own components
6
+ * reference, and the app imports it.
7
+ *
8
+ * **Where the compilation actually happens.** nebula's own release build runs
9
+ * Tailwind over `src/**` and freezes the result (`scripts/build-css.ts`).
10
+ * Tailwind is therefore a devDependency *of nebula* and never reaches the
11
+ * app's dependency tree — which is the whole point. The set of utilities is
12
+ * knowable ahead of time because the components are nebula's, and they do not
13
+ * change between one app's install and another's.
14
+ *
15
+ * **The limitation, stated plainly.** The frozen stylesheet covers the
16
+ * components as nebula wrote them. The registry copies those sources into the
17
+ * app for the user to edit, and the moment an edit introduces a utility nebula
18
+ * never used — `bg-emerald-500`, `grid-cols-7` — nothing emits it, and the
19
+ * class silently does nothing.
20
+ *
21
+ * So this adapter suits an app that takes the components as they are and
22
+ * writes its own hand-rolled CSS around them. An app that intends to retune
23
+ * components through their class strings wants `tailwind` or `unocss`, where
24
+ * the scanner sees the edits.
25
+ *
26
+ * Lifting that limitation means compiling the app's copied sources too — a
27
+ * utility scanner and a CSS emitter over the finite surface nebula uses. That
28
+ * is a build-time, string-heavy, run-on-every-build job, which is the profile
29
+ * where moving it into `ream-cli` (Rust) pays; it is the one part of nebula
30
+ * where Rust would earn its place.
31
+ */
32
+ import type { StyleAdapter } from "./types.js";
33
+ export declare const cssAdapter: StyleAdapter;
@@ -0,0 +1,67 @@
1
+ /**
2
+ * The engine-free adapter.
3
+ *
4
+ * No Tailwind, no UnoCSS, no build step in the app at all: nebula ships a
5
+ * prebuilt `nebula.css` holding exactly the utilities its own components
6
+ * reference, and the app imports it.
7
+ *
8
+ * **Where the compilation actually happens.** nebula's own release build runs
9
+ * Tailwind over `src/**` and freezes the result (`scripts/build-css.ts`).
10
+ * Tailwind is therefore a devDependency *of nebula* and never reaches the
11
+ * app's dependency tree — which is the whole point. The set of utilities is
12
+ * knowable ahead of time because the components are nebula's, and they do not
13
+ * change between one app's install and another's.
14
+ *
15
+ * **The limitation, stated plainly.** The frozen stylesheet covers the
16
+ * components as nebula wrote them. The registry copies those sources into the
17
+ * app for the user to edit, and the moment an edit introduces a utility nebula
18
+ * never used — `bg-emerald-500`, `grid-cols-7` — nothing emits it, and the
19
+ * class silently does nothing.
20
+ *
21
+ * So this adapter suits an app that takes the components as they are and
22
+ * writes its own hand-rolled CSS around them. An app that intends to retune
23
+ * components through their class strings wants `tailwind` or `unocss`, where
24
+ * the scanner sees the edits.
25
+ *
26
+ * Lifting that limitation means compiling the app's copied sources too — a
27
+ * utility scanner and a CSS emitter over the finite surface nebula uses. That
28
+ * is a build-time, string-heavy, run-on-every-build job, which is the profile
29
+ * where moving it into `ream-cli` (Rust) pays; it is the one part of nebula
30
+ * where Rust would earn its place.
31
+ */
32
+ function stylesheet() {
33
+ return `/*
34
+ * nebula, engine-free.
35
+ *
36
+ * \`nebula.css\` is prebuilt and shipped with the package — no scanner runs in
37
+ * this project, so nothing here needs a build step.
38
+ *
39
+ * It covers nebula's components as published. A utility you add to a copied
40
+ * component will not be emitted; write the rule below by hand, or switch the
41
+ * adapter in \`config/nebula.ts\` to \`tailwind\` or \`unocss\`.
42
+ */
43
+ @import "@c9up/nebula/nebula.css";
44
+
45
+ /* Your own styles go here. */
46
+ `;
47
+ }
48
+ export const cssAdapter = {
49
+ name: "css",
50
+ summary: "No engine and no build step — nebula's prebuilt stylesheet, imported as-is.",
51
+ packages: [],
52
+ files(config) {
53
+ return [
54
+ { path: config.paths.css, contents: stylesheet(), skipIfExists: true },
55
+ ];
56
+ },
57
+ /**
58
+ * Nothing to compile.
59
+ *
60
+ * `null` rather than a copy command: the app serves the stylesheet straight
61
+ * from the package, so there is no compile step for `ream build` to run and
62
+ * none should be registered.
63
+ */
64
+ commands() {
65
+ return null;
66
+ },
67
+ };
@@ -0,0 +1,16 @@
1
+ /**
2
+ * The three style adapters.
3
+ *
4
+ * All three consume the same utility class names, which is what lets one set
5
+ * of components serve all of them. Switching is a one-word change in
6
+ * `config/nebula.ts` followed by `ream nebula:init`.
7
+ */
8
+ import type { AdapterName } from "../config.js";
9
+ import type { StyleAdapter } from "./types.js";
10
+ export { cssAdapter } from "./css.js";
11
+ export { tailwindAdapter } from "./tailwind.js";
12
+ export type { AdapterCommand, GeneratedFile, StyleAdapter, } from "./types.js";
13
+ export { unocssAdapter } from "./unocss.js";
14
+ export declare function adapterFor(name: AdapterName): StyleAdapter;
15
+ export declare function adapterNames(): readonly AdapterName[];
16
+ export declare function isAdapterName(value: string): value is AdapterName;
@@ -0,0 +1,27 @@
1
+ /**
2
+ * The three style adapters.
3
+ *
4
+ * All three consume the same utility class names, which is what lets one set
5
+ * of components serve all of them. Switching is a one-word change in
6
+ * `config/nebula.ts` followed by `ream nebula:init`.
7
+ */
8
+ import { cssAdapter } from "./css.js";
9
+ import { tailwindAdapter } from "./tailwind.js";
10
+ import { unocssAdapter } from "./unocss.js";
11
+ export { cssAdapter } from "./css.js";
12
+ export { tailwindAdapter } from "./tailwind.js";
13
+ export { unocssAdapter } from "./unocss.js";
14
+ const adapters = {
15
+ tailwind: tailwindAdapter,
16
+ unocss: unocssAdapter,
17
+ css: cssAdapter,
18
+ };
19
+ export function adapterFor(name) {
20
+ return adapters[name];
21
+ }
22
+ export function adapterNames() {
23
+ return Object.keys(adapters).filter(isAdapterName);
24
+ }
25
+ export function isAdapterName(value) {
26
+ return value === "tailwind" || value === "unocss" || value === "css";
27
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Tailwind CSS v4 adapter.
3
+ *
4
+ * The default, and the one shadcn itself targets — so the class strings in
5
+ * nebula's components are exactly the ones upstream uses, and a shadcn example
6
+ * pasted into a nebula project renders the same.
7
+ *
8
+ * v4 is configured in CSS rather than a JS config file, which is why this
9
+ * emits a stylesheet and no `tailwind.config.js`. Three directives do the
10
+ * work:
11
+ *
12
+ * - `@source` points the scanner at the copied components. Without it Tailwind
13
+ * never sees `bg-primary` and emits none of it — the single most common way
14
+ * a v4 setup produces an unstyled page.
15
+ * - `@custom-variant dark` makes `dark:` respond to a `.dark` class rather
16
+ * than the system preference, so a theme toggle can override the OS.
17
+ * - `@theme inline` maps nebula's plain custom properties onto Tailwind's
18
+ * colour namespace, which is what turns `--primary` into a `bg-primary`
19
+ * utility.
20
+ *
21
+ * The build runs through `@tailwindcss/cli`, matching the aurora apps already
22
+ * in this workspace.
23
+ */
24
+ import type { StyleAdapter } from "./types.js";
25
+ export declare const tailwindAdapter: StyleAdapter;
@@ -0,0 +1,122 @@
1
+ /**
2
+ * Tailwind CSS v4 adapter.
3
+ *
4
+ * The default, and the one shadcn itself targets — so the class strings in
5
+ * nebula's components are exactly the ones upstream uses, and a shadcn example
6
+ * pasted into a nebula project renders the same.
7
+ *
8
+ * v4 is configured in CSS rather than a JS config file, which is why this
9
+ * emits a stylesheet and no `tailwind.config.js`. Three directives do the
10
+ * work:
11
+ *
12
+ * - `@source` points the scanner at the copied components. Without it Tailwind
13
+ * never sees `bg-primary` and emits none of it — the single most common way
14
+ * a v4 setup produces an unstyled page.
15
+ * - `@custom-variant dark` makes `dark:` respond to a `.dark` class rather
16
+ * than the system preference, so a theme toggle can override the OS.
17
+ * - `@theme inline` maps nebula's plain custom properties onto Tailwind's
18
+ * colour namespace, which is what turns `--primary` into a `bg-primary`
19
+ * utility.
20
+ *
21
+ * The build runs through `@tailwindcss/cli`, matching the aurora apps already
22
+ * in this workspace.
23
+ */
24
+ function stylesheet(config) {
25
+ return `@import "tailwindcss";
26
+ @import "@c9up/nebula/theme.css";
27
+
28
+ /* Scan the component tree so the utilities they use are emitted. */
29
+ @source "${relativeFromCss(config)}";
30
+
31
+ /* Class-based dark mode: \`dark:\` applies under any \`.dark\` ancestor. */
32
+ @custom-variant dark (&:is(.dark *));
33
+
34
+ /*
35
+ * Map nebula's tokens onto Tailwind's namespaces.
36
+ *
37
+ * \`inline\` matters: without it Tailwind copies the *values* at build time and
38
+ * the \`.dark\` overrides never reach the utilities, so dark mode changes the
39
+ * custom properties and nothing else.
40
+ */
41
+ @theme inline {
42
+ --color-background: var(--background);
43
+ --color-foreground: var(--foreground);
44
+ --color-card: var(--card);
45
+ --color-card-foreground: var(--card-foreground);
46
+ --color-popover: var(--popover);
47
+ --color-popover-foreground: var(--popover-foreground);
48
+ --color-primary: var(--primary);
49
+ --color-primary-foreground: var(--primary-foreground);
50
+ --color-secondary: var(--secondary);
51
+ --color-secondary-foreground: var(--secondary-foreground);
52
+ --color-muted: var(--muted);
53
+ --color-muted-foreground: var(--muted-foreground);
54
+ --color-accent: var(--accent);
55
+ --color-accent-foreground: var(--accent-foreground);
56
+ --color-destructive: var(--destructive);
57
+ --color-destructive-foreground: var(--destructive-foreground);
58
+ --color-border: var(--border);
59
+ --color-input: var(--input);
60
+ --color-ring: var(--ring);
61
+ --color-chart-1: var(--chart-1);
62
+ --color-chart-2: var(--chart-2);
63
+ --color-chart-3: var(--chart-3);
64
+ --color-chart-4: var(--chart-4);
65
+ --color-chart-5: var(--chart-5);
66
+ --color-sidebar: var(--sidebar);
67
+ --color-sidebar-foreground: var(--sidebar-foreground);
68
+ --color-sidebar-primary: var(--sidebar-primary);
69
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
70
+ --color-sidebar-accent: var(--sidebar-accent);
71
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
72
+ --color-sidebar-border: var(--sidebar-border);
73
+ --color-sidebar-ring: var(--sidebar-ring);
74
+
75
+ --radius-sm: calc(var(--radius) - 4px);
76
+ --radius-md: calc(var(--radius) - 2px);
77
+ --radius-lg: var(--radius);
78
+ --radius-xl: calc(var(--radius) + 4px);
79
+ }
80
+ `;
81
+ }
82
+ /**
83
+ * `@source` is resolved relative to the stylesheet, not the project root.
84
+ *
85
+ * The two are rarely the same — `resources/css/app.css` scanning
86
+ * `resources/pages` needs `../pages` — and getting it wrong fails silently:
87
+ * Tailwind finds no files, emits no utilities, and the page renders unstyled
88
+ * with no error anywhere.
89
+ */
90
+ function relativeFromCss(config) {
91
+ const cssDirectory = config.paths.css.split("/").slice(0, -1);
92
+ const target = config.paths.components.split("/");
93
+ let shared = 0;
94
+ while (shared < cssDirectory.length &&
95
+ shared < target.length &&
96
+ cssDirectory[shared] === target[shared]) {
97
+ shared += 1;
98
+ }
99
+ const up = new Array(cssDirectory.length - shared).fill("..");
100
+ return [...up, ...target.slice(shared)].join("/");
101
+ }
102
+ export const tailwindAdapter = {
103
+ name: "tailwind",
104
+ summary: "Tailwind CSS v4, configured in CSS. What shadcn/ui itself targets.",
105
+ packages: ["tailwindcss", "@tailwindcss/cli"],
106
+ files(config) {
107
+ return [
108
+ {
109
+ path: config.paths.css,
110
+ contents: stylesheet(config),
111
+ skipIfExists: true,
112
+ },
113
+ ];
114
+ },
115
+ commands(config) {
116
+ const base = ["-i", config.paths.css, "-o", config.paths.output];
117
+ return {
118
+ build: { command: "tailwindcss", args: base },
119
+ dev: { command: "tailwindcss", args: [...base, "--watch"] },
120
+ };
121
+ },
122
+ };
@@ -0,0 +1,49 @@
1
+ /**
2
+ * The style-adapter contract.
3
+ *
4
+ * An adapter answers three questions and nothing else:
5
+ *
6
+ * 1. What does the app need to install? nebula never installs it — the app
7
+ * owns its dependencies, and a UI library quietly adding a build tool to
8
+ * someone's `package.json` is how a dependency tree gets away from them.
9
+ * 2. What files should exist? The stylesheet entry, and any engine config.
10
+ * 3. What command compiles them? Handed to ream's `assets` config, which
11
+ * `ream dev` and `ream build` already run — the same hook AdonisJS uses for
12
+ * its bundler.
13
+ *
14
+ * Deliberately not in the contract: anything that would change how a component
15
+ * is written. All three adapters consume the same utility class names, which
16
+ * is what keeps one set of fifty-odd components serving all of them. An engine
17
+ * with a different authoring model — Panda's recipes, StyleX's compile-time
18
+ * API — cannot be added behind this interface, and pretending otherwise would
19
+ * mean a second version of every component.
20
+ */
21
+ import type { ResolvedNebulaConfig } from "../config.js";
22
+ export interface GeneratedFile {
23
+ /** Path relative to the project root. */
24
+ path: string;
25
+ contents: string;
26
+ /**
27
+ * Leave an existing file alone. True for anything the user is expected to
28
+ * edit — their stylesheet is theirs once it exists.
29
+ */
30
+ skipIfExists: boolean;
31
+ }
32
+ export interface AdapterCommand {
33
+ command: string;
34
+ args: readonly string[];
35
+ }
36
+ export interface StyleAdapter {
37
+ readonly name: string;
38
+ /** One-line summary, printed by `nebula init`. */
39
+ readonly summary: string;
40
+ /** Packages the app must add itself. Empty for an engine-free adapter. */
41
+ readonly packages: readonly string[];
42
+ /** Stubs to write. */
43
+ files(config: ResolvedNebulaConfig): readonly GeneratedFile[];
44
+ /** Build and watch commands, or `null` when there is nothing to compile. */
45
+ commands(config: ResolvedNebulaConfig): {
46
+ build: AdapterCommand;
47
+ dev: AdapterCommand;
48
+ } | null;
49
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * The style-adapter contract.
3
+ *
4
+ * An adapter answers three questions and nothing else:
5
+ *
6
+ * 1. What does the app need to install? nebula never installs it — the app
7
+ * owns its dependencies, and a UI library quietly adding a build tool to
8
+ * someone's `package.json` is how a dependency tree gets away from them.
9
+ * 2. What files should exist? The stylesheet entry, and any engine config.
10
+ * 3. What command compiles them? Handed to ream's `assets` config, which
11
+ * `ream dev` and `ream build` already run — the same hook AdonisJS uses for
12
+ * its bundler.
13
+ *
14
+ * Deliberately not in the contract: anything that would change how a component
15
+ * is written. All three adapters consume the same utility class names, which
16
+ * is what keeps one set of fifty-odd components serving all of them. An engine
17
+ * with a different authoring model — Panda's recipes, StyleX's compile-time
18
+ * API — cannot be added behind this interface, and pretending otherwise would
19
+ * mean a second version of every component.
20
+ */
21
+ export {};
@@ -0,0 +1,17 @@
1
+ /**
2
+ * UnoCSS adapter.
3
+ *
4
+ * `presetWind4` implements Tailwind v4's utility syntax, so nebula's component
5
+ * sources are unchanged between this adapter and the Tailwind one — the whole
6
+ * reason both can be offered without a second set of components.
7
+ *
8
+ * What differs is the config format. UnoCSS is configured in JavaScript rather
9
+ * than CSS, so the theme mapping that `@theme inline` expresses for Tailwind
10
+ * is a `theme` object here, and the token definitions come in through
11
+ * `preflights` since UnoCSS has no `@import` of its own.
12
+ *
13
+ * The tokens still come from `theme.css`. One file remains the source of truth
14
+ * for the palette; only the plumbing around it changes.
15
+ */
16
+ import type { StyleAdapter } from "./types.js";
17
+ export declare const unocssAdapter: StyleAdapter;