@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
@@ -0,0 +1,127 @@
1
+ /**
2
+ * `ream configure @c9up/nebula` — the house setup hook.
3
+ *
4
+ * Every package in this workspace is installed the same way: `ream add <pkg>`
5
+ * installs it and dispatches to this hook, which writes the files the package
6
+ * needs through the codemods the CLI hands in. nebula follows that rather than
7
+ * shipping a setup command of its own — one way to install a ream package, not
8
+ * one per package.
9
+ *
10
+ * Pass the adapter through the installer:
11
+ *
12
+ * ream add @c9up/nebula --adapter unocss
13
+ *
14
+ * There is no provider to register. A provider binds runtime services into the
15
+ * container, and nebula has none: its components are client-side templates and
16
+ * its adapters run at build time. An empty provider would be ceremony.
17
+ */
18
+
19
+ import { adapterFor, isAdapterName } from "./adapters/index.js";
20
+ import { type AdapterName, resolveConfig } from "./config.js";
21
+
22
+ /**
23
+ * The subset of the CLI's codemods this hook uses.
24
+ *
25
+ * Declared structurally rather than imported: `@c9up/ream-cli` is a Rust
26
+ * binary that injects this object at runtime, so there is no type to import
27
+ * and a dependency on the CLI would be a lie. Same shape the other packages'
28
+ * hooks declare.
29
+ */
30
+ interface Codemods {
31
+ writeFile(
32
+ filePath: string,
33
+ content: string,
34
+ options?: { force?: boolean },
35
+ ): Promise<void>;
36
+ }
37
+
38
+ /** Flags forwarded from `ream add` / `ream configure`, as the CLI encodes them. */
39
+ type Flags = Record<string, string[] | undefined>;
40
+
41
+ function configFile(adapter: AdapterName): string {
42
+ return `import { defineConfig } from '@c9up/nebula'
43
+
44
+ /**
45
+ * nebula — component registry and style adapter.
46
+ *
47
+ * Changing \`adapter\` and re-running \`ream configure @c9up/nebula\` swaps the
48
+ * CSS engine. The components themselves are untouched: all three adapters
49
+ * consume the same class names.
50
+ */
51
+ export default defineConfig({
52
+ adapter: '${adapter}',
53
+ paths: {
54
+ // Where \`ream nebula:add\` copies components. The atomic layers live under it.
55
+ components: 'resources/pages',
56
+ css: 'resources/css/app.css',
57
+ output: 'public/app.css',
58
+ },
59
+ })
60
+ `;
61
+ }
62
+
63
+ /**
64
+ * Read the adapter from the forwarded flags.
65
+ *
66
+ * An unrecognised name is refused rather than silently defaulted: a typo in
67
+ * `--adapter tailwnid` would otherwise write a Tailwind setup and leave the
68
+ * user hunting for why their UnoCSS config never appeared.
69
+ */
70
+ function adapterFrom(flags: Flags): AdapterName {
71
+ const requested = flags.adapter?.[0];
72
+ if (requested === undefined) return "tailwind";
73
+ if (!isAdapterName(requested)) {
74
+ throw new Error(
75
+ `[@c9up/nebula] unknown adapter "${requested}" — one of tailwind, unocss, css`,
76
+ );
77
+ }
78
+ return requested;
79
+ }
80
+
81
+ export async function configure(
82
+ codemods: Codemods,
83
+ flags: Flags = {},
84
+ ): Promise<void> {
85
+ const name = adapterFrom(flags);
86
+ const adapter = adapterFor(name);
87
+ const config = resolveConfig({ adapter: name });
88
+
89
+ await codemods.writeFile("config/nebula.ts", configFile(name));
90
+ for (const file of adapter.files(config)) {
91
+ await codemods.writeFile(file.path, file.contents);
92
+ }
93
+
94
+ // On stderr, and never installed on the user's behalf. nebula declares no
95
+ // CSS dependency at all — the app owns its build tooling, and a UI library
96
+ // quietly adding one to someone's dependency tree is how a tree gets away
97
+ // from them.
98
+ const lines = [
99
+ "",
100
+ "[@c9up/nebula] configured with the " + name + " adapter.",
101
+ ];
102
+
103
+ if (adapter.packages.length > 0) {
104
+ lines.push(
105
+ " Install the engine yourself:",
106
+ ` pnpm add -D ${adapter.packages.join(" ")}`,
107
+ );
108
+ }
109
+
110
+ const commands = adapter.commands(config);
111
+ if (commands === null) {
112
+ lines.push(" No build step — the stylesheet ships prebuilt.");
113
+ } else {
114
+ lines.push(
115
+ " Register the build in config/assets.ts:",
116
+ ` build: ${commands.build.command} ${commands.build.args.join(" ")}`,
117
+ ` devServer: ${commands.dev.command} ${commands.dev.args.join(" ")}`,
118
+ );
119
+ }
120
+
121
+ lines.push(
122
+ " Then add components with `ream nebula:add button card` — they are copied",
123
+ " into your project and are yours to edit.",
124
+ "",
125
+ );
126
+ process.stderr.write(lines.join("\n"));
127
+ }
package/src/index.ts ADDED
@@ -0,0 +1,53 @@
1
+ /**
2
+ * @c9up/nebula — shadcn/ui, ported to Aurora, organised as atomic design.
3
+ *
4
+ * Three things are worth knowing before reading further.
5
+ *
6
+ * **It adds no runtime dependency.** shadcn stands on Radix, `clsx`,
7
+ * `tailwind-merge`, `class-variance-authority`, `lucide-react`,
8
+ * `@floating-ui/dom`, `cmdk`, `sonner`, `recharts`, `@tanstack/react-table` and
9
+ * `react-day-picker`. None of them are React-agnostic, and the workspace had
10
+ * already made the call that `cn` was worth writing by hand rather than
11
+ * installing two packages. So `cva` is reimplemented, the Radix behaviour layer
12
+ * lives in `primitives/`, the placement engine is `primitives/floating.ts`, the
13
+ * icons are inlined, and the date maths is `Date` and `Intl`.
14
+ *
15
+ * **Compound components take data, not children.** shadcn composes through
16
+ * React context — `<Tabs><TabsList><TabsTrigger>`. Aurora has no context, and
17
+ * the workarounds are worse than the problem, so those components take an
18
+ * `items` array and named slots. The rendered markup is unchanged, which means
19
+ * shadcn's CSS and its examples still read across.
20
+ *
21
+ * **The atomic layer is a property of the component.** An atom renders its own
22
+ * markup and composes nothing from nebula; a molecule assembles atoms or owns
23
+ * state spanning several elements; an organism portals, traps focus, or floats;
24
+ * a template is page layout. `nebula add button` knows Button is an atom and
25
+ * puts it in `atoms/` without being told.
26
+ *
27
+ * Import from the layer barrels — `@c9up/nebula/atoms` — or take the source
28
+ * with `nebula add`, which is the mode this library is really built for.
29
+ */
30
+
31
+ export * from "./adapters/index.js";
32
+ export * from "./atoms/index.js";
33
+ export {
34
+ type AdapterName,
35
+ defaultPaths,
36
+ defineConfig,
37
+ isLayer,
38
+ type Layer,
39
+ layers,
40
+ type NebulaConfig,
41
+ type NebulaPaths,
42
+ type ResolvedNebulaConfig,
43
+ resolveConfig,
44
+ } from "./config.js";
45
+ export { type Child, type Slot, slot } from "./lib/children.js";
46
+ export * from "./lib/icons.js";
47
+ export * from "./lib/index.js";
48
+ export { fadeInOut, slideFrom, zoomInOut } from "./lib/motion.js";
49
+ export { type StyledProps, styledDiv, styledSpan } from "./lib/styled.js";
50
+ export * from "./molecules/index.js";
51
+ export * from "./organisms/index.js";
52
+ export * from "./primitives/index.js";
53
+ export * from "./templates/index.js";
@@ -0,0 +1,43 @@
1
+ /**
2
+ * What a component accepts as content.
3
+ *
4
+ * Aurora's renderer already handles the whole union in a text slot: nested
5
+ * templates, raw nodes, primitives, and arrays of any of those, with `null`,
6
+ * `undefined` and `false` rendering nothing. `Child` names that union so a
7
+ * component's props say what they mean instead of falling back to `unknown`,
8
+ * and so passing a value the renderer would silently drop is a type error at
9
+ * the call site rather than a blank space on the page.
10
+ *
11
+ * `Slot` is the reactive form. Content that changes must arrive as an accessor
12
+ * — Aurora never re-renders a component, so a plain value read at setup is
13
+ * frozen for the lifetime of the node.
14
+ */
15
+
16
+ import type { TemplateResult } from "@c9up/aurora";
17
+ import type { Reactive } from "./props.js";
18
+
19
+ export type Child =
20
+ | TemplateResult
21
+ | Node
22
+ | string
23
+ | number
24
+ | boolean
25
+ | null
26
+ | undefined
27
+ | readonly Child[];
28
+
29
+ /** Content that may be constant or recomputed. */
30
+ export type Slot = Reactive<Child>;
31
+
32
+ /**
33
+ * Bind content into a template so it stays live.
34
+ *
35
+ * Returns the accessor form unchanged and wraps a constant in one. Templates
36
+ * should always interpolate `${slot(props.children)}`: handing the renderer a
37
+ * bare value works until a sibling prop makes the content dynamic, at which
38
+ * point the binding quietly stops updating and nothing points at why.
39
+ */
40
+ export function slot(content: Slot | undefined): () => Child {
41
+ if (content === undefined) return () => null;
42
+ return typeof content === "function" ? content : () => content;
43
+ }
package/src/lib/cn.ts ADDED
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Class composition — re-exported from Aurora rather than reimplemented here.
3
+ *
4
+ * `@c9up/aurora` already ships `clsx` + `tailwind-merge` written from scratch
5
+ * with no dependencies (its `src/cn.ts`, targeting the Tailwind v4 utility
6
+ * set). nebula is a peer of aurora, never a fork of it: one class-merge
7
+ * implementation in the workspace, one place to fix a mis-grouped utility.
8
+ *
9
+ * This module exists so every nebula component imports `./lib/cn.js` and not
10
+ * `@c9up/aurora` directly. That indirection is what lets the registry rewrite
11
+ * a single import line when it copies a component into a host app.
12
+ */
13
+
14
+ export { type ClassValue, clsx, cn, twMerge } from "@c9up/aurora";
package/src/lib/cva.ts ADDED
@@ -0,0 +1,249 @@
1
+ /**
2
+ * `cva` — class-variance-authority, reimplemented from scratch with ZERO
3
+ * dependencies.
4
+ *
5
+ * shadcn/ui is built on two class helpers: `cn` (clsx + tailwind-merge) and
6
+ * `cva` (variant → class-list resolution). Aurora already ships `cn` written
7
+ * from scratch in `@c9up/aurora` (src/cn.ts), so this file is the other half.
8
+ * Between them, nebula pulls in no runtime dependency at all.
9
+ *
10
+ * const button = cva(
11
+ * "inline-flex items-center rounded-md text-sm font-medium",
12
+ * {
13
+ * variants: {
14
+ * variant: { default: "bg-primary text-primary-foreground",
15
+ * ghost: "hover:bg-accent" },
16
+ * size: { sm: "h-8 px-3", lg: "h-10 px-8" },
17
+ * },
18
+ * compoundVariants: [
19
+ * { variant: "ghost", size: "sm", class: "px-2" },
20
+ * ],
21
+ * defaultVariants: { variant: "default", size: "sm" },
22
+ * },
23
+ * )
24
+ *
25
+ * button() // base + default variants
26
+ * button({ variant: "ghost" }) // ghost, still size sm
27
+ * button({ size: "lg", class: "w-full" }) // caller classes win
28
+ *
29
+ * Differences from the upstream package, both deliberate:
30
+ *
31
+ * 1. The result runs through Aurora's `cn`, so a variant that sets `px-2`
32
+ * genuinely overrides a base `px-3` instead of emitting both and leaving
33
+ * the winner to CSS source order. Upstream cva only concatenates; every
34
+ * real shadcn component then wraps the call in `cn(...)` to get here
35
+ * anyway. Doing it inside removes that ceremony from ~50 components.
36
+ * 2. `class` and `className` are both accepted. Aurora templates use `class`;
37
+ * `className` exists only so code pasted over from a React codebase keeps
38
+ * working.
39
+ */
40
+
41
+ import { type ClassValue, cn } from "./cn.js";
42
+
43
+ /**
44
+ * One variant axis: a variant name mapped to the classes each of its values
45
+ * contributes. `{ size: { sm: "h-8", lg: "h-10" } }` is one axis with two
46
+ * values.
47
+ */
48
+ export type VariantShape = Record<string, Record<string, ClassValue>>;
49
+
50
+ /**
51
+ * The value accepted for one axis.
52
+ *
53
+ * An axis keyed by `"true"` / `"false"` is a boolean axis — the caller passes
54
+ * a real `boolean`, not the string. That mapping is what lets
55
+ * `{ disabled: { true: "opacity-50" } }` be driven by `disabled: true`.
56
+ */
57
+ type VariantValue<T> =
58
+ Extract<keyof T, string> extends "true" | "false"
59
+ ? boolean
60
+ : Extract<keyof T, string>;
61
+
62
+ /** Props selecting one value per axis. Every axis is optional. */
63
+ export type VariantSelection<V extends VariantShape> = {
64
+ [K in keyof V]?: VariantValue<V[K]> | null | undefined;
65
+ };
66
+
67
+ /**
68
+ * A compound rule matches when EVERY axis it names matches. An axis may list
69
+ * several accepted values, in which case any of them matches.
70
+ */
71
+ export type CompoundVariant<V extends VariantShape> = {
72
+ [K in keyof V]?: VariantValue<V[K]> | ReadonlyArray<VariantValue<V[K]>>;
73
+ } & ClassOverrides;
74
+
75
+ /** Extra keys every generated function accepts on top of its own axes. */
76
+ export interface ClassOverrides {
77
+ class?: ClassValue;
78
+ className?: ClassValue;
79
+ }
80
+
81
+ export interface VariantConfig<V extends VariantShape> {
82
+ variants?: V;
83
+ compoundVariants?: ReadonlyArray<CompoundVariant<V>>;
84
+ defaultVariants?: VariantSelection<V>;
85
+ }
86
+
87
+ /** The props a `cva()` result accepts, minus the two class escape hatches. */
88
+ export type VariantProps<F> = F extends (props?: infer P) => string
89
+ ? Omit<NonNullable<P>, "class" | "className">
90
+ : never;
91
+
92
+ /** A compound rule flattened into "conditions + classes it contributes". */
93
+ interface CompoundRule {
94
+ /** Each entry is one axis and the set of values that satisfy it. */
95
+ readonly conditions: ReadonlyArray<readonly [string, readonly string[]]>;
96
+ readonly classes: readonly ClassValue[];
97
+ }
98
+
99
+ /**
100
+ * Build a variant resolver. Returns a function from a variant selection to a
101
+ * single, conflict-resolved class string.
102
+ */
103
+ export function cva<V extends VariantShape>(
104
+ base?: ClassValue,
105
+ config?: VariantConfig<V>,
106
+ ): (props?: VariantSelection<V> & ClassOverrides) => string {
107
+ // Widening to the non-generic shapes up front is what keeps the body free
108
+ // of casts: `V` cannot be indexed by a plain `string`, `VariantShape` can.
109
+ const variants: VariantShape = config?.variants ?? {};
110
+ const axes = Object.keys(variants);
111
+ const defaults = toValueMap(config?.defaultVariants);
112
+ const compounds = normaliseCompounds(config?.compoundVariants);
113
+
114
+ return (props?: VariantSelection<V> & ClassOverrides): string => {
115
+ const selection = selectionFor(defaults, props);
116
+ const parts: ClassValue[] = [base];
117
+
118
+ for (const axis of axes) {
119
+ const chosen = selection[axis];
120
+ if (chosen === undefined) continue;
121
+ // Indexed by the *string form* of the value, so a boolean axis reaches
122
+ // its `"true"` / `"false"` entry here.
123
+ const classesForValue = variants[axis]?.[chosen];
124
+ if (classesForValue !== undefined) parts.push(classesForValue);
125
+ }
126
+
127
+ for (const rule of compounds) {
128
+ if (satisfies(rule, selection)) parts.push(...rule.classes);
129
+ }
130
+
131
+ // Caller overrides come last so they win the tailwind-merge pass.
132
+ parts.push(props?.class, props?.className);
133
+ return cn(parts);
134
+ };
135
+ }
136
+
137
+ /**
138
+ * Read an object's own enumerable entries with honest `unknown` values.
139
+ *
140
+ * Variant selections are generic mapped types, which TypeScript will not let
141
+ * you index with a plain `string`. Funnelling every read through one helper
142
+ * typed `object → [string, unknown][]` gets the values out without a cast at
143
+ * any call site, and forces each one to narrow before use.
144
+ */
145
+ function entriesOf(source: object): ReadonlyArray<readonly [string, unknown]> {
146
+ return Object.entries(source);
147
+ }
148
+
149
+ /**
150
+ * Collapse a selection object down to `axis → string value`.
151
+ *
152
+ * Normalising to strings here is what keeps boolean-axis handling in a single
153
+ * place: `true` becomes `"true"`, and every lookup downstream — variant
154
+ * classes and compound matching alike — is a plain string compare.
155
+ */
156
+ function toValueMap(source: object | undefined): Record<string, string> {
157
+ const out: Record<string, string> = {};
158
+ if (!source) return out;
159
+ for (const [key, value] of entriesOf(source)) {
160
+ if (isClassKey(key)) continue;
161
+ if (value === undefined || value === null) continue;
162
+ out[key] = String(value);
163
+ }
164
+ return out;
165
+ }
166
+
167
+ /**
168
+ * Merge caller props over the defaults.
169
+ *
170
+ * An omitted axis (or an explicit `undefined`) keeps its default; an explicit
171
+ * `null` clears it, which is how upstream cva lets a caller opt out of a
172
+ * default variant entirely.
173
+ */
174
+ function selectionFor(
175
+ defaults: Record<string, string>,
176
+ props: object | undefined,
177
+ ): Record<string, string | undefined> {
178
+ const selection: Record<string, string | undefined> = { ...defaults };
179
+ if (!props) return selection;
180
+
181
+ for (const [key, value] of entriesOf(props)) {
182
+ if (isClassKey(key)) continue;
183
+ if (value === undefined) continue;
184
+ selection[key] = value === null ? undefined : String(value);
185
+ }
186
+ return selection;
187
+ }
188
+
189
+ /** Flatten compound rules once, at construction, rather than on every call. */
190
+ function normaliseCompounds<V extends VariantShape>(
191
+ compounds: ReadonlyArray<CompoundVariant<V>> | undefined,
192
+ ): readonly CompoundRule[] {
193
+ if (!compounds) return [];
194
+
195
+ const out: CompoundRule[] = [];
196
+ for (const rule of compounds) {
197
+ const conditions: Array<readonly [string, readonly string[]]> = [];
198
+ const classes: ClassValue[] = [];
199
+
200
+ for (const [key, value] of entriesOf(rule)) {
201
+ if (isClassKey(key)) {
202
+ if (isClassValue(value)) classes.push(value);
203
+ continue;
204
+ }
205
+ if (value === undefined || value === null) continue;
206
+ const accepted = Array.isArray(value)
207
+ ? value.map((one) => String(one))
208
+ : [String(value)];
209
+ conditions.push([key, accepted]);
210
+ }
211
+ out.push({ conditions, classes });
212
+ }
213
+ return out;
214
+ }
215
+
216
+ /** Does every condition of a compound rule hold for this selection? */
217
+ function satisfies(
218
+ rule: CompoundRule,
219
+ selection: Record<string, string | undefined>,
220
+ ): boolean {
221
+ for (const [axis, accepted] of rule.conditions) {
222
+ const actual = selection[axis];
223
+ if (actual === undefined) return false;
224
+ if (!accepted.includes(actual)) return false;
225
+ }
226
+ return true;
227
+ }
228
+
229
+ function isClassKey(key: string): boolean {
230
+ return key === "class" || key === "className";
231
+ }
232
+
233
+ /**
234
+ * Narrow a compound rule's `class` / `className` back to `ClassValue`.
235
+ *
236
+ * The value arrives as `unknown` from `entriesOf`; `ClassValue` covers every
237
+ * shape `cn` accepts, so anything that is not a rejected primitive passes
238
+ * through untouched and `cn` does the rest of the work.
239
+ */
240
+ function isClassValue(value: unknown): value is ClassValue {
241
+ return (
242
+ typeof value === "string" ||
243
+ typeof value === "number" ||
244
+ typeof value === "boolean" ||
245
+ value === null ||
246
+ value === undefined ||
247
+ typeof value === "object"
248
+ );
249
+ }
@@ -0,0 +1,138 @@
1
+ /**
2
+ * Inline icons.
3
+ *
4
+ * shadcn/ui pulls every glyph from `lucide-react`. nebula cannot: it is a
5
+ * React package, and taking a dependency for fifteen paths would undo the
6
+ * point of writing `cn` and `cva` by hand.
7
+ *
8
+ * So the glyphs the component set actually needs are inlined here, traced from
9
+ * Lucide's own 24×24 grid so they sit correctly next to Lucide icons in an app
10
+ * that also uses the real thing. Nothing else is added — this is not an icon
11
+ * library, and an app wanting a hundred more should install one and pass the
12
+ * markup in as content.
13
+ *
14
+ * `currentColor` throughout and no hardcoded size: an icon inherits the text
15
+ * colour of whatever it sits in, and the `[&_svg]:size-4` rules on Button and
16
+ * friends set the size from the component. That is what lets the same icon
17
+ * work in a button, a menu item and a badge without a variant each.
18
+ */
19
+
20
+ import { html, type TemplateResult } from "@c9up/aurora";
21
+
22
+ export interface IconProps {
23
+ class?: string;
24
+ /** Icons are decorative by default; give a label to expose one to a reader. */
25
+ label?: string;
26
+ }
27
+
28
+ /**
29
+ * Wrap path data in a Lucide-shaped `<svg>`.
30
+ *
31
+ * `aria-hidden` unless labelled, because an icon beside its own text label is
32
+ * noise to a screen reader — it reads the glyph's name and then the text. The
33
+ * exceptions are icon-only controls, which pass a label.
34
+ */
35
+ function svg(body: TemplateResult, props: IconProps = {}): TemplateResult {
36
+ return html`<svg
37
+ xmlns="http://www.w3.org/2000/svg"
38
+ viewBox="0 0 24 24"
39
+ fill="none"
40
+ stroke="currentColor"
41
+ stroke-width="2"
42
+ stroke-linecap="round"
43
+ stroke-linejoin="round"
44
+ class="${props.class}"
45
+ aria-hidden="${props.label === undefined ? "true" : undefined}"
46
+ aria-label="${props.label}"
47
+ role="${props.label === undefined ? undefined : "img"}"
48
+ >${body}</svg>`;
49
+ }
50
+
51
+ export const CheckIcon = (props?: IconProps): TemplateResult =>
52
+ svg(html`<path d="M20 6 9 17l-5-5" />`, props);
53
+
54
+ export const MinusIcon = (props?: IconProps): TemplateResult =>
55
+ svg(html`<path d="M5 12h14" />`, props);
56
+
57
+ export const XIcon = (props?: IconProps): TemplateResult =>
58
+ svg(html`<path d="M18 6 6 18" /><path d="m6 6 12 12" />`, props);
59
+
60
+ export const ChevronDownIcon = (props?: IconProps): TemplateResult =>
61
+ svg(html`<path d="m6 9 6 6 6-6" />`, props);
62
+
63
+ export const ChevronUpIcon = (props?: IconProps): TemplateResult =>
64
+ svg(html`<path d="m18 15-6-6-6 6" />`, props);
65
+
66
+ export const ChevronLeftIcon = (props?: IconProps): TemplateResult =>
67
+ svg(html`<path d="m15 18-6-6 6-6" />`, props);
68
+
69
+ export const ChevronRightIcon = (props?: IconProps): TemplateResult =>
70
+ svg(html`<path d="m9 18 6-6-6-6" />`, props);
71
+
72
+ export const ChevronsUpDownIcon = (props?: IconProps): TemplateResult =>
73
+ svg(html`<path d="m7 15 5 5 5-5" /><path d="m7 9 5-5 5 5" />`, props);
74
+
75
+ export const SearchIcon = (props?: IconProps): TemplateResult =>
76
+ svg(html`<circle cx="11" cy="11" r="8" /><path d="m21 21-4.3-4.3" />`, props);
77
+
78
+ export const CircleIcon = (props?: IconProps): TemplateResult =>
79
+ svg(html`<circle cx="12" cy="12" r="10" />`, props);
80
+
81
+ /** Filled dot — the selected marker inside a radio. */
82
+ export const DotIcon = (props?: IconProps): TemplateResult =>
83
+ svg(
84
+ html`<circle cx="12" cy="12" r="4" fill="currentColor" stroke="none" />`,
85
+ props,
86
+ );
87
+
88
+ export const MoreHorizontalIcon = (props?: IconProps): TemplateResult =>
89
+ svg(
90
+ html`<circle cx="12" cy="12" r="1" /><circle cx="19" cy="12" r="1" /><circle
91
+ cx="5"
92
+ cy="12"
93
+ r="1"
94
+ />`,
95
+ props,
96
+ );
97
+
98
+ export const ArrowLeftIcon = (props?: IconProps): TemplateResult =>
99
+ svg(html`<path d="m12 19-7-7 7-7" /><path d="M19 12H5" />`, props);
100
+
101
+ export const ArrowRightIcon = (props?: IconProps): TemplateResult =>
102
+ svg(html`<path d="M5 12h14" /><path d="m12 5 7 7-7 7" />`, props);
103
+
104
+ export const CalendarIcon = (props?: IconProps): TemplateResult =>
105
+ svg(
106
+ html`<path d="M8 2v4" /><path d="M16 2v4" /><rect
107
+ width="18"
108
+ height="18"
109
+ x="3"
110
+ y="4"
111
+ rx="2"
112
+ /><path d="M3 10h18" />`,
113
+ props,
114
+ );
115
+
116
+ export const PanelLeftIcon = (props?: IconProps): TemplateResult =>
117
+ svg(
118
+ html`<rect width="18" height="18" x="3" y="3" rx="2" /><path d="M9 3v18" />`,
119
+ props,
120
+ );
121
+
122
+ export const GripVerticalIcon = (props?: IconProps): TemplateResult =>
123
+ svg(
124
+ html`<circle cx="9" cy="12" r="1" /><circle cx="9" cy="5" r="1" /><circle
125
+ cx="9"
126
+ cy="19"
127
+ r="1"
128
+ /><circle cx="15" cy="12" r="1" /><circle cx="15" cy="5" r="1" /><circle
129
+ cx="15"
130
+ cy="19"
131
+ r="1"
132
+ />`,
133
+ props,
134
+ );
135
+
136
+ /** The arc Lucide uses for `loader-circle`; spun by the Spinner's animation. */
137
+ export const LoaderIcon = (props?: IconProps): TemplateResult =>
138
+ svg(html`<path d="M21 12a9 9 0 1 1-6.219-8.56" />`, props);
package/src/lib/id.ts ADDED
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Stable element ids.
3
+ *
4
+ * Radix leans on React's `useId` to wire ARIA relationships — a trigger's
5
+ * `aria-controls` has to name the id its content actually carries. Aurora has
6
+ * no equivalent, and nebula needs one for two reasons: the ARIA wiring above,
7
+ * and element lookup. Aurora templates have no `ref` directive, so a component
8
+ * that must measure or focus a node finds it by id inside `onMount()`.
9
+ *
10
+ * The counter is monotonic per module instance, so the sequence depends only
11
+ * on the order components are constructed in. That order is identical on the
12
+ * server and in the browser for the same tree, which is what makes the ids
13
+ * survive hydration — the same property React's `useId` relies on.
14
+ *
15
+ * `resetIds()` exists for the server: a long-lived process renders many
16
+ * requests, and without a reset the counter climbs forever and every response
17
+ * ships different markup, defeating any HTML cache. Call it once per render
18
+ * pass, before the tree is built.
19
+ */
20
+
21
+ let counter = 0;
22
+
23
+ /** Mint an id unique within this render pass. */
24
+ export function uid(prefix = "nebula"): string {
25
+ counter += 1;
26
+ return `${prefix}-${counter}`;
27
+ }
28
+
29
+ /** Restart the sequence. Call once per SSR render, before building the tree. */
30
+ export function resetIds(): void {
31
+ counter = 0;
32
+ }
33
+
34
+ /**
35
+ * Look an element up by the id a component minted for it.
36
+ *
37
+ * Returns `null` off the DOM (SSR) or before mount rather than throwing, so
38
+ * callers can run the same code in both environments. Every consumer inside
39
+ * nebula is in `onMount`, where the node is live.
40
+ */
41
+ export function byId(id: string): HTMLElement | null {
42
+ if (typeof document === "undefined") return null;
43
+ return document.getElementById(id);
44
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * The two class helpers shadcn is built on, plus the prop plumbing Aurora
3
+ * needs in their place.
4
+ *
5
+ * `cn` comes from aurora (clsx + tailwind-merge, already written from scratch
6
+ * there); `cva` is reimplemented here. Between them nebula adds no runtime
7
+ * dependency at all — the same call the workspace made for `cn` itself.
8
+ */
9
+
10
+ export { type ClassValue, clsx, cn, twMerge } from "./cn.js";
11
+ export {
12
+ type ClassOverrides,
13
+ type CompoundVariant,
14
+ cva,
15
+ type VariantConfig,
16
+ type VariantProps,
17
+ type VariantSelection,
18
+ type VariantShape,
19
+ } from "./cva.js";
20
+ export { byId, resetIds, uid } from "./id.js";
21
+ export { accessor, callHandler, type Reactive, read, readOr } from "./props.js";