@misoto22/design 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (191) hide show
  1. package/LICENSE +21 -0
  2. package/dist/components/Accordion/Accordion.d.ts +34 -0
  3. package/dist/components/Accordion/Accordion.js +37 -0
  4. package/dist/components/Accordion/Accordion.js.map +1 -0
  5. package/dist/components/Alert/Alert.d.ts +32 -0
  6. package/dist/components/Alert/Alert.js +49 -0
  7. package/dist/components/Alert/Alert.js.map +1 -0
  8. package/dist/components/AppShell/AppShell.d.ts +38 -0
  9. package/dist/components/AppShell/AppShell.js +92 -0
  10. package/dist/components/AppShell/AppShell.js.map +1 -0
  11. package/dist/components/Avatar/Avatar.d.ts +29 -0
  12. package/dist/components/Avatar/Avatar.js +39 -0
  13. package/dist/components/Avatar/Avatar.js.map +1 -0
  14. package/dist/components/Badge/Badge.d.ts +30 -0
  15. package/dist/components/Badge/Badge.js +29 -0
  16. package/dist/components/Badge/Badge.js.map +1 -0
  17. package/dist/components/Breadcrumb/Breadcrumb.d.ts +31 -0
  18. package/dist/components/Breadcrumb/Breadcrumb.js +31 -0
  19. package/dist/components/Breadcrumb/Breadcrumb.js.map +1 -0
  20. package/dist/components/Button/Button.d.ts +70 -0
  21. package/dist/components/Button/Button.js +81 -0
  22. package/dist/components/Button/Button.js.map +1 -0
  23. package/dist/components/Calendar/Calendar.d.ts +28 -0
  24. package/dist/components/Calendar/Calendar.js +87 -0
  25. package/dist/components/Calendar/Calendar.js.map +1 -0
  26. package/dist/components/Card/Card.d.ts +49 -0
  27. package/dist/components/Card/Card.js +64 -0
  28. package/dist/components/Card/Card.js.map +1 -0
  29. package/dist/components/Checkbox/Checkbox.d.ts +23 -0
  30. package/dist/components/Checkbox/Checkbox.js +24 -0
  31. package/dist/components/Checkbox/Checkbox.js.map +1 -0
  32. package/dist/components/Collapsible/Collapsible.d.ts +31 -0
  33. package/dist/components/Collapsible/Collapsible.js +35 -0
  34. package/dist/components/Collapsible/Collapsible.js.map +1 -0
  35. package/dist/components/Combobox/Combobox.d.ts +63 -0
  36. package/dist/components/Combobox/Combobox.js +151 -0
  37. package/dist/components/Combobox/Combobox.js.map +1 -0
  38. package/dist/components/Command/Command.d.ts +98 -0
  39. package/dist/components/Command/Command.js +166 -0
  40. package/dist/components/Command/Command.js.map +1 -0
  41. package/dist/components/ContextMenu/ContextMenu.d.ts +38 -0
  42. package/dist/components/ContextMenu/ContextMenu.js +79 -0
  43. package/dist/components/ContextMenu/ContextMenu.js.map +1 -0
  44. package/dist/components/DatePicker/DatePicker.d.ts +98 -0
  45. package/dist/components/DatePicker/DatePicker.js +214 -0
  46. package/dist/components/DatePicker/DatePicker.js.map +1 -0
  47. package/dist/components/Dialog/Dialog.d.ts +48 -0
  48. package/dist/components/Dialog/Dialog.js +72 -0
  49. package/dist/components/Dialog/Dialog.js.map +1 -0
  50. package/dist/components/DropdownMenu/DropdownMenu.d.ts +49 -0
  51. package/dist/components/DropdownMenu/DropdownMenu.js +89 -0
  52. package/dist/components/DropdownMenu/DropdownMenu.js.map +1 -0
  53. package/dist/components/EmptyState/EmptyState.d.ts +31 -0
  54. package/dist/components/EmptyState/EmptyState.js +30 -0
  55. package/dist/components/EmptyState/EmptyState.js.map +1 -0
  56. package/dist/components/ErrorState/ErrorState.d.ts +34 -0
  57. package/dist/components/ErrorState/ErrorState.js +32 -0
  58. package/dist/components/ErrorState/ErrorState.js.map +1 -0
  59. package/dist/components/Field/Field.d.ts +44 -0
  60. package/dist/components/Field/Field.js +54 -0
  61. package/dist/components/Field/Field.js.map +1 -0
  62. package/dist/components/FigureBand/FigureBand.d.ts +51 -0
  63. package/dist/components/FigureBand/FigureBand.js +52 -0
  64. package/dist/components/FigureBand/FigureBand.js.map +1 -0
  65. package/dist/components/FloatingIconButton/FloatingIconButton.d.ts +29 -0
  66. package/dist/components/FloatingIconButton/FloatingIconButton.js +32 -0
  67. package/dist/components/FloatingIconButton/FloatingIconButton.js.map +1 -0
  68. package/dist/components/Input/Input.d.ts +22 -0
  69. package/dist/components/Input/Input.js +21 -0
  70. package/dist/components/Input/Input.js.map +1 -0
  71. package/dist/components/Kbd/Kbd.d.ts +20 -0
  72. package/dist/components/Kbd/Kbd.js +21 -0
  73. package/dist/components/Kbd/Kbd.js.map +1 -0
  74. package/dist/components/LinkArrow/LinkArrow.d.ts +24 -0
  75. package/dist/components/LinkArrow/LinkArrow.js +24 -0
  76. package/dist/components/LinkArrow/LinkArrow.js.map +1 -0
  77. package/dist/components/NativeSelect/NativeSelect.d.ts +27 -0
  78. package/dist/components/NativeSelect/NativeSelect.js +46 -0
  79. package/dist/components/NativeSelect/NativeSelect.js.map +1 -0
  80. package/dist/components/NavItem/NavItem.d.ts +39 -0
  81. package/dist/components/NavItem/NavItem.js +31 -0
  82. package/dist/components/NavItem/NavItem.js.map +1 -0
  83. package/dist/components/Pagination/Pagination.d.ts +44 -0
  84. package/dist/components/Pagination/Pagination.js +109 -0
  85. package/dist/components/Pagination/Pagination.js.map +1 -0
  86. package/dist/components/Popover/Popover.d.ts +39 -0
  87. package/dist/components/Popover/Popover.js +57 -0
  88. package/dist/components/Popover/Popover.js.map +1 -0
  89. package/dist/components/Progress/Progress.d.ts +31 -0
  90. package/dist/components/Progress/Progress.js +45 -0
  91. package/dist/components/Progress/Progress.js.map +1 -0
  92. package/dist/components/RadioGroup/RadioGroup.d.ts +43 -0
  93. package/dist/components/RadioGroup/RadioGroup.js +43 -0
  94. package/dist/components/RadioGroup/RadioGroup.js.map +1 -0
  95. package/dist/components/ScrollArea/ScrollArea.d.ts +31 -0
  96. package/dist/components/ScrollArea/ScrollArea.js +54 -0
  97. package/dist/components/ScrollArea/ScrollArea.js.map +1 -0
  98. package/dist/components/SearchableMenu/SearchableMenu.d.ts +59 -0
  99. package/dist/components/SearchableMenu/SearchableMenu.js +79 -0
  100. package/dist/components/SearchableMenu/SearchableMenu.js.map +1 -0
  101. package/dist/components/Select/Select.d.ts +71 -0
  102. package/dist/components/Select/Select.js +114 -0
  103. package/dist/components/Select/Select.js.map +1 -0
  104. package/dist/components/Separator/Separator.d.ts +30 -0
  105. package/dist/components/Separator/Separator.js +35 -0
  106. package/dist/components/Separator/Separator.js.map +1 -0
  107. package/dist/components/Sheet/Sheet.d.ts +38 -0
  108. package/dist/components/Sheet/Sheet.js +68 -0
  109. package/dist/components/Sheet/Sheet.js.map +1 -0
  110. package/dist/components/Skeleton/Skeleton.d.ts +63 -0
  111. package/dist/components/Skeleton/Skeleton.js +44 -0
  112. package/dist/components/Skeleton/Skeleton.js.map +1 -0
  113. package/dist/components/Slider/Slider.d.ts +35 -0
  114. package/dist/components/Slider/Slider.js +52 -0
  115. package/dist/components/Slider/Slider.js.map +1 -0
  116. package/dist/components/Spinner/Spinner.d.ts +50 -0
  117. package/dist/components/Spinner/Spinner.js +48 -0
  118. package/dist/components/Spinner/Spinner.js.map +1 -0
  119. package/dist/components/StatusDot/StatusDot.d.ts +29 -0
  120. package/dist/components/StatusDot/StatusDot.js +44 -0
  121. package/dist/components/StatusDot/StatusDot.js.map +1 -0
  122. package/dist/components/StatusPill/StatusPill.d.ts +25 -0
  123. package/dist/components/StatusPill/StatusPill.js +31 -0
  124. package/dist/components/StatusPill/StatusPill.js.map +1 -0
  125. package/dist/components/Switch/Switch.d.ts +23 -0
  126. package/dist/components/Switch/Switch.js +23 -0
  127. package/dist/components/Switch/Switch.js.map +1 -0
  128. package/dist/components/Table/Table.d.ts +96 -0
  129. package/dist/components/Table/Table.js +136 -0
  130. package/dist/components/Table/Table.js.map +1 -0
  131. package/dist/components/Tabs/Tabs.d.ts +35 -0
  132. package/dist/components/Tabs/Tabs.js +48 -0
  133. package/dist/components/Tabs/Tabs.js.map +1 -0
  134. package/dist/components/Tag/Tag.d.ts +28 -0
  135. package/dist/components/Tag/Tag.js +22 -0
  136. package/dist/components/Tag/Tag.js.map +1 -0
  137. package/dist/components/Textarea/Textarea.d.ts +20 -0
  138. package/dist/components/Textarea/Textarea.js +32 -0
  139. package/dist/components/Textarea/Textarea.js.map +1 -0
  140. package/dist/components/Toast/Toast.d.ts +24 -0
  141. package/dist/components/Toast/Toast.js +41 -0
  142. package/dist/components/Toast/Toast.js.map +1 -0
  143. package/dist/components/ToggleGroup/ToggleGroup.d.ts +41 -0
  144. package/dist/components/ToggleGroup/ToggleGroup.js +78 -0
  145. package/dist/components/ToggleGroup/ToggleGroup.js.map +1 -0
  146. package/dist/components/Tooltip/Tooltip.d.ts +43 -0
  147. package/dist/components/Tooltip/Tooltip.js +40 -0
  148. package/dist/components/Tooltip/Tooltip.js.map +1 -0
  149. package/dist/fonts/hanken-grotesk-300.woff2 +0 -0
  150. package/dist/fonts/hanken-grotesk-400.woff2 +0 -0
  151. package/dist/fonts/hanken-grotesk-500.woff2 +0 -0
  152. package/dist/fonts/hanken-grotesk-600.woff2 +0 -0
  153. package/dist/fonts/hanken-grotesk-700.woff2 +0 -0
  154. package/dist/fonts/ibm-plex-mono-400.woff2 +0 -0
  155. package/dist/fonts/ibm-plex-mono-500.woff2 +0 -0
  156. package/dist/fonts/ibm-plex-mono-600.woff2 +0 -0
  157. package/dist/fonts/newsreader-300.woff2 +0 -0
  158. package/dist/fonts/newsreader-400.woff2 +0 -0
  159. package/dist/fonts/newsreader-500.woff2 +0 -0
  160. package/dist/fonts/newsreader-600.woff2 +0 -0
  161. package/dist/fonts.css +96 -0
  162. package/dist/index.d.ts +77 -0
  163. package/dist/index.js +63 -0
  164. package/dist/index.js.map +1 -0
  165. package/dist/keyframes.css +212 -0
  166. package/dist/lib/cn.d.ts +21 -0
  167. package/dist/lib/cn.js +9 -0
  168. package/dist/lib/cn.js.map +1 -0
  169. package/dist/lib/control.d.ts +31 -0
  170. package/dist/lib/control.js +14 -0
  171. package/dist/lib/control.js.map +1 -0
  172. package/dist/lib/index.d.ts +4 -0
  173. package/dist/lib/index.js +11 -0
  174. package/dist/lib/index.js.map +1 -0
  175. package/dist/lib/overlay-container.d.ts +38 -0
  176. package/dist/lib/overlay-container.js +15 -0
  177. package/dist/lib/overlay-container.js.map +1 -0
  178. package/dist/lib/useSelectionIndicator.d.ts +29 -0
  179. package/dist/lib/useSelectionIndicator.js +45 -0
  180. package/dist/lib/useSelectionIndicator.js.map +1 -0
  181. package/dist/semantic.css +127 -0
  182. package/dist/styles.css +98 -0
  183. package/dist/themes.css +115 -0
  184. package/dist/tokens/brand.d.ts +47 -0
  185. package/dist/tokens/brand.js +38 -0
  186. package/dist/tokens/brand.js.map +1 -0
  187. package/dist/tokens.css +323 -0
  188. package/dist/tokens.d.ts +153 -0
  189. package/dist/tokens.js +766 -0
  190. package/dist/tokens.json +765 -0
  191. package/package.json +130 -0
@@ -0,0 +1,81 @@
1
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
+ import { Slot } from "@radix-ui/react-slot";
3
+ import { cn } from "../../lib/cn.js";
4
+ import { Spinner } from "../Spinner/Spinner.js";
5
+ const BASE = "inline-flex items-center justify-center gap-(--control-gap) rounded-(--radius-pill) font-sans transition-[opacity,color,border-color,background-color] duration-(--duration-fast) ease-(--ease-out-expo) disabled:opacity-(--disabled-opacity) disabled:pointer-events-none aria-disabled:opacity-(--disabled-opacity) aria-disabled:pointer-events-none";
6
+ const VARIANT = {
7
+ primary: "bg-(--accent) text-(--accent-foreground) border border-(--accent) hover:opacity-85",
8
+ secondary: "border border-(--rule-2) text-(--ink) bg-transparent hover:border-(--ink)",
9
+ ghost: "border border-transparent text-(--ink-2) hover:text-(--ink) hover:bg-(--stone)",
10
+ /* The one place chroma is allowed on a control, and only because the action
11
+ is destructive — that is a state, not a brand colour. */
12
+ danger: "bg-(--danger) text-(--paper) border border-(--danger) hover:opacity-85"
13
+ };
14
+ const SIZE = {
15
+ sm: "min-h-(--control-h-sm) px-(--control-px-sm) py-(--control-py-sm) text-[13px]",
16
+ md: "min-h-(--control-h-md) px-(--control-px-md) py-(--control-py-md) text-sm",
17
+ lg: "min-h-(--control-h-lg) px-(--control-px-lg) py-(--control-py-lg) text-[15px]"
18
+ };
19
+ const ICON_SIZE = {
20
+ sm: "size-(--control-h-sm) p-0",
21
+ md: "size-(--control-h-md) p-0",
22
+ lg: "size-(--control-h-lg) p-0"
23
+ };
24
+ const SPINNER_SIZE = { sm: "sm", md: "sm", lg: "md" };
25
+ function Keycap({ glyph }) {
26
+ return /* @__PURE__ */ jsx("span", { className: "rounded-(--radius-pill) border border-current px-2 py-0.5 font-mono text-[11px] opacity-55", children: glyph });
27
+ }
28
+ function Button(props) {
29
+ const {
30
+ children,
31
+ variant = "primary",
32
+ size = "md",
33
+ keycap,
34
+ loading = false,
35
+ iconOnly = false,
36
+ asChild = false,
37
+ className
38
+ } = props;
39
+ const cls = cn(BASE, VARIANT[variant], iconOnly ? ICON_SIZE[size] : SIZE[size], className);
40
+ const spinnerTone = variant === "primary" || variant === "danger" ? "current" : "default";
41
+ const decoration = /* @__PURE__ */ jsxs(Fragment, { children: [
42
+ loading && /* @__PURE__ */ jsx(Spinner, { size: SPINNER_SIZE[size], tone: spinnerTone, label: null }),
43
+ children,
44
+ keycap && /* @__PURE__ */ jsx(Keycap, { glyph: keycap })
45
+ ] });
46
+ if (asChild) {
47
+ const { children: _c2, variant: _v2, size: _s2, keycap: _k2, loading: _l2, iconOnly: _i2, asChild: _a2, className: _cn2, ...rest2 } = props;
48
+ return /* @__PURE__ */ jsx(Slot, { className: cls, ...rest2, children });
49
+ }
50
+ if (props.href !== void 0) {
51
+ const { children: _c2, variant: _v2, size: _s2, keycap: _k2, loading: _l2, iconOnly: _i2, asChild: _a2, className: _cn2, href, ...rest2 } = props;
52
+ return /* @__PURE__ */ jsx(
53
+ "a",
54
+ {
55
+ href,
56
+ className: cls,
57
+ "aria-disabled": loading || void 0,
58
+ ...rest2,
59
+ children: decoration
60
+ }
61
+ );
62
+ }
63
+ const { children: _c, variant: _v, size: _s, keycap: _k, loading: _l, iconOnly: _i, asChild: _a, className: _cn, ...rest } = props;
64
+ return /* @__PURE__ */ jsx(
65
+ "button",
66
+ {
67
+ type: rest.type ?? "button",
68
+ "aria-busy": loading || void 0,
69
+ disabled: rest.disabled || loading,
70
+ className: cls,
71
+ ...rest,
72
+ children: decoration
73
+ }
74
+ );
75
+ }
76
+ var Button_default = Button;
77
+ export {
78
+ Button,
79
+ Button_default as default
80
+ };
81
+ //# sourceMappingURL=Button.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/Button/Button.tsx"],"sourcesContent":["import { Slot } from '@radix-ui/react-slot'\nimport type { AnchorHTMLAttributes, ButtonHTMLAttributes, ReactNode } from 'react'\nimport { cn } from '../../lib/cn'\nimport { Spinner } from '../Spinner/Spinner'\n\nexport type ButtonVariant = 'primary' | 'secondary' | 'ghost' | 'danger'\nexport type ButtonSize = 'sm' | 'md' | 'lg'\n\n/**\n * The base every variant shares.\n *\n * A pill, and it does not move on hover. An earlier base lifted a pixel and\n * asked for a shadow the White Reset does not have — `--shadow` resolves to\n * `none`, so that hover state was invisible on every button in the system while\n * still costing a transition. Opacity carries the filled hover; the border\n * carries the outlined one.\n *\n * The transition names its properties rather than using `all`, so a layout\n * change (a width that grows when the label becomes \"Saving…\") is instant\n * instead of sliding.\n */\nconst BASE =\n 'inline-flex items-center justify-center gap-(--control-gap) rounded-(--radius-pill) font-sans transition-[opacity,color,border-color,background-color] duration-(--duration-fast) ease-(--ease-out-expo) disabled:opacity-(--disabled-opacity) disabled:pointer-events-none aria-disabled:opacity-(--disabled-opacity) aria-disabled:pointer-events-none'\n\nconst VARIANT: Record<ButtonVariant, string> = {\n primary: 'bg-(--accent) text-(--accent-foreground) border border-(--accent) hover:opacity-85',\n secondary: 'border border-(--rule-2) text-(--ink) bg-transparent hover:border-(--ink)',\n ghost: 'border border-transparent text-(--ink-2) hover:text-(--ink) hover:bg-(--stone)',\n /* The one place chroma is allowed on a control, and only because the action\n is destructive — that is a state, not a brand colour. */\n danger: 'bg-(--danger) text-(--paper) border border-(--danger) hover:opacity-85',\n}\n\n/**\n * `min-h` rather than `h`, so a button that wraps to two lines grows instead of\n * clipping.\n *\n * The heights come from the density tokens rather than from a literal, so a\n * subtree marked `data-density=\"compact\"` shrinks its controls without a single\n * call site being told. At the default density `md` is 44px — the pointer\n * target WCAG 2.5.5 asks for.\n */\nconst SIZE: Record<ButtonSize, string> = {\n sm: 'min-h-(--control-h-sm) px-(--control-px-sm) py-(--control-py-sm) text-[13px]',\n md: 'min-h-(--control-h-md) px-(--control-px-md) py-(--control-py-md) text-sm',\n lg: 'min-h-(--control-h-lg) px-(--control-px-lg) py-(--control-py-lg) text-[15px]',\n}\n\n/** Square, because an icon has no label to give the box its width. */\nconst ICON_SIZE: Record<ButtonSize, string> = {\n sm: 'size-(--control-h-sm) p-0',\n md: 'size-(--control-h-md) p-0',\n lg: 'size-(--control-h-lg) p-0',\n}\n\nconst SPINNER_SIZE: Record<ButtonSize, 'sm' | 'md'> = { sm: 'sm', md: 'sm', lg: 'md' }\n\ninterface CommonProps {\n children?: ReactNode\n /**\n * Which action this is. `primary` is the one thing the screen wants you to\n * do, so there is at most one per view; `danger` is reserved for destructive\n * actions and is the only place chroma is allowed on a control.\n */\n variant?: ButtonVariant\n /** 36 / 44 / 48px tall. `md` meets the pointer-target floor on its own. */\n size?: ButtonSize\n /** Optional mono keycap glyph rendered after the label (e.g. \"P\"). */\n keycap?: string\n /**\n * Swaps the leading content for a spinner and blocks interaction. The label\n * stays put — a button that empties out while it works loses its width, and\n * the page reflows under the pointer that just clicked it.\n */\n loading?: boolean\n /**\n * Square control with no label. REQUIRES `aria-label`: an icon-only button\n * with no accessible name is invisible to a screen reader, and this is the\n * single most common way a design system ships an unusable control.\n */\n iconOnly?: boolean\n /**\n * Render the child element instead of a `<button>`, keeping these styles.\n * Use it to hand the styling to a router's `Link` — `asChild` is what keeps\n * this package free of any one router.\n *\n * The decoration slots (`keycap`, `loading`) are not injected into a slotted\n * child; compose them inside it yourself.\n *\n * @example\n * <Button asChild><Link href=\"/work\">Work</Link></Button>\n */\n asChild?: boolean\n className?: string\n}\n\ntype ButtonAsLink = CommonProps &\n Omit<AnchorHTMLAttributes<HTMLAnchorElement>, 'className' | 'children'> & { href: string }\n\ntype ButtonAsButton = CommonProps &\n Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'className' | 'children'> & { href?: undefined }\n\nexport type ButtonProps = ButtonAsLink | ButtonAsButton\n\nfunction Keycap({ glyph }: { glyph: string }) {\n return (\n <span className=\"rounded-(--radius-pill) border border-current px-2 py-0.5 font-mono text-[11px] opacity-55\">\n {glyph}\n </span>\n )\n}\n\n/**\n * The system's action.\n *\n * Renders a `<button>`, or an `<a>` when given `href`, or whatever you hand it\n * via `asChild`. No router is imported, so the package stays framework-agnostic\n * and a Next or React Router app wires its own `Link` at the call site.\n *\n * Server-component friendly: there is no client boundary here, so it renders in\n * a static page as well as an interactive one.\n *\n * @example\n * <Button variant=\"secondary\" keycap=\"P\">View projects</Button>\n * @example\n * <Button loading disabled>Saving…</Button>\n * @example\n * <Button iconOnly aria-label=\"Close\"><X size={16} /></Button>\n */\nexport function Button(props: ButtonProps) {\n const {\n children,\n variant = 'primary',\n size = 'md',\n keycap,\n loading = false,\n iconOnly = false,\n asChild = false,\n className,\n } = props\n\n const cls = cn(BASE, VARIANT[variant], iconOnly ? ICON_SIZE[size] : SIZE[size], className)\n\n // A filled ground needs a spinner in the inherited colour; an outlined one\n // reads better against the page's own rule scale.\n const spinnerTone = variant === 'primary' || variant === 'danger' ? 'current' : 'default'\n const decoration = (\n <>\n {loading && <Spinner size={SPINNER_SIZE[size]} tone={spinnerTone} label={null} />}\n {children}\n {keycap && <Keycap glyph={keycap} />}\n </>\n )\n\n if (asChild) {\n const { children: _c, variant: _v, size: _s, keycap: _k, loading: _l, iconOnly: _i, asChild: _a, className: _cn, ...rest } = props\n return (\n <Slot className={cls} {...rest}>\n {children}\n </Slot>\n )\n }\n\n if (props.href !== undefined) {\n const { children: _c, variant: _v, size: _s, keycap: _k, loading: _l, iconOnly: _i, asChild: _a, className: _cn, href, ...rest } = props\n return (\n <a\n href={href}\n className={cls}\n // A link cannot be `disabled`; `aria-disabled` plus the pointer-events\n // rule in BASE is the accessible equivalent.\n aria-disabled={loading || undefined}\n {...rest}\n >\n {decoration}\n </a>\n )\n }\n\n const { children: _c, variant: _v, size: _s, keycap: _k, loading: _l, iconOnly: _i, asChild: _a, className: _cn, ...rest } = props\n return (\n <button\n type={rest.type ?? 'button'}\n aria-busy={loading || undefined}\n disabled={rest.disabled || loading}\n className={cls}\n {...rest}\n >\n {decoration}\n </button>\n )\n}\n\nexport default Button\n"],"mappings":"AA0GI,SAyCA,UAzCA,KAyCA,YAzCA;AA1GJ,SAAS,YAAY;AAErB,SAAS,UAAU;AACnB,SAAS,eAAe;AAkBxB,MAAM,OACJ;AAEF,MAAM,UAAyC;AAAA,EAC7C,SAAS;AAAA,EACT,WAAW;AAAA,EACX,OAAO;AAAA;AAAA;AAAA,EAGP,QAAQ;AACV;AAWA,MAAM,OAAmC;AAAA,EACvC,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AACN;AAGA,MAAM,YAAwC;AAAA,EAC5C,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AACN;AAEA,MAAM,eAAgD,EAAE,IAAI,MAAM,IAAI,MAAM,IAAI,KAAK;AAiDrF,SAAS,OAAO,EAAE,MAAM,GAAsB;AAC5C,SACE,oBAAC,UAAK,WAAU,8FACb,iBACH;AAEJ;AAmBO,SAAS,OAAO,OAAoB;AACzC,QAAM;AAAA,IACJ;AAAA,IACA,UAAU;AAAA,IACV,OAAO;AAAA,IACP;AAAA,IACA,UAAU;AAAA,IACV,WAAW;AAAA,IACX,UAAU;AAAA,IACV;AAAA,EACF,IAAI;AAEJ,QAAM,MAAM,GAAG,MAAM,QAAQ,OAAO,GAAG,WAAW,UAAU,IAAI,IAAI,KAAK,IAAI,GAAG,SAAS;AAIzF,QAAM,cAAc,YAAY,aAAa,YAAY,WAAW,YAAY;AAChF,QAAM,aACJ,iCACG;AAAA,eAAW,oBAAC,WAAQ,MAAM,aAAa,IAAI,GAAG,MAAM,aAAa,OAAO,MAAM;AAAA,IAC9E;AAAA,IACA,UAAU,oBAAC,UAAO,OAAO,QAAQ;AAAA,KACpC;AAGF,MAAI,SAAS;AACX,UAAM,EAAE,UAAUA,KAAI,SAASC,KAAI,MAAMC,KAAI,QAAQC,KAAI,SAASC,KAAI,UAAUC,KAAI,SAASC,KAAI,WAAWC,MAAK,GAAGC,MAAK,IAAI;AAC7H,WACE,oBAAC,QAAK,WAAW,KAAM,GAAGA,OACvB,UACH;AAAA,EAEJ;AAEA,MAAI,MAAM,SAAS,QAAW;AAC5B,UAAM,EAAE,UAAUR,KAAI,SAASC,KAAI,MAAMC,KAAI,QAAQC,KAAI,SAASC,KAAI,UAAUC,KAAI,SAASC,KAAI,WAAWC,MAAK,MAAM,GAAGC,MAAK,IAAI;AACnI,WACE;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA,WAAW;AAAA,QAGX,iBAAe,WAAW;AAAA,QACzB,GAAGA;AAAA,QAEH;AAAA;AAAA,IACH;AAAA,EAEJ;AAEA,QAAM,EAAE,UAAU,IAAI,SAAS,IAAI,MAAM,IAAI,QAAQ,IAAI,SAAS,IAAI,UAAU,IAAI,SAAS,IAAI,WAAW,KAAK,GAAG,KAAK,IAAI;AAC7H,SACE;AAAA,IAAC;AAAA;AAAA,MACC,MAAM,KAAK,QAAQ;AAAA,MACnB,aAAW,WAAW;AAAA,MACtB,UAAU,KAAK,YAAY;AAAA,MAC3B,WAAW;AAAA,MACV,GAAG;AAAA,MAEH;AAAA;AAAA,EACH;AAEJ;AAEA,IAAO,iBAAQ;","names":["_c","_v","_s","_k","_l","_i","_a","_cn","rest"]}
@@ -0,0 +1,28 @@
1
+ import * as react from 'react';
2
+ import { DayPickerProps } from 'react-day-picker';
3
+
4
+ type CalendarProps = DayPickerProps;
5
+ /**
6
+ * A month, as a grid of days.
7
+ *
8
+ * Wraps react-day-picker rather than building a calendar, and the reason is the
9
+ * long tail: a month grid is easy, and everything around it is not — the
10
+ * keyboard contract (arrows move a day, Page moves a month, Home and End reach
11
+ * the week's ends), the `aria-live` announcement when the month changes, week
12
+ * numbering, the first day of the week varying by locale, and the fact that
13
+ * "today" and "selected" are different states a screen reader must hear apart.
14
+ *
15
+ * Every class is replaced rather than layered over the library's stylesheet, so
16
+ * nothing here depends on a CSS file this package does not control — but the
17
+ * library's OWN class is kept on each slot, because passing a class REPLACES it
18
+ * and every `.rdp-*` selector downstream depends on those.
19
+ *
20
+ * @example
21
+ * <Calendar mode="single" selected={date} onSelect={setDate} />
22
+ * @example
23
+ * // A birth date: widen the years, since the default span is deliberately short.
24
+ * <Calendar mode="single" startMonth={new Date(1920, 0)} endMonth={new Date()} />
25
+ */
26
+ declare function Calendar({ className, classNames, captionLayout, components, ...props }: CalendarProps): react.JSX.Element;
27
+
28
+ export { Calendar, type CalendarProps, Calendar as default };
@@ -0,0 +1,87 @@
1
+ "use client";
2
+ import { jsx } from "react/jsx-runtime";
3
+ import { ChevronLeft, ChevronRight } from "lucide-react";
4
+ import { DayPicker } from "react-day-picker";
5
+ import { cn } from "../../lib/cn.js";
6
+ import { Select, SelectItem } from "../Select/Select.js";
7
+ function CalendarDropdown({ options, value, onChange, "aria-label": label }) {
8
+ return /* @__PURE__ */ jsx(
9
+ Select,
10
+ {
11
+ label: label ?? "Select",
12
+ value: value === void 0 ? void 0 : String(value),
13
+ onValueChange: (next) => onChange?.({ target: { value: next } }),
14
+ contentClassName: "max-h-[10rem]",
15
+ className: "h-8 w-auto min-w-24 gap-1.5 border-transparent px-2 py-1 font-heading text-[length:var(--fs-item)] hover:border-(--rule-2)",
16
+ children: (options ?? []).map((option) => /* @__PURE__ */ jsx(SelectItem, { value: String(option.value), disabled: option.disabled, children: option.label }, option.value))
17
+ }
18
+ );
19
+ }
20
+ const DEFAULT_SPAN = 10;
21
+ function Calendar({ className, classNames, captionLayout, components, ...props }) {
22
+ const now = /* @__PURE__ */ new Date();
23
+ const isRange = props.mode === "range";
24
+ const FILL = "[&>button]:bg-(--accent) [&>button]:text-(--accent-foreground) [&>button:hover]:bg-(--accent) [&>button:hover]:text-(--accent-foreground)";
25
+ return /* @__PURE__ */ jsx(
26
+ DayPicker,
27
+ {
28
+ showOutsideDays: true,
29
+ captionLayout: captionLayout ?? "dropdown",
30
+ startMonth: props.startMonth ?? new Date(now.getFullYear() - DEFAULT_SPAN, 0),
31
+ endMonth: props.endMonth ?? new Date(now.getFullYear() + DEFAULT_SPAN, 11),
32
+ className: cn("w-fit p-3", className),
33
+ components: {
34
+ Chevron: ({ orientation, ...rest }) => orientation === "left" ? /* @__PURE__ */ jsx(ChevronLeft, { size: 16, strokeWidth: 1.5, "aria-hidden": true, ...rest }) : /* @__PURE__ */ jsx(ChevronRight, { size: 16, strokeWidth: 1.5, "aria-hidden": true, ...rest }),
35
+ Dropdown: CalendarDropdown,
36
+ ...components
37
+ },
38
+ classNames: {
39
+ // `rdp-root` is kept deliberately. Passing a class for a slot REPLACES
40
+ // the library's own, so overriding `root` silently removed the hook
41
+ // that every `.rdp-*` selector — ours and a consumer's — depends on.
42
+ root: "rdp-root relative",
43
+ // The nav renders BEFORE the months in the DOM, so left in the flow it
44
+ // stacks its two arrows above the grid. Taking it out of the flow puts
45
+ // them where they belong — one at each end of the caption.
46
+ nav: "absolute inset-x-3 top-3 z-1 flex items-center justify-between",
47
+ months: "flex flex-col gap-4 sm:flex-row",
48
+ month: "flex flex-col gap-3",
49
+ month_caption: "flex h-9 items-center justify-center",
50
+ dropdowns: "flex items-center gap-1",
51
+ dropdown_root: "relative inline-flex items-center",
52
+ month_grid: "w-full border-collapse",
53
+ weekdays: "flex",
54
+ weekday: "w-9 mono-meta font-normal text-(--ink-3-aa)",
55
+ week: "mt-1 flex w-full",
56
+ // The band lives on the CELL and the mark lives on the BUTTON. Keeping
57
+ // them apart is what lets a range be one continuous wash while both of
58
+ // its ends stay round — the previous arrangement flattened the corners
59
+ // of whichever day was an end, and a one-day range is BOTH ends, so it
60
+ // came out as a square.
61
+ day: "relative p-0 text-center",
62
+ day_button: "relative z-1 inline-flex size-9 items-center justify-center rounded-(--radius-pill) text-sm text-(--ink-2) transition-colors duration-(--duration-fast) hover:bg-(--stone) hover:text-(--ink)",
63
+ selected: isRange ? "" : FILL,
64
+ // Today is an outline, not a fill — it is a fact about the calendar,
65
+ // not a choice the reader made, and the two must not look alike.
66
+ today: "[&>button]:ring-1 [&>button]:ring-(--rule-hard) [&>button]:ring-inset",
67
+ outside: "[&>button]:text-(--ink-3-aa)",
68
+ disabled: "[&>button]:opacity-(--disabled-opacity) [&>button]:pointer-events-none",
69
+ // The wash lives on the CELL and the mark on the BUTTON, which is what
70
+ // lets a range read as one continuous band while both of its ends stay
71
+ // round. A middle day gets the wash and no fill; the ends get both.
72
+ range_middle: "bg-(--accent-muted) [&>button]:text-(--ink) [&>button:hover]:bg-(--stone)",
73
+ range_start: cn("bg-(--accent-muted) rounded-s-(--radius-pill)", FILL),
74
+ range_end: cn("bg-(--accent-muted) rounded-e-(--radius-pill)", FILL),
75
+ hidden: "invisible",
76
+ ...classNames
77
+ },
78
+ ...props
79
+ }
80
+ );
81
+ }
82
+ var Calendar_default = Calendar;
83
+ export {
84
+ Calendar,
85
+ Calendar_default as default
86
+ };
87
+ //# sourceMappingURL=Calendar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/Calendar/Calendar.tsx"],"sourcesContent":["'use client'\n\nimport { ChevronLeft, ChevronRight } from 'lucide-react'\nimport { DayPicker, type DayPickerProps, type DropdownProps } from 'react-day-picker'\nimport { cn } from '../../lib/cn'\nimport { Select, SelectItem } from '../Select/Select'\n\nexport type CalendarProps = DayPickerProps\n\n/**\n * Month and year, as the system's own `Select`.\n *\n * The library renders a native `<select>` here. Two problems with that: the\n * option list is drawn by the operating system, so it carries none of these\n * tokens and looks like a different product the moment it opens; and with a\n * wide year range it becomes a hundred-row list the platform renders as one\n * enormous column.\n *\n * The adapter is the small awkward part — the library hands back a native\n * change event, so a value from our Select is wrapped to look like one. That is\n * contained here rather than leaking into every call site.\n */\nfunction CalendarDropdown({ options, value, onChange, 'aria-label': label }: DropdownProps) {\n return (\n <Select\n label={label ?? 'Select'}\n value={value === undefined ? undefined : String(value)}\n onValueChange={(next) =>\n onChange?.({ target: { value: next } } as React.ChangeEvent<HTMLSelectElement>)\n }\n // A third of the calendar's height. The default list is as tall as the\n // month grid, so opening the year picker blotted out the thing the reader\n // opened it in order to change.\n contentClassName=\"max-h-[10rem]\"\n className=\"h-8 w-auto min-w-24 gap-1.5 border-transparent px-2 py-1 font-heading text-[length:var(--fs-item)] hover:border-(--rule-2)\"\n >\n {(options ?? []).map((option) => (\n <SelectItem key={option.value} value={String(option.value)} disabled={option.disabled}>\n {option.label}\n </SelectItem>\n ))}\n </Select>\n )\n}\n\n/** Ten years either side of now — see the note on `startMonth` below. */\nconst DEFAULT_SPAN = 10\n\n/**\n * A month, as a grid of days.\n *\n * Wraps react-day-picker rather than building a calendar, and the reason is the\n * long tail: a month grid is easy, and everything around it is not — the\n * keyboard contract (arrows move a day, Page moves a month, Home and End reach\n * the week's ends), the `aria-live` announcement when the month changes, week\n * numbering, the first day of the week varying by locale, and the fact that\n * \"today\" and \"selected\" are different states a screen reader must hear apart.\n *\n * Every class is replaced rather than layered over the library's stylesheet, so\n * nothing here depends on a CSS file this package does not control — but the\n * library's OWN class is kept on each slot, because passing a class REPLACES it\n * and every `.rdp-*` selector downstream depends on those.\n *\n * @example\n * <Calendar mode=\"single\" selected={date} onSelect={setDate} />\n * @example\n * // A birth date: widen the years, since the default span is deliberately short.\n * <Calendar mode=\"single\" startMonth={new Date(1920, 0)} endMonth={new Date()} />\n */\nexport function Calendar({ className, classNames, captionLayout, components, ...props }: CalendarProps) {\n const now = new Date()\n\n /**\n * A one-day range is marked start AND end AND middle, all three.\n *\n * That is what made the earlier arrangement collapse: `range_middle` cleared\n * the button's fill so a middle day would not look chosen, and on a one-day\n * range that same rule cleared the fill of the only day there was. Deciding\n * by mode instead of fighting it with `!important` means the two never\n * describe the same button — in a range the fill belongs to the ends, and in\n * single mode it belongs to `selected`.\n */\n const isRange = props.mode === 'range'\n const FILL =\n '[&>button]:bg-(--accent) [&>button]:text-(--accent-foreground) [&>button:hover]:bg-(--accent) [&>button:hover]:text-(--accent-foreground)'\n\n return (\n <DayPicker\n showOutsideDays\n // Month and year as dropdowns, not two arrows. Stepping a month at a time\n // is fine for \"next Tuesday\" and useless for a date two years back.\n captionLayout={captionLayout ?? 'dropdown'}\n // Ten years either side, NOT a century. A hundred-and-eleven-row year\n // list is a scroll, not a choice — and the case that needs one (a birth\n // date) is rare enough to ask for it explicitly. Both ends are props, so\n // widening is one line at the call site.\n startMonth={props.startMonth ?? new Date(now.getFullYear() - DEFAULT_SPAN, 0)}\n endMonth={props.endMonth ?? new Date(now.getFullYear() + DEFAULT_SPAN, 11)}\n className={cn('w-fit p-3', className)}\n components={{\n Chevron: ({ orientation, ...rest }) =>\n orientation === 'left' ? (\n <ChevronLeft size={16} strokeWidth={1.5} aria-hidden {...rest} />\n ) : (\n <ChevronRight size={16} strokeWidth={1.5} aria-hidden {...rest} />\n ),\n Dropdown: CalendarDropdown,\n ...components,\n }}\n classNames={{\n // `rdp-root` is kept deliberately. Passing a class for a slot REPLACES\n // the library's own, so overriding `root` silently removed the hook\n // that every `.rdp-*` selector — ours and a consumer's — depends on.\n root: 'rdp-root relative',\n // The nav renders BEFORE the months in the DOM, so left in the flow it\n // stacks its two arrows above the grid. Taking it out of the flow puts\n // them where they belong — one at each end of the caption.\n nav: 'absolute inset-x-3 top-3 z-1 flex items-center justify-between',\n months: 'flex flex-col gap-4 sm:flex-row',\n month: 'flex flex-col gap-3',\n month_caption: 'flex h-9 items-center justify-center',\n dropdowns: 'flex items-center gap-1',\n dropdown_root: 'relative inline-flex items-center',\n month_grid: 'w-full border-collapse',\n weekdays: 'flex',\n weekday: 'w-9 mono-meta font-normal text-(--ink-3-aa)',\n week: 'mt-1 flex w-full',\n // The band lives on the CELL and the mark lives on the BUTTON. Keeping\n // them apart is what lets a range be one continuous wash while both of\n // its ends stay round — the previous arrangement flattened the corners\n // of whichever day was an end, and a one-day range is BOTH ends, so it\n // came out as a square.\n day: 'relative p-0 text-center',\n day_button:\n 'relative z-1 inline-flex size-9 items-center justify-center rounded-(--radius-pill) text-sm text-(--ink-2) transition-colors duration-(--duration-fast) hover:bg-(--stone) hover:text-(--ink)',\n selected: isRange ? '' : FILL,\n // Today is an outline, not a fill — it is a fact about the calendar,\n // not a choice the reader made, and the two must not look alike.\n today: '[&>button]:ring-1 [&>button]:ring-(--rule-hard) [&>button]:ring-inset',\n outside: '[&>button]:text-(--ink-3-aa)',\n disabled: '[&>button]:opacity-(--disabled-opacity) [&>button]:pointer-events-none',\n // The wash lives on the CELL and the mark on the BUTTON, which is what\n // lets a range read as one continuous band while both of its ends stay\n // round. A middle day gets the wash and no fill; the ends get both.\n range_middle: 'bg-(--accent-muted) [&>button]:text-(--ink) [&>button:hover]:bg-(--stone)',\n range_start: cn('bg-(--accent-muted) rounded-s-(--radius-pill)', FILL),\n range_end: cn('bg-(--accent-muted) rounded-e-(--radius-pill)', FILL),\n hidden: 'invisible',\n ...classNames,\n }}\n {...props}\n />\n )\n}\n\nexport default Calendar\n"],"mappings":";AAqCQ;AAnCR,SAAS,aAAa,oBAAoB;AAC1C,SAAS,iBAA0D;AACnE,SAAS,UAAU;AACnB,SAAS,QAAQ,kBAAkB;AAiBnC,SAAS,iBAAiB,EAAE,SAAS,OAAO,UAAU,cAAc,MAAM,GAAkB;AAC1F,SACE;AAAA,IAAC;AAAA;AAAA,MACC,OAAO,SAAS;AAAA,MAChB,OAAO,UAAU,SAAY,SAAY,OAAO,KAAK;AAAA,MACrD,eAAe,CAAC,SACd,WAAW,EAAE,QAAQ,EAAE,OAAO,KAAK,EAAE,CAAyC;AAAA,MAKhF,kBAAiB;AAAA,MACjB,WAAU;AAAA,MAER,sBAAW,CAAC,GAAG,IAAI,CAAC,WACpB,oBAAC,cAA8B,OAAO,OAAO,OAAO,KAAK,GAAG,UAAU,OAAO,UAC1E,iBAAO,SADO,OAAO,KAExB,CACD;AAAA;AAAA,EACH;AAEJ;AAGA,MAAM,eAAe;AAuBd,SAAS,SAAS,EAAE,WAAW,YAAY,eAAe,YAAY,GAAG,MAAM,GAAkB;AACtG,QAAM,MAAM,oBAAI,KAAK;AAYrB,QAAM,UAAU,MAAM,SAAS;AAC/B,QAAM,OACJ;AAEF,SACE;AAAA,IAAC;AAAA;AAAA,MACC,iBAAe;AAAA,MAGf,eAAe,iBAAiB;AAAA,MAKhC,YAAY,MAAM,cAAc,IAAI,KAAK,IAAI,YAAY,IAAI,cAAc,CAAC;AAAA,MAC5E,UAAU,MAAM,YAAY,IAAI,KAAK,IAAI,YAAY,IAAI,cAAc,EAAE;AAAA,MACzE,WAAW,GAAG,aAAa,SAAS;AAAA,MACpC,YAAY;AAAA,QACV,SAAS,CAAC,EAAE,aAAa,GAAG,KAAK,MAC/B,gBAAgB,SACd,oBAAC,eAAY,MAAM,IAAI,aAAa,KAAK,eAAW,MAAE,GAAG,MAAM,IAE/D,oBAAC,gBAAa,MAAM,IAAI,aAAa,KAAK,eAAW,MAAE,GAAG,MAAM;AAAA,QAEpE,UAAU;AAAA,QACV,GAAG;AAAA,MACL;AAAA,MACA,YAAY;AAAA;AAAA;AAAA;AAAA,QAIV,MAAM;AAAA;AAAA;AAAA;AAAA,QAIN,KAAK;AAAA,QACL,QAAQ;AAAA,QACR,OAAO;AAAA,QACP,eAAe;AAAA,QACf,WAAW;AAAA,QACX,eAAe;AAAA,QACf,YAAY;AAAA,QACZ,UAAU;AAAA,QACV,SAAS;AAAA,QACT,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAMN,KAAK;AAAA,QACL,YACE;AAAA,QACF,UAAU,UAAU,KAAK;AAAA;AAAA;AAAA,QAGzB,OAAO;AAAA,QACP,SAAS;AAAA,QACT,UAAU;AAAA;AAAA;AAAA;AAAA,QAIV,cAAc;AAAA,QACd,aAAa,GAAG,iDAAiD,IAAI;AAAA,QACrE,WAAW,GAAG,iDAAiD,IAAI;AAAA,QACnE,QAAQ;AAAA,QACR,GAAG;AAAA,MACL;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;AAEA,IAAO,mBAAQ;","names":[]}
@@ -0,0 +1,49 @@
1
+ import * as react from 'react';
2
+ import { HTMLAttributes } from 'react';
3
+
4
+ interface CardProps extends HTMLAttributes<HTMLDivElement> {
5
+ /**
6
+ * `outline` is a hairline box on the page ground — the default, and correct
7
+ * for a card sitting among other cards. `plate` fills with the one reversed
8
+ * surface and is for a card that IS the point of its band; use at most one
9
+ * per screen. `flat` drops the border entirely, for a card whose grid already
10
+ * draws the rules between cells.
11
+ */
12
+ variant?: 'outline' | 'plate' | 'flat';
13
+ }
14
+ /**
15
+ * A bounded surface.
16
+ *
17
+ * No shadow, by law: depth in this system is a hairline and a change of ground,
18
+ * never a blur. A card that needs to read as raised is a `plate`, which
19
+ * separates by reversal instead.
20
+ *
21
+ * Compose with the named sub-parts, or drop children straight in when the card
22
+ * has no header or footer to speak of.
23
+ *
24
+ * @example
25
+ * <Card>
26
+ * <CardHeader><CardTitle>Recent deploys</CardTitle><Badge>12</Badge></CardHeader>
27
+ * <CardBody>…</CardBody>
28
+ * <CardFooter>Updated just now</CardFooter>
29
+ * </Card>
30
+ */
31
+ declare function Card({ variant, className, children, ...rest }: CardProps): react.JSX.Element;
32
+ /** Header row — title on the left, an action or marker on the right. */
33
+ declare function CardHeader({ className, children, ...rest }: HTMLAttributes<HTMLDivElement>): react.JSX.Element;
34
+ /**
35
+ * The card's title, in the editorial serif.
36
+ *
37
+ * An `<h3>` by default, which is right inside a section with its own `<h2>` and
38
+ * wrong nearly everywhere else — pass `as` rather than leaving a page with a
39
+ * heading order that jumps.
40
+ */
41
+ declare function CardTitle({ as: Comp, className, children, ...rest }: HTMLAttributes<HTMLHeadingElement> & {
42
+ as?: 'h2' | 'h3' | 'h4';
43
+ }): react.JSX.Element;
44
+ /** The content well. */
45
+ declare function CardBody({ className, children, ...rest }: HTMLAttributes<HTMLDivElement>): react.JSX.Element;
46
+ /** A quiet strip for metadata or secondary actions. */
47
+ declare function CardFooter({ className, children, ...rest }: HTMLAttributes<HTMLDivElement>): react.JSX.Element;
48
+
49
+ export { Card, CardBody, CardFooter, CardHeader, type CardProps, CardTitle, Card as default };
@@ -0,0 +1,64 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { cn } from "../../lib/cn.js";
3
+ const VARIANT = {
4
+ outline: "border border-(--rule) bg-(--paper)",
5
+ plate: "border border-(--feature-surface) bg-(--feature-surface) text-(--on-feature) [--card-title:var(--on-feature)]",
6
+ flat: "border border-transparent bg-transparent"
7
+ };
8
+ function Card({ variant = "outline", className, children, ...rest }) {
9
+ return /* @__PURE__ */ jsx("div", { className: cn("rounded-(--radius-lg)", VARIANT[variant], className), ...rest, children });
10
+ }
11
+ function CardHeader({ className, children, ...rest }) {
12
+ return /* @__PURE__ */ jsx(
13
+ "div",
14
+ {
15
+ className: cn(
16
+ "flex items-center justify-between gap-4 border-b border-(--rule) px-5 py-4",
17
+ className
18
+ ),
19
+ ...rest,
20
+ children
21
+ }
22
+ );
23
+ }
24
+ function CardTitle({
25
+ as: Comp = "h3",
26
+ className,
27
+ children,
28
+ ...rest
29
+ }) {
30
+ return /* @__PURE__ */ jsx(
31
+ Comp,
32
+ {
33
+ className: cn(
34
+ "m-0 font-heading text-[length:var(--fs-item)] font-normal text-[var(--card-title,var(--ink))]",
35
+ className
36
+ ),
37
+ ...rest,
38
+ children
39
+ }
40
+ );
41
+ }
42
+ function CardBody({ className, children, ...rest }) {
43
+ return /* @__PURE__ */ jsx("div", { className: cn("p-5 text-sm leading-relaxed text-(--ink-2)", className), ...rest, children });
44
+ }
45
+ function CardFooter({ className, children, ...rest }) {
46
+ return /* @__PURE__ */ jsx(
47
+ "div",
48
+ {
49
+ className: cn("border-t border-(--rule) px-5 py-4 mono-meta text-(--ink-3-aa)", className),
50
+ ...rest,
51
+ children
52
+ }
53
+ );
54
+ }
55
+ var Card_default = Card;
56
+ export {
57
+ Card,
58
+ CardBody,
59
+ CardFooter,
60
+ CardHeader,
61
+ CardTitle,
62
+ Card_default as default
63
+ };
64
+ //# sourceMappingURL=Card.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/Card/Card.tsx"],"sourcesContent":["import type { HTMLAttributes } from 'react'\nimport { cn } from '../../lib/cn'\n\nexport interface CardProps extends HTMLAttributes<HTMLDivElement> {\n /**\n * `outline` is a hairline box on the page ground — the default, and correct\n * for a card sitting among other cards. `plate` fills with the one reversed\n * surface and is for a card that IS the point of its band; use at most one\n * per screen. `flat` drops the border entirely, for a card whose grid already\n * draws the rules between cells.\n */\n variant?: 'outline' | 'plate' | 'flat'\n}\n\nconst VARIANT = {\n outline: 'border border-(--rule) bg-(--paper)',\n plate:\n 'border border-(--feature-surface) bg-(--feature-surface) text-(--on-feature) [--card-title:var(--on-feature)]',\n flat: 'border border-transparent bg-transparent',\n} as const\n\n/**\n * A bounded surface.\n *\n * No shadow, by law: depth in this system is a hairline and a change of ground,\n * never a blur. A card that needs to read as raised is a `plate`, which\n * separates by reversal instead.\n *\n * Compose with the named sub-parts, or drop children straight in when the card\n * has no header or footer to speak of.\n *\n * @example\n * <Card>\n * <CardHeader><CardTitle>Recent deploys</CardTitle><Badge>12</Badge></CardHeader>\n * <CardBody>…</CardBody>\n * <CardFooter>Updated just now</CardFooter>\n * </Card>\n */\nexport function Card({ variant = 'outline', className, children, ...rest }: CardProps) {\n return (\n <div className={cn('rounded-(--radius-lg)', VARIANT[variant], className)} {...rest}>\n {children}\n </div>\n )\n}\n\n/** Header row — title on the left, an action or marker on the right. */\nexport function CardHeader({ className, children, ...rest }: HTMLAttributes<HTMLDivElement>) {\n return (\n <div\n className={cn(\n 'flex items-center justify-between gap-4 border-b border-(--rule) px-5 py-4',\n className,\n )}\n {...rest}\n >\n {children}\n </div>\n )\n}\n\n/**\n * The card's title, in the editorial serif.\n *\n * An `<h3>` by default, which is right inside a section with its own `<h2>` and\n * wrong nearly everywhere else — pass `as` rather than leaving a page with a\n * heading order that jumps.\n */\nexport function CardTitle({\n as: Comp = 'h3',\n className,\n children,\n ...rest\n}: HTMLAttributes<HTMLHeadingElement> & { as?: 'h2' | 'h3' | 'h4' }) {\n return (\n <Comp\n // Reads the card's own title colour rather than --ink directly. A plate\n // is a reversed surface, and a hardcoded ink title on it came out at\n // 1.25:1 — invisible, and invisible only on the one variant whose whole\n // job is to be different.\n className={cn(\n 'm-0 font-heading text-[length:var(--fs-item)] font-normal text-[var(--card-title,var(--ink))]',\n className,\n )}\n {...rest}\n >\n {children}\n </Comp>\n )\n}\n\n/** The content well. */\nexport function CardBody({ className, children, ...rest }: HTMLAttributes<HTMLDivElement>) {\n return (\n <div className={cn('p-5 text-sm leading-relaxed text-(--ink-2)', className)} {...rest}>\n {children}\n </div>\n )\n}\n\n/** A quiet strip for metadata or secondary actions. */\nexport function CardFooter({ className, children, ...rest }: HTMLAttributes<HTMLDivElement>) {\n return (\n <div\n className={cn('border-t border-(--rule) px-5 py-4 mono-meta text-(--ink-3-aa)', className)}\n {...rest}\n >\n {children}\n </div>\n )\n}\n\nexport default Card\n"],"mappings":"AAwCI;AAvCJ,SAAS,UAAU;AAanB,MAAM,UAAU;AAAA,EACd,SAAS;AAAA,EACT,OACE;AAAA,EACF,MAAM;AACR;AAmBO,SAAS,KAAK,EAAE,UAAU,WAAW,WAAW,UAAU,GAAG,KAAK,GAAc;AACrF,SACE,oBAAC,SAAI,WAAW,GAAG,yBAAyB,QAAQ,OAAO,GAAG,SAAS,GAAI,GAAG,MAC3E,UACH;AAEJ;AAGO,SAAS,WAAW,EAAE,WAAW,UAAU,GAAG,KAAK,GAAmC;AAC3F,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA,MAEH;AAAA;AAAA,EACH;AAEJ;AASO,SAAS,UAAU;AAAA,EACxB,IAAI,OAAO;AAAA,EACX;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAqE;AACnE,SACE;AAAA,IAAC;AAAA;AAAA,MAKC,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA,MAEH;AAAA;AAAA,EACH;AAEJ;AAGO,SAAS,SAAS,EAAE,WAAW,UAAU,GAAG,KAAK,GAAmC;AACzF,SACE,oBAAC,SAAI,WAAW,GAAG,8CAA8C,SAAS,GAAI,GAAG,MAC9E,UACH;AAEJ;AAGO,SAAS,WAAW,EAAE,WAAW,UAAU,GAAG,KAAK,GAAmC;AAC3F,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW,GAAG,kEAAkE,SAAS;AAAA,MACxF,GAAG;AAAA,MAEH;AAAA;AAAA,EACH;AAEJ;AAEA,IAAO,eAAQ;","names":[]}
@@ -0,0 +1,23 @@
1
+ import * as react from 'react';
2
+ import { ComponentProps } from 'react';
3
+ import * as CheckboxPrimitive from '@radix-ui/react-checkbox';
4
+
5
+ type CheckboxProps = ComponentProps<typeof CheckboxPrimitive.Root>;
6
+ /**
7
+ * A choice that takes effect when the form is submitted.
8
+ *
9
+ * Supports the indeterminate state (`checked="indeterminate"`), which is what a
10
+ * "select all" header needs when only some rows are selected — a plain
11
+ * unchecked box there tells the reader the opposite of the truth.
12
+ *
13
+ * Pair with `Field`, or wrap it in a `<label>` at the call site so the words
14
+ * beside it are part of the click target.
15
+ *
16
+ * @example
17
+ * <label className="flex items-center gap-2.5 text-sm">
18
+ * <Checkbox defaultChecked /> Subscribe to updates
19
+ * </label>
20
+ */
21
+ declare function Checkbox({ className, ...props }: CheckboxProps): react.JSX.Element;
22
+
23
+ export { Checkbox, type CheckboxProps, Checkbox as default };
@@ -0,0 +1,24 @@
1
+ "use client";
2
+ import { jsx } from "react/jsx-runtime";
3
+ import * as CheckboxPrimitive from "@radix-ui/react-checkbox";
4
+ import { Check, Minus } from "lucide-react";
5
+ import { cn } from "../../lib/cn.js";
6
+ function Checkbox({ className, ...props }) {
7
+ return /* @__PURE__ */ jsx(
8
+ CheckboxPrimitive.Root,
9
+ {
10
+ className: cn(
11
+ "inline-flex size-[18px] shrink-0 items-center justify-center rounded-(--radius-sm) border border-(--rule-2) bg-(--paper) text-(--accent-foreground) transition-colors duration-(--duration-fast) data-[state=checked]:border-(--accent) data-[state=checked]:bg-(--accent) data-[state=indeterminate]:border-(--accent) data-[state=indeterminate]:bg-(--accent) disabled:opacity-(--disabled-opacity) disabled:pointer-events-none",
12
+ className
13
+ ),
14
+ ...props,
15
+ children: /* @__PURE__ */ jsx(CheckboxPrimitive.Indicator, { className: "flex items-center justify-center text-current", children: props.checked === "indeterminate" ? /* @__PURE__ */ jsx(Minus, { className: "size-3", strokeWidth: 3, "aria-hidden": true }) : /* @__PURE__ */ jsx(Check, { className: "size-3", strokeWidth: 3, "aria-hidden": true }) })
16
+ }
17
+ );
18
+ }
19
+ var Checkbox_default = Checkbox;
20
+ export {
21
+ Checkbox,
22
+ Checkbox_default as default
23
+ };
24
+ //# sourceMappingURL=Checkbox.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/Checkbox/Checkbox.tsx"],"sourcesContent":["'use client'\n\nimport * as CheckboxPrimitive from '@radix-ui/react-checkbox'\nimport { Check, Minus } from 'lucide-react'\nimport type { ComponentProps } from 'react'\nimport { cn } from '../../lib/cn'\n\nexport type CheckboxProps = ComponentProps<typeof CheckboxPrimitive.Root>\n\n/**\n * A choice that takes effect when the form is submitted.\n *\n * Supports the indeterminate state (`checked=\"indeterminate\"`), which is what a\n * \"select all\" header needs when only some rows are selected — a plain\n * unchecked box there tells the reader the opposite of the truth.\n *\n * Pair with `Field`, or wrap it in a `<label>` at the call site so the words\n * beside it are part of the click target.\n *\n * @example\n * <label className=\"flex items-center gap-2.5 text-sm\">\n * <Checkbox defaultChecked /> Subscribe to updates\n * </label>\n */\nexport function Checkbox({ className, ...props }: CheckboxProps) {\n return (\n <CheckboxPrimitive.Root\n className={cn(\n 'inline-flex size-[18px] shrink-0 items-center justify-center rounded-(--radius-sm) border border-(--rule-2) bg-(--paper) text-(--accent-foreground) transition-colors duration-(--duration-fast) data-[state=checked]:border-(--accent) data-[state=checked]:bg-(--accent) data-[state=indeterminate]:border-(--accent) data-[state=indeterminate]:bg-(--accent) disabled:opacity-(--disabled-opacity) disabled:pointer-events-none',\n className,\n )}\n {...props}\n >\n <CheckboxPrimitive.Indicator className=\"flex items-center justify-center text-current\">\n {props.checked === 'indeterminate' ? (\n <Minus className=\"size-3\" strokeWidth={3} aria-hidden />\n ) : (\n <Check className=\"size-3\" strokeWidth={3} aria-hidden />\n )}\n </CheckboxPrimitive.Indicator>\n </CheckboxPrimitive.Root>\n )\n}\n\nexport default Checkbox\n"],"mappings":";AAmCU;AAjCV,YAAY,uBAAuB;AACnC,SAAS,OAAO,aAAa;AAE7B,SAAS,UAAU;AAmBZ,SAAS,SAAS,EAAE,WAAW,GAAG,MAAM,GAAkB;AAC/D,SACE;AAAA,IAAC,kBAAkB;AAAA,IAAlB;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA,MAEJ,8BAAC,kBAAkB,WAAlB,EAA4B,WAAU,iDACpC,gBAAM,YAAY,kBACjB,oBAAC,SAAM,WAAU,UAAS,aAAa,GAAG,eAAW,MAAC,IAEtD,oBAAC,SAAM,WAAU,UAAS,aAAa,GAAG,eAAW,MAAC,GAE1D;AAAA;AAAA,EACF;AAEJ;AAEA,IAAO,mBAAQ;","names":[]}
@@ -0,0 +1,31 @@
1
+ import * as react from 'react';
2
+ import { ComponentProps, ReactNode } from 'react';
3
+ import * as CollapsiblePrimitive from '@radix-ui/react-collapsible';
4
+
5
+ /** Radix Collapsible root, as a typed passthrough. */
6
+ declare const Collapsible: react.ForwardRefExoticComponent<CollapsiblePrimitive.CollapsibleProps & react.RefAttributes<HTMLDivElement>>;
7
+ interface CollapsibleSectionProps extends Omit<ComponentProps<typeof CollapsiblePrimitive.Root>, 'children' | 'title'> {
8
+ /** What the trigger says. */
9
+ title: ReactNode;
10
+ children: ReactNode;
11
+ }
12
+ /**
13
+ * One thing that opens, on its own.
14
+ *
15
+ * The difference from `Accordion` is arithmetic: an accordion is a SET, and a
16
+ * set can coordinate — opening one closes another. A collapsible is one
17
+ * disclosure with nothing to coordinate with. Reaching for an accordion of one
18
+ * gets you a component managing a value you never read.
19
+ *
20
+ * The marker here is a chevron rather than the accordion's plus, and
21
+ * deliberately: this reveals more of the same thing, where an accordion row
22
+ * opens a distinct answer.
23
+ *
24
+ * @example
25
+ * <CollapsibleSection title="Advanced settings">
26
+ * <Field label="Retries"><Input type="number" /></Field>
27
+ * </CollapsibleSection>
28
+ */
29
+ declare function CollapsibleSection({ title, children, className, ...props }: CollapsibleSectionProps): react.JSX.Element;
30
+
31
+ export { Collapsible, CollapsibleSection, type CollapsibleSectionProps, Collapsible as default };
@@ -0,0 +1,35 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import * as CollapsiblePrimitive from "@radix-ui/react-collapsible";
4
+ import { ChevronDown } from "lucide-react";
5
+ import { cn } from "../../lib/cn.js";
6
+ const Collapsible = CollapsiblePrimitive.Root;
7
+ function CollapsibleSection({
8
+ title,
9
+ children,
10
+ className,
11
+ ...props
12
+ }) {
13
+ return /* @__PURE__ */ jsxs(CollapsiblePrimitive.Root, { className: cn("w-full", className), ...props, children: [
14
+ /* @__PURE__ */ jsxs(CollapsiblePrimitive.Trigger, { className: "group flex w-full items-center justify-between gap-4 py-3 text-start text-sm text-(--ink) transition-colors duration-(--duration-fast) hover:text-(--ink-2)", children: [
15
+ title,
16
+ /* @__PURE__ */ jsx(
17
+ ChevronDown,
18
+ {
19
+ size: 16,
20
+ strokeWidth: 1.5,
21
+ "aria-hidden": true,
22
+ className: "shrink-0 text-(--ink-3-aa) transition-transform duration-(--duration-base) ease-(--ease-out-expo) group-data-[state=open]:rotate-180"
23
+ }
24
+ )
25
+ ] }),
26
+ /* @__PURE__ */ jsx(CollapsiblePrimitive.Content, { className: "overflow-hidden text-sm leading-relaxed text-(--ink-2) data-[state=closed]:animate-[m22-collapsible-up_var(--duration-base)_var(--ease)] data-[state=open]:animate-[m22-collapsible-down_var(--duration-base)_var(--ease)]", children: /* @__PURE__ */ jsx("div", { className: "pb-3", children }) })
27
+ ] });
28
+ }
29
+ var Collapsible_default = Collapsible;
30
+ export {
31
+ Collapsible,
32
+ CollapsibleSection,
33
+ Collapsible_default as default
34
+ };
35
+ //# sourceMappingURL=Collapsible.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/Collapsible/Collapsible.tsx"],"sourcesContent":["'use client'\n\nimport * as CollapsiblePrimitive from '@radix-ui/react-collapsible'\nimport { ChevronDown } from 'lucide-react'\nimport type { ComponentProps, ReactNode } from 'react'\nimport { cn } from '../../lib/cn'\n\n/** Radix Collapsible root, as a typed passthrough. */\nexport const Collapsible = CollapsiblePrimitive.Root\n\nexport interface CollapsibleSectionProps\n extends Omit<ComponentProps<typeof CollapsiblePrimitive.Root>, 'children' | 'title'> {\n /** What the trigger says. */\n title: ReactNode\n children: ReactNode\n}\n\n/**\n * One thing that opens, on its own.\n *\n * The difference from `Accordion` is arithmetic: an accordion is a SET, and a\n * set can coordinate — opening one closes another. A collapsible is one\n * disclosure with nothing to coordinate with. Reaching for an accordion of one\n * gets you a component managing a value you never read.\n *\n * The marker here is a chevron rather than the accordion's plus, and\n * deliberately: this reveals more of the same thing, where an accordion row\n * opens a distinct answer.\n *\n * @example\n * <CollapsibleSection title=\"Advanced settings\">\n * <Field label=\"Retries\"><Input type=\"number\" /></Field>\n * </CollapsibleSection>\n */\nexport function CollapsibleSection({\n title,\n children,\n className,\n ...props\n}: CollapsibleSectionProps) {\n return (\n <CollapsiblePrimitive.Root className={cn('w-full', className)} {...props}>\n <CollapsiblePrimitive.Trigger className=\"group flex w-full items-center justify-between gap-4 py-3 text-start text-sm text-(--ink) transition-colors duration-(--duration-fast) hover:text-(--ink-2)\">\n {title}\n <ChevronDown\n size={16}\n strokeWidth={1.5}\n aria-hidden\n className=\"shrink-0 text-(--ink-3-aa) transition-transform duration-(--duration-base) ease-(--ease-out-expo) group-data-[state=open]:rotate-180\"\n />\n </CollapsiblePrimitive.Trigger>\n <CollapsiblePrimitive.Content className=\"overflow-hidden text-sm leading-relaxed text-(--ink-2) data-[state=closed]:animate-[m22-collapsible-up_var(--duration-base)_var(--ease)] data-[state=open]:animate-[m22-collapsible-down_var(--duration-base)_var(--ease)]\">\n <div className=\"pb-3\">{children}</div>\n </CollapsiblePrimitive.Content>\n </CollapsiblePrimitive.Root>\n )\n}\n\nexport default Collapsible\n"],"mappings":";AA0CM,SAEE,KAFF;AAxCN,YAAY,0BAA0B;AACtC,SAAS,mBAAmB;AAE5B,SAAS,UAAU;AAGZ,MAAM,cAAc,qBAAqB;AA0BzC,SAAS,mBAAmB;AAAA,EACjC;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAA4B;AAC1B,SACE,qBAAC,qBAAqB,MAArB,EAA0B,WAAW,GAAG,UAAU,SAAS,GAAI,GAAG,OACjE;AAAA,yBAAC,qBAAqB,SAArB,EAA6B,WAAU,+JACrC;AAAA;AAAA,MACD;AAAA,QAAC;AAAA;AAAA,UACC,MAAM;AAAA,UACN,aAAa;AAAA,UACb,eAAW;AAAA,UACX,WAAU;AAAA;AAAA,MACZ;AAAA,OACF;AAAA,IACA,oBAAC,qBAAqB,SAArB,EAA6B,WAAU,8NACtC,8BAAC,SAAI,WAAU,QAAQ,UAAS,GAClC;AAAA,KACF;AAEJ;AAEA,IAAO,sBAAQ;","names":[]}
@@ -0,0 +1,63 @@
1
+ import * as react from 'react';
2
+
3
+ interface ComboboxOption {
4
+ value: string;
5
+ label: string;
6
+ /** Extra text matched by the filter but not shown as the label. */
7
+ keywords?: string[];
8
+ disabled?: boolean;
9
+ }
10
+ interface CommonProps {
11
+ options: ComboboxOption[];
12
+ /** Names the control. Required — the trigger's text is a value, not a label. */
13
+ label: string;
14
+ /** Shown on the trigger when nothing is chosen. */
15
+ placeholder?: string;
16
+ /** Placeholder inside the filter field. */
17
+ searchPlaceholder?: string;
18
+ /** Shown when the filter matches nothing. Say what WOULD match. */
19
+ emptyMessage?: string;
20
+ disabled?: boolean;
21
+ className?: string;
22
+ }
23
+ interface SingleProps extends CommonProps {
24
+ multiple?: false;
25
+ value?: string;
26
+ defaultValue?: string;
27
+ onValueChange?: (value: string) => void;
28
+ }
29
+ interface MultipleProps extends CommonProps {
30
+ /**
31
+ * Several at once. The trigger then summarises the choice rather than naming
32
+ * it — "3 selected" past a threshold, because a trigger that grows with its
33
+ * value reflows the form every time someone picks another one.
34
+ */
35
+ multiple: true;
36
+ value?: string[];
37
+ defaultValue?: string[];
38
+ onValueChange?: (value: string[]) => void;
39
+ }
40
+ type ComboboxProps = SingleProps | MultipleProps;
41
+ /**
42
+ * A select you can type into, choosing one or several.
43
+ *
44
+ * The line against `Select` is length, and it is not a matter of taste: a
45
+ * styled select is better up to roughly a dozen options, because a list nobody
46
+ * can filter is faster to scan than one they have to think about. Past that,
47
+ * this is the right answer.
48
+ *
49
+ * Filtering, the highlighted row and the arrow keys come from cmdk, which
50
+ * implements the ARIA combobox pattern properly: the highlight moves through
51
+ * `aria-activedescendant` while focus stays in the input. Hand-rolled comboboxes
52
+ * move focus into the list instead, and the typed text stops being editable.
53
+ *
54
+ * Controlled or uncontrolled, like every other form control here.
55
+ *
56
+ * @example
57
+ * <Combobox label="Framework" options={FRAMEWORKS} placeholder="Pick one" />
58
+ * @example
59
+ * <Combobox multiple label="Tags" options={TAGS} />
60
+ */
61
+ declare function Combobox(props: ComboboxProps): react.JSX.Element;
62
+
63
+ export { Combobox, type ComboboxOption, type ComboboxProps, Combobox as default };