@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,26 @@
1
+ /**
2
+ * Label — the caption for a form control.
3
+ *
4
+ * Radix's Label exists mostly to stop a click on the label from selecting its
5
+ * text on double-click, and to forward clicks to the control. The browser
6
+ * already does the forwarding through `for`, so nebula keeps the native
7
+ * element and only adds `select-none`.
8
+ *
9
+ * The disabled styling is driven from outside: `peer-disabled:` picks up a
10
+ * sibling control marked disabled, `group-data-[disabled=true]:` picks up a
11
+ * wrapping `Field`. Both are why the label dims without being told anything
12
+ * about the control it captions.
13
+ */
14
+ import { component, html } from "@c9up/aurora";
15
+ import { slot } from "../lib/children.js";
16
+ import { cn } from "../lib/cn.js";
17
+ import { read } from "../lib/props.js";
18
+ export const labelClasses = "flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50";
19
+ export const Label = component((props) => {
20
+ return html `<label
21
+ data-slot="label"
22
+ id="${props.id}"
23
+ for="${props.for}"
24
+ class="${() => cn(labelClasses, read(props.class))}"
25
+ >${slot(props.children)}</label>`;
26
+ });
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Marker — an inline annotation in a stream of content.
3
+ *
4
+ * "Yesterday", "3 unread", "Claude joined the conversation", "context
5
+ * truncated". A line that is *about* the conversation rather than part of it.
6
+ *
7
+ * `role="separator"` when it carries no text, `role="status"` when it does.
8
+ * The distinction matters: a bare rule between two days is structure and
9
+ * should be announced as a boundary, while "context truncated" is information
10
+ * the reader needs and would be lost as a separator.
11
+ *
12
+ * The rules on either side are `<span>`s with a border, not a `<hr>` — a
13
+ * horizontal rule cannot sit either side of a label without absolute
14
+ * positioning and a background colour matched to whatever is behind it, which
15
+ * breaks the moment the surface changes.
16
+ */
17
+ import type { Child } from "../lib/children.js";
18
+ import { type VariantProps } from "../lib/cva.js";
19
+ import { type Reactive } from "../lib/props.js";
20
+ export declare const markerVariants: (props?: (import("../index.js").VariantSelection<{
21
+ variant: {
22
+ default: string;
23
+ muted: string;
24
+ destructive: string;
25
+ };
26
+ align: {
27
+ center: string;
28
+ start: string;
29
+ };
30
+ }> & import("../index.js").ClassOverrides) | undefined) => string;
31
+ export type MarkerVariants = VariantProps<typeof markerVariants>;
32
+ export interface MarkerProps {
33
+ /** The annotation. Omit for a plain rule. */
34
+ label?: Child;
35
+ icon?: Child;
36
+ variant?: Reactive<MarkerVariants["variant"]>;
37
+ align?: Reactive<MarkerVariants["align"]>;
38
+ /** Draw rules either side of the label. Default `true`. */
39
+ rules?: boolean;
40
+ class?: Reactive<string>;
41
+ }
42
+ export declare const Marker: (props?: MarkerProps | undefined) => import("@c9up/aurora").TemplateResult;
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Marker — an inline annotation in a stream of content.
3
+ *
4
+ * "Yesterday", "3 unread", "Claude joined the conversation", "context
5
+ * truncated". A line that is *about* the conversation rather than part of it.
6
+ *
7
+ * `role="separator"` when it carries no text, `role="status"` when it does.
8
+ * The distinction matters: a bare rule between two days is structure and
9
+ * should be announced as a boundary, while "context truncated" is information
10
+ * the reader needs and would be lost as a separator.
11
+ *
12
+ * The rules on either side are `<span>`s with a border, not a `<hr>` — a
13
+ * horizontal rule cannot sit either side of a label without absolute
14
+ * positioning and a background colour matched to whatever is behind it, which
15
+ * breaks the moment the surface changes.
16
+ */
17
+ import { component, html } from "@c9up/aurora";
18
+ import { cva } from "../lib/cva.js";
19
+ import { read } from "../lib/props.js";
20
+ export const markerVariants = cva("flex w-full items-center gap-2 text-xs [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-3.5 [&_svg]:shrink-0", {
21
+ variants: {
22
+ variant: {
23
+ default: "text-muted-foreground",
24
+ muted: "text-muted-foreground/70",
25
+ destructive: "text-destructive",
26
+ },
27
+ align: {
28
+ center: "justify-center",
29
+ start: "justify-start",
30
+ },
31
+ },
32
+ defaultVariants: { variant: "default", align: "center" },
33
+ });
34
+ export const Marker = component((props) => {
35
+ const hasLabel = props.label !== undefined;
36
+ const withRules = props.rules !== false;
37
+ const rule = html `<span aria-hidden="true" class="bg-border h-px flex-1"></span>`;
38
+ return html `<div
39
+ data-slot="marker"
40
+ role="${hasLabel ? "status" : "separator"}"
41
+ class="${() => markerVariants({
42
+ variant: read(props.variant),
43
+ align: read(props.align),
44
+ class: read(props.class),
45
+ })}"
46
+ >
47
+ ${withRules ? rule : null}
48
+ ${hasLabel
49
+ ? html `<span class="flex shrink-0 items-center gap-1.5 whitespace-nowrap"
50
+ >${props.icon}${props.label}</span
51
+ >`
52
+ : null}
53
+ ${withRules ? rule : null}
54
+ </div>`;
55
+ });
@@ -0,0 +1,43 @@
1
+ /**
2
+ * NativeSelect — a real `<select>`, styled.
3
+ *
4
+ * The counterpart to `Select`, and the one to reach for first. `Select` builds
5
+ * a listbox out of divs because the operating system draws a native popup that
6
+ * cannot be styled at all — options take no markup, no colours, no checkmarks.
7
+ * That freedom costs everything the native control does for free, and `Select`
8
+ * has to put it all back by hand: roles, `aria-activedescendant`, type-ahead,
9
+ * arrows, a hidden input.
10
+ *
11
+ * When the options are plain text, none of that is worth paying for. A native
12
+ * select gives you the platform's own picker — the wheel on iOS, the
13
+ * search-as-you-type list on desktop — form participation, and zero JavaScript.
14
+ * `appearance-none` plus a background chevron gets it to match the design
15
+ * system everywhere except the open popup itself.
16
+ *
17
+ * Use `Select` when an option needs an icon, a description, or a swatch. Use
18
+ * this one otherwise.
19
+ */
20
+ import { type Reactive } from "../lib/props.js";
21
+ export interface NativeSelectOption {
22
+ value: string;
23
+ label: string;
24
+ disabled?: boolean;
25
+ /** Groups consecutive options under an `<optgroup>`. */
26
+ group?: string;
27
+ }
28
+ export declare const nativeSelectClasses = "border-input dark:bg-input/30 flex h-9 w-full min-w-0 appearance-none rounded-md border bg-transparent py-1 pr-8 pl-3 text-base shadow-xs transition-[color,box-shadow] outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm aria-invalid:border-destructive aria-invalid:ring-destructive/20";
29
+ export interface NativeSelectProps {
30
+ options: readonly NativeSelectOption[];
31
+ id?: string;
32
+ name?: string;
33
+ value?: Reactive<string | undefined>;
34
+ /** Shown as a disabled first entry while nothing is chosen. */
35
+ placeholder?: string;
36
+ disabled?: Reactive<boolean>;
37
+ required?: Reactive<boolean>;
38
+ invalid?: Reactive<boolean>;
39
+ describedBy?: Reactive<string | undefined>;
40
+ class?: Reactive<string>;
41
+ onValueChange?: (value: string, event: Event) => void;
42
+ }
43
+ export declare const NativeSelect: (props?: NativeSelectProps | undefined) => import("@c9up/aurora").TemplateResult;
@@ -0,0 +1,81 @@
1
+ /**
2
+ * NativeSelect — a real `<select>`, styled.
3
+ *
4
+ * The counterpart to `Select`, and the one to reach for first. `Select` builds
5
+ * a listbox out of divs because the operating system draws a native popup that
6
+ * cannot be styled at all — options take no markup, no colours, no checkmarks.
7
+ * That freedom costs everything the native control does for free, and `Select`
8
+ * has to put it all back by hand: roles, `aria-activedescendant`, type-ahead,
9
+ * arrows, a hidden input.
10
+ *
11
+ * When the options are plain text, none of that is worth paying for. A native
12
+ * select gives you the platform's own picker — the wheel on iOS, the
13
+ * search-as-you-type list on desktop — form participation, and zero JavaScript.
14
+ * `appearance-none` plus a background chevron gets it to match the design
15
+ * system everywhere except the open popup itself.
16
+ *
17
+ * Use `Select` when an option needs an icon, a description, or a swatch. Use
18
+ * this one otherwise.
19
+ */
20
+ import { component, html } from "@c9up/aurora";
21
+ import { cn } from "../lib/cn.js";
22
+ import { accessor, read } from "../lib/props.js";
23
+ export const nativeSelectClasses = "border-input dark:bg-input/30 flex h-9 w-full min-w-0 appearance-none rounded-md border bg-transparent py-1 pr-8 pl-3 text-base shadow-xs transition-[color,box-shadow] outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm aria-invalid:border-destructive aria-invalid:ring-destructive/20";
24
+ function selectedValue(event) {
25
+ const target = event.target;
26
+ return target instanceof HTMLSelectElement ? target.value : "";
27
+ }
28
+ export const NativeSelect = component((props) => {
29
+ /**
30
+ * Options grouped in source order.
31
+ *
32
+ * Consecutive entries sharing a `group` become one `<optgroup>`; a run that
33
+ * starts again later becomes a second one. Sorting by group instead would
34
+ * quietly reorder a list the caller arranged deliberately.
35
+ */
36
+ function runs() {
37
+ const out = [];
38
+ for (const option of props.options) {
39
+ const last = out[out.length - 1];
40
+ if (last !== undefined && last.group === option.group)
41
+ last.items.push(option);
42
+ else
43
+ out.push({ group: option.group, items: [option] });
44
+ }
45
+ return out;
46
+ }
47
+ const renderOption = (option) => html `<option value="${option.value}" ?disabled="${option.disabled === true}">
48
+ ${option.label}
49
+ </option>`;
50
+ return html `<div data-slot="native-select" class="relative inline-flex w-full items-center">
51
+ <select
52
+ id="${props.id}"
53
+ name="${props.name}"
54
+ aria-invalid="${() => (read(props.invalid) === true ? "true" : undefined)}"
55
+ aria-describedby="${accessor(props.describedBy, undefined)}"
56
+ ?disabled="${accessor(props.disabled, false)}"
57
+ ?required="${accessor(props.required, false)}"
58
+ .value="${accessor(props.value, "")}"
59
+ class="${() => cn(nativeSelectClasses, read(props.class))}"
60
+ @change="${(event) => props.onValueChange?.(selectedValue(event), event)}"
61
+ >
62
+ ${props.placeholder === undefined
63
+ ? null
64
+ : html `<option value="" disabled selected>${props.placeholder}</option>`}
65
+ ${runs().map((run) => run.group === undefined
66
+ ? run.items.map(renderOption)
67
+ : html `<optgroup label="${run.group}">${run.items.map(renderOption)}</optgroup>`)}
68
+ </select>
69
+ <svg
70
+ xmlns="http://www.w3.org/2000/svg"
71
+ viewBox="0 0 24 24"
72
+ fill="none"
73
+ stroke="currentColor"
74
+ stroke-width="2"
75
+ stroke-linecap="round"
76
+ stroke-linejoin="round"
77
+ aria-hidden="true"
78
+ class="pointer-events-none absolute end-3 size-4 opacity-50"
79
+ ><path d="m6 9 6 6 6-6" /></svg>
80
+ </div>`;
81
+ });
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Progress — a determinate progress bar.
3
+ *
4
+ * The fill is a full-width child translated left by the remaining percentage,
5
+ * rather than a width animation. Transforms are composited off the main
6
+ * thread; animating `width` relayouts the bar on every frame, which is visible
7
+ * on a long upload.
8
+ *
9
+ * A `value` of `null` means indeterminate, and drops `aria-valuenow` — the
10
+ * ARIA spec is explicit that an absent value is how "unknown progress" is
11
+ * expressed, and reporting `0` instead tells the user it has not started.
12
+ */
13
+ import { type Reactive } from "../lib/props.js";
14
+ export interface ProgressProps {
15
+ /** `0`–`max`, or `null` for indeterminate. */
16
+ value?: Reactive<number | null>;
17
+ max?: Reactive<number>;
18
+ label?: Reactive<string | undefined>;
19
+ class?: Reactive<string>;
20
+ }
21
+ export declare const Progress: (props?: ProgressProps | undefined) => import("@c9up/aurora").TemplateResult;
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Progress — a determinate progress bar.
3
+ *
4
+ * The fill is a full-width child translated left by the remaining percentage,
5
+ * rather than a width animation. Transforms are composited off the main
6
+ * thread; animating `width` relayouts the bar on every frame, which is visible
7
+ * on a long upload.
8
+ *
9
+ * A `value` of `null` means indeterminate, and drops `aria-valuenow` — the
10
+ * ARIA spec is explicit that an absent value is how "unknown progress" is
11
+ * expressed, and reporting `0` instead tells the user it has not started.
12
+ */
13
+ import { component, html } from "@c9up/aurora";
14
+ import { cn } from "../lib/cn.js";
15
+ import { read, readOr } from "../lib/props.js";
16
+ export const Progress = component((props) => {
17
+ const max = () => {
18
+ const given = readOr(props.max, 100);
19
+ // A zero or negative maximum would make the percentage infinite or
20
+ // negative and push the fill off the track in one direction or the other.
21
+ return given > 0 ? given : 100;
22
+ };
23
+ const value = () => {
24
+ const given = readOr(props.value, 0);
25
+ if (given === null)
26
+ return null;
27
+ return Math.min(Math.max(given, 0), max());
28
+ };
29
+ const percent = () => {
30
+ const current = value();
31
+ return current === null ? 0 : (current / max()) * 100;
32
+ };
33
+ return html `<div
34
+ data-slot="progress"
35
+ role="progressbar"
36
+ aria-valuemin="0"
37
+ aria-valuemax="${max}"
38
+ aria-valuenow="${() => value() ?? undefined}"
39
+ aria-label="${() => read(props.label)}"
40
+ class="${() => cn("bg-primary/20 relative h-2 w-full overflow-hidden rounded-full", read(props.class))}"
41
+ >
42
+ <div
43
+ data-slot="progress-indicator"
44
+ class="bg-primary h-full w-full flex-1 transition-transform"
45
+ style="${() => `transform: translateX(-${100 - percent()}%)`}"
46
+ ></div>
47
+ </div>`;
48
+ });
@@ -0,0 +1,28 @@
1
+ /**
2
+ * ScrollArea — a scroll container with a restrained scrollbar.
3
+ *
4
+ * A deliberate departure from Radix, which hides the native scrollbar and
5
+ * draws its own out of divs, driven by scroll and resize listeners. That buys
6
+ * pixel-identical scrollbars across platforms; it costs a virtualised
7
+ * scrollbar that has to re-derive thumb size and position on every frame, and
8
+ * that drops momentum scrolling on touch.
9
+ *
10
+ * The trade is not worth it here. `scrollbar-width` and `scrollbar-color` are
11
+ * supported everywhere nebula targets, the WebKit pseudo-elements cover the
12
+ * rest, and the result keeps native scrolling — including overscroll, momentum
13
+ * and the platform's own accessibility behaviour — for a handful of classes
14
+ * and no JavaScript.
15
+ *
16
+ * Apps needing a fully custom scrollbar should reach for a dedicated library;
17
+ * this component would be the wrong place to grow one.
18
+ */
19
+ import { type Slot } from "../lib/children.js";
20
+ import { type Reactive } from "../lib/props.js";
21
+ export declare const scrollAreaClasses: string;
22
+ export interface ScrollAreaProps {
23
+ children?: Slot;
24
+ /** Which axis may scroll. Default `"vertical"`. */
25
+ orientation?: Reactive<"vertical" | "horizontal" | "both">;
26
+ class?: Reactive<string>;
27
+ }
28
+ export declare const ScrollArea: (props?: ScrollAreaProps | undefined) => import("@c9up/aurora").TemplateResult;
@@ -0,0 +1,37 @@
1
+ /**
2
+ * ScrollArea — a scroll container with a restrained scrollbar.
3
+ *
4
+ * A deliberate departure from Radix, which hides the native scrollbar and
5
+ * draws its own out of divs, driven by scroll and resize listeners. That buys
6
+ * pixel-identical scrollbars across platforms; it costs a virtualised
7
+ * scrollbar that has to re-derive thumb size and position on every frame, and
8
+ * that drops momentum scrolling on touch.
9
+ *
10
+ * The trade is not worth it here. `scrollbar-width` and `scrollbar-color` are
11
+ * supported everywhere nebula targets, the WebKit pseudo-elements cover the
12
+ * rest, and the result keeps native scrolling — including overscroll, momentum
13
+ * and the platform's own accessibility behaviour — for a handful of classes
14
+ * and no JavaScript.
15
+ *
16
+ * Apps needing a fully custom scrollbar should reach for a dedicated library;
17
+ * this component would be the wrong place to grow one.
18
+ */
19
+ import { component, html } from "@c9up/aurora";
20
+ import { slot } from "../lib/children.js";
21
+ import { cn } from "../lib/cn.js";
22
+ import { read, readOr } from "../lib/props.js";
23
+ export const scrollAreaClasses = cn("relative overflow-auto [scrollbar-width:thin] [scrollbar-color:var(--border)_transparent]", "[&::-webkit-scrollbar]:w-2.5 [&::-webkit-scrollbar]:h-2.5", "[&::-webkit-scrollbar-track]:bg-transparent", "[&::-webkit-scrollbar-thumb]:rounded-full [&::-webkit-scrollbar-thumb]:border-2 [&::-webkit-scrollbar-thumb]:border-solid [&::-webkit-scrollbar-thumb]:border-transparent [&::-webkit-scrollbar-thumb]:bg-border [&::-webkit-scrollbar-thumb]:bg-clip-content", "hover:[&::-webkit-scrollbar-thumb]:bg-muted-foreground/40");
24
+ export const ScrollArea = component((props) => {
25
+ const axisClass = () => {
26
+ const orientation = readOr(props.orientation, "vertical");
27
+ if (orientation === "horizontal")
28
+ return "overflow-x-auto overflow-y-hidden";
29
+ if (orientation === "both")
30
+ return "overflow-auto";
31
+ return "overflow-y-auto overflow-x-hidden";
32
+ };
33
+ return html `<div
34
+ data-slot="scroll-area"
35
+ class="${() => cn(scrollAreaClasses, axisClass(), read(props.class))}"
36
+ >${slot(props.children)}</div>`;
37
+ });
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Separator — a rule between sections.
3
+ *
4
+ * `role="separator"` with an orientation, or `role="none"` when decorative.
5
+ * The distinction matters to screen readers: a separator is announced as a
6
+ * structural boundary, and a purely visual line between two buttons in a
7
+ * toolbar should not be. Decorative is the default, matching Radix.
8
+ */
9
+ import { type Reactive } from "../lib/props.js";
10
+ export interface SeparatorProps {
11
+ orientation?: Reactive<"horizontal" | "vertical">;
12
+ /** `false` exposes it to assistive technology as a real boundary. */
13
+ decorative?: Reactive<boolean>;
14
+ class?: Reactive<string>;
15
+ }
16
+ export declare const Separator: (props?: SeparatorProps | undefined) => import("@c9up/aurora").TemplateResult;
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Separator — a rule between sections.
3
+ *
4
+ * `role="separator"` with an orientation, or `role="none"` when decorative.
5
+ * The distinction matters to screen readers: a separator is announced as a
6
+ * structural boundary, and a purely visual line between two buttons in a
7
+ * toolbar should not be. Decorative is the default, matching Radix.
8
+ */
9
+ import { component, html } from "@c9up/aurora";
10
+ import { cn } from "../lib/cn.js";
11
+ import { read, readOr } from "../lib/props.js";
12
+ export const Separator = component((props) => {
13
+ const orientation = () => readOr(props.orientation, "horizontal");
14
+ return html `<div
15
+ data-slot="separator"
16
+ data-orientation="${orientation}"
17
+ role="${() => (readOr(props.decorative, true) ? "none" : "separator")}"
18
+ aria-orientation="${() => (readOr(props.decorative, true) ? undefined : orientation())}"
19
+ class="${() => cn("bg-border shrink-0 data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px", read(props.class))}"
20
+ ></div>`;
21
+ });
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Skeleton — a placeholder block while content loads.
3
+ *
4
+ * `aria-hidden` and no live region on purpose. A pulsing grey box carries no
5
+ * information, and announcing one per row of a loading table is worse than
6
+ * silence. The surrounding region should carry `aria-busy` instead, which says
7
+ * the same thing once.
8
+ */
9
+ import { type Reactive } from "../lib/props.js";
10
+ export interface SkeletonProps {
11
+ class?: Reactive<string>;
12
+ }
13
+ export declare const Skeleton: (props?: SkeletonProps | undefined) => import("@c9up/aurora").TemplateResult;
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Skeleton — a placeholder block while content loads.
3
+ *
4
+ * `aria-hidden` and no live region on purpose. A pulsing grey box carries no
5
+ * information, and announcing one per row of a loading table is worse than
6
+ * silence. The surrounding region should carry `aria-busy` instead, which says
7
+ * the same thing once.
8
+ */
9
+ import { component, html } from "@c9up/aurora";
10
+ import { cn } from "../lib/cn.js";
11
+ import { read } from "../lib/props.js";
12
+ export const Skeleton = component((props) => {
13
+ return html `<div
14
+ data-slot="skeleton"
15
+ aria-hidden="true"
16
+ class="${() => cn("bg-accent animate-pulse rounded-md", read(props.class))}"
17
+ ></div>`;
18
+ });
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Slider — pick a number from a range.
3
+ *
4
+ * A native `<input type="range">`, restyled. Radix builds its own from a div
5
+ * and pointer maths because it supports multiple thumbs; nebula does not, and
6
+ * for the single-thumb case the native control already brings keyboard
7
+ * support (arrows, Page Up/Down, Home/End), the correct ARIA role and value
8
+ * announcements, and touch behaviour that no reimplementation gets right for
9
+ * free.
10
+ *
11
+ * The cost is the thumb, which can only be reached through the two vendor
12
+ * pseudo-elements. Both are written out; they cannot be combined into one
13
+ * selector, since a browser drops an entire rule containing a pseudo-element
14
+ * it does not recognise.
15
+ */
16
+ import { type Reactive } from "../lib/props.js";
17
+ export declare const sliderClasses: string;
18
+ export interface SliderProps {
19
+ id?: string;
20
+ name?: string;
21
+ value?: Reactive<number>;
22
+ min?: Reactive<number>;
23
+ max?: Reactive<number>;
24
+ step?: Reactive<number>;
25
+ disabled?: Reactive<boolean>;
26
+ label?: Reactive<string | undefined>;
27
+ class?: Reactive<string>;
28
+ onValueChange?: (value: number, event: Event) => void;
29
+ }
30
+ export declare const Slider: (props?: SliderProps | undefined) => import("@c9up/aurora").TemplateResult;
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Slider — pick a number from a range.
3
+ *
4
+ * A native `<input type="range">`, restyled. Radix builds its own from a div
5
+ * and pointer maths because it supports multiple thumbs; nebula does not, and
6
+ * for the single-thumb case the native control already brings keyboard
7
+ * support (arrows, Page Up/Down, Home/End), the correct ARIA role and value
8
+ * announcements, and touch behaviour that no reimplementation gets right for
9
+ * free.
10
+ *
11
+ * The cost is the thumb, which can only be reached through the two vendor
12
+ * pseudo-elements. Both are written out; they cannot be combined into one
13
+ * selector, since a browser drops an entire rule containing a pseudo-element
14
+ * it does not recognise.
15
+ */
16
+ import { component, html } from "@c9up/aurora";
17
+ import { cn } from "../lib/cn.js";
18
+ import { accessor, read } from "../lib/props.js";
19
+ const THUMB = "size-4 appearance-none rounded-full border border-primary bg-background shadow-sm transition-[color,box-shadow]";
20
+ export const sliderClasses = cn("h-1.5 w-full cursor-pointer appearance-none rounded-full bg-primary/20 outline-none disabled:pointer-events-none disabled:opacity-50", `[&::-webkit-slider-thumb]:${THUMB.split(" ").join(" [&::-webkit-slider-thumb]:")}`, `[&::-moz-range-thumb]:${THUMB.split(" ").join(" [&::-moz-range-thumb]:")}`, "focus-visible:[&::-webkit-slider-thumb]:ring-ring/50 focus-visible:[&::-webkit-slider-thumb]:ring-[3px]", "focus-visible:[&::-moz-range-thumb]:ring-ring/50 focus-visible:[&::-moz-range-thumb]:ring-[3px]");
21
+ function inputValue(event) {
22
+ const target = event.target;
23
+ return target instanceof HTMLInputElement ? Number(target.value) : 0;
24
+ }
25
+ export const Slider = component((props) => {
26
+ return html `<input
27
+ type="range"
28
+ data-slot="slider"
29
+ id="${props.id}"
30
+ name="${props.name}"
31
+ min="${accessor(props.min, 0)}"
32
+ max="${accessor(props.max, 100)}"
33
+ step="${accessor(props.step, 1)}"
34
+ aria-label="${accessor(props.label, undefined)}"
35
+ ?disabled="${accessor(props.disabled, false)}"
36
+ .value="${accessor(props.value, 0)}"
37
+ class="${() => cn(sliderClasses, read(props.class))}"
38
+ @input="${(event) => props.onValueChange?.(inputValue(event), event)}"
39
+ />`;
40
+ });
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Spinner — an indeterminate activity indicator.
3
+ *
4
+ * `role="status"` with a visually hidden label, so a screen reader announces
5
+ * that something is loading. A bare spinning glyph is invisible to a reader
6
+ * and the user is left with silence where a sighted user sees motion.
7
+ *
8
+ * `motion-reduce:animate-none` stops the rotation for users who asked the
9
+ * system to reduce motion. The glyph stays, so the indicator does not vanish.
10
+ */
11
+ import { type Reactive } from "../lib/props.js";
12
+ export interface SpinnerProps {
13
+ /** Announced to assistive technology. Defaults to `"Loading"`. */
14
+ label?: Reactive<string>;
15
+ class?: Reactive<string>;
16
+ }
17
+ export declare const Spinner: (props?: SpinnerProps | undefined) => import("@c9up/aurora").TemplateResult;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Spinner — an indeterminate activity indicator.
3
+ *
4
+ * `role="status"` with a visually hidden label, so a screen reader announces
5
+ * that something is loading. A bare spinning glyph is invisible to a reader
6
+ * and the user is left with silence where a sighted user sees motion.
7
+ *
8
+ * `motion-reduce:animate-none` stops the rotation for users who asked the
9
+ * system to reduce motion. The glyph stays, so the indicator does not vanish.
10
+ */
11
+ import { component, html } from "@c9up/aurora";
12
+ import { cn } from "../lib/cn.js";
13
+ import { LoaderIcon } from "../lib/icons.js";
14
+ import { read, readOr } from "../lib/props.js";
15
+ export const Spinner = component((props) => {
16
+ return html `<span data-slot="spinner" role="status" class="inline-flex">
17
+ ${LoaderIcon({ class: cn("size-4 animate-spin motion-reduce:animate-none", read(props.class)) })}
18
+ <span class="sr-only">${() => readOr(props.label, "Loading")}</span>
19
+ </span>`;
20
+ });
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Switch — an on/off toggle that applies immediately.
3
+ *
4
+ * Same native-input approach as Checkbox, with `role="switch"` so assistive
5
+ * technology announces "on/off" rather than "checked". The distinction is not
6
+ * cosmetic: a checkbox is a choice you confirm by submitting, a switch is a
7
+ * setting that takes effect the moment you flip it.
8
+ *
9
+ * The thumb is a sibling translated by `peer-checked:`, not a pseudo-element,
10
+ * so the travel distance stays expressible in utility classes and a user can
11
+ * retune the size without leaving the class string.
12
+ */
13
+ import { type Reactive } from "../lib/props.js";
14
+ export declare const switchTrackClasses = "peer absolute inset-0 size-full appearance-none rounded-full border border-transparent bg-input shadow-xs outline-none transition-colors dark:bg-input/80 checked:bg-primary focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50";
15
+ export interface SwitchProps {
16
+ id?: string;
17
+ name?: string;
18
+ checked?: Reactive<boolean>;
19
+ disabled?: Reactive<boolean>;
20
+ label?: Reactive<string | undefined>;
21
+ class?: Reactive<string>;
22
+ onCheckedChange?: (checked: boolean, event: Event) => void;
23
+ }
24
+ export declare const Switch: (props?: SwitchProps | undefined) => import("@c9up/aurora").TemplateResult;
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Switch — an on/off toggle that applies immediately.
3
+ *
4
+ * Same native-input approach as Checkbox, with `role="switch"` so assistive
5
+ * technology announces "on/off" rather than "checked". The distinction is not
6
+ * cosmetic: a checkbox is a choice you confirm by submitting, a switch is a
7
+ * setting that takes effect the moment you flip it.
8
+ *
9
+ * The thumb is a sibling translated by `peer-checked:`, not a pseudo-element,
10
+ * so the travel distance stays expressible in utility classes and a user can
11
+ * retune the size without leaving the class string.
12
+ */
13
+ import { component, html } from "@c9up/aurora";
14
+ import { cn } from "../lib/cn.js";
15
+ import { accessor, read } from "../lib/props.js";
16
+ export const switchTrackClasses = "peer absolute inset-0 size-full appearance-none rounded-full border border-transparent bg-input shadow-xs outline-none transition-colors dark:bg-input/80 checked:bg-primary focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50";
17
+ function inputChecked(event) {
18
+ const target = event.target;
19
+ return target instanceof HTMLInputElement ? target.checked : false;
20
+ }
21
+ export const Switch = component((props) => {
22
+ return html `<span
23
+ data-slot="switch"
24
+ class="${() => cn("relative inline-flex h-[1.15rem] w-8 shrink-0 items-center", read(props.class))}"
25
+ >
26
+ <input
27
+ type="checkbox"
28
+ role="switch"
29
+ id="${props.id}"
30
+ name="${props.name}"
31
+ aria-label="${accessor(props.label, undefined)}"
32
+ ?disabled="${accessor(props.disabled, false)}"
33
+ .checked="${accessor(props.checked, false)}"
34
+ class="${switchTrackClasses}"
35
+ @change="${(event) => props.onCheckedChange?.(inputChecked(event), event)}"
36
+ />
37
+ <span
38
+ class="pointer-events-none ml-px block size-4 rounded-full bg-background shadow-lg ring-0 transition-transform peer-checked:translate-x-[calc(100%-2px)] dark:peer-checked:bg-primary-foreground"
39
+ ></span>
40
+ </span>`;
41
+ });