@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,31 @@
1
+ import * as react from 'react';
2
+ import { HTMLAttributes, ReactNode } from 'react';
3
+ import { LucideIcon } from 'lucide-react';
4
+
5
+ interface EmptyStateProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'> {
6
+ /** Optional lucide icon, shown muted inside a quiet circle. */
7
+ icon?: LucideIcon;
8
+ title: ReactNode;
9
+ description?: ReactNode;
10
+ /** The one thing to do next. An empty state without an action is a dead end. */
11
+ action?: ReactNode;
12
+ }
13
+ /**
14
+ * A collection with nothing in it — yet.
15
+ *
16
+ * Distinct from `ErrorState`: nothing went wrong here, so the copy says what to
17
+ * do rather than what failed. The two are different components because the
18
+ * shared temptation — one component with a `variant` — produces error pages
19
+ * that read like empty folders.
20
+ *
21
+ * @example
22
+ * <EmptyState
23
+ * icon={Inbox}
24
+ * title="No projects yet"
25
+ * description="Create your first project to get started."
26
+ * action={<Button>New project</Button>}
27
+ * />
28
+ */
29
+ declare function EmptyState({ icon: Icon, title, description, action, className, ...rest }: EmptyStateProps): react.JSX.Element;
30
+
31
+ export { EmptyState, type EmptyStateProps, EmptyState as default };
@@ -0,0 +1,30 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { cn } from "../../lib/cn.js";
3
+ function EmptyState({
4
+ icon: Icon,
5
+ title,
6
+ description,
7
+ action,
8
+ className,
9
+ ...rest
10
+ }) {
11
+ return /* @__PURE__ */ jsxs(
12
+ "div",
13
+ {
14
+ className: cn("flex flex-col items-center px-(--page-pad) py-20 text-center", className),
15
+ ...rest,
16
+ children: [
17
+ Icon && /* @__PURE__ */ jsx("span", { className: "mb-6 flex size-14 items-center justify-center rounded-(--radius-pill) bg-(--stone) text-(--ink-3-aa)", children: /* @__PURE__ */ jsx(Icon, { size: 24, strokeWidth: 1.5, "aria-hidden": true }) }),
18
+ /* @__PURE__ */ jsx("h3", { className: "m-0 font-heading text-[length:var(--fs-sub)] font-normal text-(--ink)", children: title }),
19
+ description && /* @__PURE__ */ jsx("p", { className: "m-0 mt-2 max-w-sm text-sm leading-relaxed text-(--ink-3-aa)", children: description }),
20
+ action && /* @__PURE__ */ jsx("div", { className: "mt-8", children: action })
21
+ ]
22
+ }
23
+ );
24
+ }
25
+ var EmptyState_default = EmptyState;
26
+ export {
27
+ EmptyState,
28
+ EmptyState_default as default
29
+ };
30
+ //# sourceMappingURL=EmptyState.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/EmptyState/EmptyState.tsx"],"sourcesContent":["import type { LucideIcon } from 'lucide-react'\nimport type { HTMLAttributes, ReactNode } from 'react'\nimport { cn } from '../../lib/cn'\n\nexport interface EmptyStateProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'> {\n /** Optional lucide icon, shown muted inside a quiet circle. */\n icon?: LucideIcon\n title: ReactNode\n description?: ReactNode\n /** The one thing to do next. An empty state without an action is a dead end. */\n action?: ReactNode\n}\n\n/**\n * A collection with nothing in it — yet.\n *\n * Distinct from `ErrorState`: nothing went wrong here, so the copy says what to\n * do rather than what failed. The two are different components because the\n * shared temptation — one component with a `variant` — produces error pages\n * that read like empty folders.\n *\n * @example\n * <EmptyState\n * icon={Inbox}\n * title=\"No projects yet\"\n * description=\"Create your first project to get started.\"\n * action={<Button>New project</Button>}\n * />\n */\nexport function EmptyState({\n icon: Icon,\n title,\n description,\n action,\n className,\n ...rest\n}: EmptyStateProps) {\n return (\n <div\n className={cn('flex flex-col items-center px-(--page-pad) py-20 text-center', className)}\n {...rest}\n >\n {Icon && (\n <span className=\"mb-6 flex size-14 items-center justify-center rounded-(--radius-pill) bg-(--stone) text-(--ink-3-aa)\">\n <Icon size={24} strokeWidth={1.5} aria-hidden />\n </span>\n )}\n <h3 className=\"m-0 font-heading text-[length:var(--fs-sub)] font-normal text-(--ink)\">\n {title}\n </h3>\n {description && (\n <p className=\"m-0 mt-2 max-w-sm text-sm leading-relaxed text-(--ink-3-aa)\">{description}</p>\n )}\n {action && <div className=\"mt-8\">{action}</div>}\n </div>\n )\n}\n\nexport default EmptyState\n"],"mappings":"AAsCI,SAMM,KANN;AApCJ,SAAS,UAAU;AA2BZ,SAAS,WAAW;AAAA,EACzB,MAAM;AAAA,EACN;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAoB;AAClB,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW,GAAG,gEAAgE,SAAS;AAAA,MACtF,GAAG;AAAA,MAEH;AAAA,gBACC,oBAAC,UAAK,WAAU,wGACd,8BAAC,QAAK,MAAM,IAAI,aAAa,KAAK,eAAW,MAAC,GAChD;AAAA,QAEF,oBAAC,QAAG,WAAU,yEACX,iBACH;AAAA,QACC,eACC,oBAAC,OAAE,WAAU,+DAA+D,uBAAY;AAAA,QAEzF,UAAU,oBAAC,SAAI,WAAU,QAAQ,kBAAO;AAAA;AAAA;AAAA,EAC3C;AAEJ;AAEA,IAAO,qBAAQ;","names":[]}
@@ -0,0 +1,34 @@
1
+ import * as react from 'react';
2
+ import { HTMLAttributes, ReactNode } from 'react';
3
+
4
+ interface ErrorStateProps extends HTMLAttributes<HTMLElement> {
5
+ /** The status, set large — "404", "500". Decorative; the heading carries the meaning. */
6
+ code: ReactNode;
7
+ heading: ReactNode;
8
+ message: ReactNode;
9
+ /** The way back. Render it with {@link ERROR_ACTION_CLASS}. */
10
+ action: ReactNode;
11
+ }
12
+ /**
13
+ * The recovery affordance's look, exported so every error page offers the same
14
+ * one rather than each inventing a link style.
15
+ */
16
+ declare const ERROR_ACTION_CLASS = "text-sm text-(--ink) underline decoration-(--rule-2) underline-offset-4 transition-colors duration-(--duration-fast) hover:decoration-(--ink)";
17
+ /**
18
+ * A page that could not be shown.
19
+ *
20
+ * The code is set at the top of the type ladder and marked `aria-hidden` — read
21
+ * aloud, "404" before the sentence explaining it is noise, and the heading
22
+ * immediately after says the same thing in words.
23
+ *
24
+ * @example
25
+ * <ErrorState
26
+ * code="404"
27
+ * heading="Page not found"
28
+ * message="The page you're looking for has moved, or never existed."
29
+ * action={<a href="/" className={ERROR_ACTION_CLASS}>Back home</a>}
30
+ * />
31
+ */
32
+ declare function ErrorState({ code, heading, message, action, className, ...rest }: ErrorStateProps): react.JSX.Element;
33
+
34
+ export { ERROR_ACTION_CLASS, ErrorState, type ErrorStateProps, ErrorState as default };
@@ -0,0 +1,32 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { cn } from "../../lib/cn.js";
3
+ const ERROR_ACTION_CLASS = "text-sm text-(--ink) underline decoration-(--rule-2) underline-offset-4 transition-colors duration-(--duration-fast) hover:decoration-(--ink)";
4
+ function ErrorState({ code, heading, message, action, className, ...rest }) {
5
+ return /* @__PURE__ */ jsx(
6
+ "section",
7
+ {
8
+ className: cn("flex min-h-svh flex-col justify-center bg-(--paper) pt-24", className),
9
+ ...rest,
10
+ children: /* @__PURE__ */ jsxs("div", { className: "mx-auto w-full max-w-(--w-page) px-(--page-pad)", children: [
11
+ /* @__PURE__ */ jsx(
12
+ "p",
13
+ {
14
+ "aria-hidden": "true",
15
+ className: "m-0 font-heading text-[length:var(--fs-title)] leading-none text-(--ink)",
16
+ children: code
17
+ }
18
+ ),
19
+ /* @__PURE__ */ jsx("h1", { className: "mb-4 mt-6 font-heading text-[length:var(--fs-heading)] font-normal text-(--ink)", children: heading }),
20
+ /* @__PURE__ */ jsx("p", { className: "mb-10 max-w-(--measure-record) text-base leading-relaxed text-(--ink-3-aa)", children: message }),
21
+ action
22
+ ] })
23
+ }
24
+ );
25
+ }
26
+ var ErrorState_default = ErrorState;
27
+ export {
28
+ ERROR_ACTION_CLASS,
29
+ ErrorState,
30
+ ErrorState_default as default
31
+ };
32
+ //# sourceMappingURL=ErrorState.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/ErrorState/ErrorState.tsx"],"sourcesContent":["import type { HTMLAttributes, ReactNode } from 'react'\nimport { cn } from '../../lib/cn'\n\nexport interface ErrorStateProps extends HTMLAttributes<HTMLElement> {\n /** The status, set large — \"404\", \"500\". Decorative; the heading carries the meaning. */\n code: ReactNode\n heading: ReactNode\n message: ReactNode\n /** The way back. Render it with {@link ERROR_ACTION_CLASS}. */\n action: ReactNode\n}\n\n/**\n * The recovery affordance's look, exported so every error page offers the same\n * one rather than each inventing a link style.\n */\nexport const ERROR_ACTION_CLASS =\n 'text-sm text-(--ink) underline decoration-(--rule-2) underline-offset-4 transition-colors duration-(--duration-fast) hover:decoration-(--ink)'\n\n/**\n * A page that could not be shown.\n *\n * The code is set at the top of the type ladder and marked `aria-hidden` — read\n * aloud, \"404\" before the sentence explaining it is noise, and the heading\n * immediately after says the same thing in words.\n *\n * @example\n * <ErrorState\n * code=\"404\"\n * heading=\"Page not found\"\n * message=\"The page you're looking for has moved, or never existed.\"\n * action={<a href=\"/\" className={ERROR_ACTION_CLASS}>Back home</a>}\n * />\n */\nexport function ErrorState({ code, heading, message, action, className, ...rest }: ErrorStateProps) {\n return (\n <section\n className={cn('flex min-h-svh flex-col justify-center bg-(--paper) pt-24', className)}\n {...rest}\n >\n <div className=\"mx-auto w-full max-w-(--w-page) px-(--page-pad)\">\n <p\n aria-hidden=\"true\"\n className=\"m-0 font-heading text-[length:var(--fs-title)] leading-none text-(--ink)\"\n >\n {code}\n </p>\n <h1 className=\"mb-4 mt-6 font-heading text-[length:var(--fs-heading)] font-normal text-(--ink)\">\n {heading}\n </h1>\n <p className=\"mb-10 max-w-(--measure-record) text-base leading-relaxed text-(--ink-3-aa)\">\n {message}\n </p>\n {action}\n </div>\n </section>\n )\n}\n\nexport default ErrorState\n"],"mappings":"AAwCM,SACE,KADF;AAvCN,SAAS,UAAU;AAeZ,MAAM,qBACX;AAiBK,SAAS,WAAW,EAAE,MAAM,SAAS,SAAS,QAAQ,WAAW,GAAG,KAAK,GAAoB;AAClG,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW,GAAG,6DAA6D,SAAS;AAAA,MACnF,GAAG;AAAA,MAEJ,+BAAC,SAAI,WAAU,mDACb;AAAA;AAAA,UAAC;AAAA;AAAA,YACC,eAAY;AAAA,YACZ,WAAU;AAAA,YAET;AAAA;AAAA,QACH;AAAA,QACA,oBAAC,QAAG,WAAU,mFACX,mBACH;AAAA,QACA,oBAAC,OAAE,WAAU,8EACV,mBACH;AAAA,QACC;AAAA,SACH;AAAA;AAAA,EACF;AAEJ;AAEA,IAAO,qBAAQ;","names":[]}
@@ -0,0 +1,44 @@
1
+ import * as react from 'react';
2
+ import { HTMLAttributes, ReactNode } from 'react';
3
+
4
+ interface FieldProps extends HTMLAttributes<HTMLDivElement> {
5
+ /** Visible label text; renders a `--danger` asterisk when `required`. */
6
+ label?: ReactNode;
7
+ /**
8
+ * The control's `id`. Optional: when omitted, the field generates one and
9
+ * puts it on the control child itself, so the label still points at
10
+ * something. Pass it explicitly when the id has to be stable across renders
11
+ * — a form library referencing it by name, say.
12
+ */
13
+ htmlFor?: string;
14
+ /** Helper copy shown below the control when there is no `error`. */
15
+ hint?: ReactNode;
16
+ /** Validation message; takes precedence over `hint` when present. */
17
+ error?: ReactNode;
18
+ required?: boolean;
19
+ children: ReactNode;
20
+ }
21
+ /**
22
+ * A labelled form row: label, control, and the one message below it.
23
+ *
24
+ * The visible message is only half of accessible validation — it must also
25
+ * reach the control. This wires `aria-describedby`, `aria-required` and
26
+ * `aria-invalid` onto the single control child so the requirement and the error
27
+ * are announced, not merely drawn (WCAG 1.3.1 / 3.3.1 / 4.1.2).
28
+ *
29
+ * An earlier version derived the message id from `htmlFor`, which meant a
30
+ * caller who left `htmlFor` off got a hint that was rendered and never
31
+ * announced — the failure was invisible in the browser and total for a screen
32
+ * reader. The id is now generated when it is not supplied.
33
+ *
34
+ * `hint` and `error` are one slot, not two stacked messages: when a field is
35
+ * wrong, the thing to read is what is wrong with it.
36
+ *
37
+ * @example
38
+ * <Field label="Email" required hint="We never share it."><Input type="email" /></Field>
39
+ * @example
40
+ * <Field label="Name" error="Name is required."><Input /></Field>
41
+ */
42
+ declare function Field({ label, htmlFor, hint, error, required, children, className, ...rest }: FieldProps): react.JSX.Element;
43
+
44
+ export { Field, type FieldProps, Field as default };
@@ -0,0 +1,54 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { Label } from "@radix-ui/react-label";
4
+ import { cloneElement, isValidElement, useId } from "react";
5
+ import { cn } from "../../lib/cn.js";
6
+ function Field({
7
+ label,
8
+ htmlFor,
9
+ hint,
10
+ error,
11
+ required,
12
+ children,
13
+ className,
14
+ ...rest
15
+ }) {
16
+ const generatedId = useId();
17
+ const controlId = htmlFor ?? generatedId;
18
+ const message = error ?? hint;
19
+ const messageId = message != null ? `${controlId}-${error != null ? "error" : "hint"}` : void 0;
20
+ let control = children;
21
+ if (isValidElement(children)) {
22
+ const child = children;
23
+ control = cloneElement(child, {
24
+ id: child.props.id ?? controlId,
25
+ "aria-describedby": [child.props["aria-describedby"], messageId].filter(Boolean).join(" ") || void 0,
26
+ "aria-required": required || child.props["aria-required"] || void 0,
27
+ "aria-invalid": error != null ? child.props["aria-invalid"] ?? true : child.props["aria-invalid"]
28
+ });
29
+ }
30
+ return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-1.5", className), ...rest, children: [
31
+ label != null && /* @__PURE__ */ jsxs(Label, { htmlFor: controlId, className: "text-sm text-(--ink)", children: [
32
+ label,
33
+ required && /* @__PURE__ */ jsxs("span", { className: "text-(--danger)", "aria-hidden": "true", children: [
34
+ " ",
35
+ "*"
36
+ ] })
37
+ ] }),
38
+ control,
39
+ message != null && /* @__PURE__ */ jsx(
40
+ "p",
41
+ {
42
+ id: messageId,
43
+ className: cn("m-0 text-xs", error != null ? "text-(--danger)" : "text-(--ink-3-aa)"),
44
+ children: message
45
+ }
46
+ )
47
+ ] });
48
+ }
49
+ var Field_default = Field;
50
+ export {
51
+ Field,
52
+ Field_default as default
53
+ };
54
+ //# sourceMappingURL=Field.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/Field/Field.tsx"],"sourcesContent":["'use client'\n\nimport { Label } from '@radix-ui/react-label'\nimport { cloneElement, isValidElement, useId } from 'react'\nimport type { HTMLAttributes, ReactElement, ReactNode } from 'react'\nimport { cn } from '../../lib/cn'\n\nexport interface FieldProps extends HTMLAttributes<HTMLDivElement> {\n /** Visible label text; renders a `--danger` asterisk when `required`. */\n label?: ReactNode\n /**\n * The control's `id`. Optional: when omitted, the field generates one and\n * puts it on the control child itself, so the label still points at\n * something. Pass it explicitly when the id has to be stable across renders\n * — a form library referencing it by name, say.\n */\n htmlFor?: string\n /** Helper copy shown below the control when there is no `error`. */\n hint?: ReactNode\n /** Validation message; takes precedence over `hint` when present. */\n error?: ReactNode\n required?: boolean\n children: ReactNode\n}\n\ntype WirableControl = ReactElement<{\n id?: string\n 'aria-describedby'?: string\n 'aria-required'?: boolean\n 'aria-invalid'?: boolean | 'true' | 'false'\n}>\n\n/**\n * A labelled form row: label, control, and the one message below it.\n *\n * The visible message is only half of accessible validation — it must also\n * reach the control. This wires `aria-describedby`, `aria-required` and\n * `aria-invalid` onto the single control child so the requirement and the error\n * are announced, not merely drawn (WCAG 1.3.1 / 3.3.1 / 4.1.2).\n *\n * An earlier version derived the message id from `htmlFor`, which meant a\n * caller who left `htmlFor` off got a hint that was rendered and never\n * announced — the failure was invisible in the browser and total for a screen\n * reader. The id is now generated when it is not supplied.\n *\n * `hint` and `error` are one slot, not two stacked messages: when a field is\n * wrong, the thing to read is what is wrong with it.\n *\n * @example\n * <Field label=\"Email\" required hint=\"We never share it.\"><Input type=\"email\" /></Field>\n * @example\n * <Field label=\"Name\" error=\"Name is required.\"><Input /></Field>\n */\nexport function Field({\n label,\n htmlFor,\n hint,\n error,\n required,\n children,\n className,\n ...rest\n}: FieldProps) {\n const generatedId = useId()\n const controlId = htmlFor ?? generatedId\n const message = error ?? hint\n const messageId = message != null ? `${controlId}-${error != null ? 'error' : 'hint'}` : undefined\n\n let control = children\n if (isValidElement(children)) {\n const child = children as WirableControl\n control = cloneElement(child, {\n id: child.props.id ?? controlId,\n 'aria-describedby':\n [child.props['aria-describedby'], messageId].filter(Boolean).join(' ') || undefined,\n 'aria-required': required || child.props['aria-required'] || undefined,\n 'aria-invalid':\n error != null ? (child.props['aria-invalid'] ?? true) : child.props['aria-invalid'],\n })\n }\n\n return (\n <div className={cn('flex flex-col gap-1.5', className)} {...rest}>\n {label != null && (\n <Label htmlFor={controlId} className=\"text-sm text-(--ink)\">\n {label}\n {required && (\n <span className=\"text-(--danger)\" aria-hidden=\"true\">\n {' '}\n *\n </span>\n )}\n </Label>\n )}\n {control}\n {message != null && (\n <p\n id={messageId}\n className={cn('m-0 text-xs', error != null ? 'text-(--danger)' : 'text-(--ink-3-aa)')}\n >\n {message}\n </p>\n )}\n </div>\n )\n}\n\nexport default Field\n"],"mappings":";AAuFY,SASJ,KATI;AArFZ,SAAS,aAAa;AACtB,SAAS,cAAc,gBAAgB,aAAa;AAEpD,SAAS,UAAU;AAgDZ,SAAS,MAAM;AAAA,EACpB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAe;AACb,QAAM,cAAc,MAAM;AAC1B,QAAM,YAAY,WAAW;AAC7B,QAAM,UAAU,SAAS;AACzB,QAAM,YAAY,WAAW,OAAO,GAAG,SAAS,IAAI,SAAS,OAAO,UAAU,MAAM,KAAK;AAEzF,MAAI,UAAU;AACd,MAAI,eAAe,QAAQ,GAAG;AAC5B,UAAM,QAAQ;AACd,cAAU,aAAa,OAAO;AAAA,MAC5B,IAAI,MAAM,MAAM,MAAM;AAAA,MACtB,oBACE,CAAC,MAAM,MAAM,kBAAkB,GAAG,SAAS,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG,KAAK;AAAA,MAC5E,iBAAiB,YAAY,MAAM,MAAM,eAAe,KAAK;AAAA,MAC7D,gBACE,SAAS,OAAQ,MAAM,MAAM,cAAc,KAAK,OAAQ,MAAM,MAAM,cAAc;AAAA,IACtF,CAAC;AAAA,EACH;AAEA,SACE,qBAAC,SAAI,WAAW,GAAG,yBAAyB,SAAS,GAAI,GAAG,MACzD;AAAA,aAAS,QACR,qBAAC,SAAM,SAAS,WAAW,WAAU,wBAClC;AAAA;AAAA,MACA,YACC,qBAAC,UAAK,WAAU,mBAAkB,eAAY,QAC3C;AAAA;AAAA,QAAI;AAAA,SAEP;AAAA,OAEJ;AAAA,IAED;AAAA,IACA,WAAW,QACV;AAAA,MAAC;AAAA;AAAA,QACC,IAAI;AAAA,QACJ,WAAW,GAAG,eAAe,SAAS,OAAO,oBAAoB,mBAAmB;AAAA,QAEnF;AAAA;AAAA,IACH;AAAA,KAEJ;AAEJ;AAEA,IAAO,gBAAQ;","names":[]}
@@ -0,0 +1,51 @@
1
+ import * as react from 'react';
2
+ import { ReactNode, HTMLAttributes } from 'react';
3
+
4
+ interface Figure {
5
+ /** Stable key; also what a caller keys its own data by. */
6
+ id: string;
7
+ /** The mono kicker over the value. */
8
+ label: string;
9
+ /** The value itself, set in the serif at the band's scale. */
10
+ value: ReactNode;
11
+ /** The quiet line under it — a trend, a qualifier, a second fact. */
12
+ note?: ReactNode;
13
+ }
14
+ interface FigureBandProps extends Omit<HTMLAttributes<HTMLDListElement>, 'children'> {
15
+ figures: Figure[];
16
+ /**
17
+ * How large the value is set.
18
+ *
19
+ * `lead` is for a band that is the point of its page — a stats headline,
20
+ * where the numbers ARE the content. `sub` is for a band that supports the
21
+ * page around it, and keeps the ladder honest: a supporting figure must not
22
+ * be set at the same size as the page's own subject.
23
+ */
24
+ scale?: 'lead' | 'sub';
25
+ /** Names the band for assistive tech when it has no visible heading of its own. */
26
+ label?: string;
27
+ }
28
+ /**
29
+ * A row of counted facts, divided by hairlines and nothing else.
30
+ *
31
+ * A `<dl>`, because that is what this is: each cell is a term and its value,
32
+ * and a grid of `<div>`s tells a screen reader nothing about which number goes
33
+ * with which label.
34
+ *
35
+ * Each divider width names the cells that do NOT open a row rather than adding
36
+ * a rule and taking it back — an `undo` at equal specificity resolves on
37
+ * Tailwind's own sort order, which is not something a layout should depend on.
38
+ * Drawn this way, no edge is ever painted past the last column.
39
+ *
40
+ * @example
41
+ * <FigureBand
42
+ * label="At a glance"
43
+ * figures={[
44
+ * { id: 'posts', label: 'Posts', value: '48', note: '+6 this year' },
45
+ * { id: 'photos', label: 'Frames', value: '1,204' },
46
+ * ]}
47
+ * />
48
+ */
49
+ declare function FigureBand({ figures, scale, label, className, ...rest }: FigureBandProps): react.JSX.Element | null;
50
+
51
+ export { type Figure, FigureBand, type FigureBandProps, FigureBand as default };
@@ -0,0 +1,52 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { cn } from "../../lib/cn.js";
3
+ const VALUE_SCALE = {
4
+ lead: "text-[length:var(--fs-lead)] leading-[1.1] tracking-[-0.02em]",
5
+ sub: "text-[length:var(--fs-sub)] leading-[1.2]"
6
+ };
7
+ function FigureBand({
8
+ figures,
9
+ scale = "lead",
10
+ label,
11
+ className,
12
+ ...rest
13
+ }) {
14
+ if (figures.length === 0) return null;
15
+ return /* @__PURE__ */ jsx(
16
+ "dl",
17
+ {
18
+ "aria-label": label,
19
+ className: cn(
20
+ // A container, not a viewport reader: four figures across is a decision
21
+ // about how wide THIS band is, and it is wrong in a 390px frame inside a
22
+ // 1440px window.
23
+ // `w-full` is load-bearing: `@container` applies `contain: inline-size`,
24
+ // which computes the element's width WITHOUT looking at its contents.
25
+ // As a shrink-to-fit flex item that resolved to zero, and the band
26
+ // rendered as two 0px columns of overlapping text.
27
+ "@container m-0 w-full grid grid-cols-2 border-y border-(--rule) @5xl:grid-cols-4",
28
+ "[&>div]:border-(--rule) @max-5xl:[&>div:nth-child(even)]:border-s @max-5xl:[&>div:nth-child(n+3)]:border-t @5xl:[&>div:not(:first-child)]:border-s",
29
+ className
30
+ ),
31
+ ...rest,
32
+ children: figures.map((figure) => /* @__PURE__ */ jsxs(
33
+ "div",
34
+ {
35
+ className: "bg-transparent px-[clamp(1rem,calc(3*var(--fluid)),1.625rem)] py-[clamp(1.5rem,calc(4*var(--fluid)),2.25rem)]",
36
+ children: [
37
+ /* @__PURE__ */ jsx("dt", { className: "mb-3 eyebrow text-(--ink-3-aa)", children: figure.label }),
38
+ /* @__PURE__ */ jsx("dd", { className: cn("m-0 font-heading font-normal text-(--ink)", VALUE_SCALE[scale]), children: figure.value }),
39
+ figure.note !== void 0 && figure.note !== null && /* @__PURE__ */ jsx("dd", { className: "m-0 mt-2.5 font-sans text-[13px] font-light leading-[1.5] text-(--ink-3-aa)", children: figure.note })
40
+ ]
41
+ },
42
+ figure.id
43
+ ))
44
+ }
45
+ );
46
+ }
47
+ var FigureBand_default = FigureBand;
48
+ export {
49
+ FigureBand,
50
+ FigureBand_default as default
51
+ };
52
+ //# sourceMappingURL=FigureBand.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/FigureBand/FigureBand.tsx"],"sourcesContent":["import type { HTMLAttributes, ReactNode } from 'react'\nimport { cn } from '../../lib/cn'\n\nexport interface Figure {\n /** Stable key; also what a caller keys its own data by. */\n id: string\n /** The mono kicker over the value. */\n label: string\n /** The value itself, set in the serif at the band's scale. */\n value: ReactNode\n /** The quiet line under it — a trend, a qualifier, a second fact. */\n note?: ReactNode\n}\n\nexport interface FigureBandProps extends Omit<HTMLAttributes<HTMLDListElement>, 'children'> {\n figures: Figure[]\n /**\n * How large the value is set.\n *\n * `lead` is for a band that is the point of its page — a stats headline,\n * where the numbers ARE the content. `sub` is for a band that supports the\n * page around it, and keeps the ladder honest: a supporting figure must not\n * be set at the same size as the page's own subject.\n */\n scale?: 'lead' | 'sub'\n /** Names the band for assistive tech when it has no visible heading of its own. */\n label?: string\n}\n\nconst VALUE_SCALE = {\n lead: 'text-[length:var(--fs-lead)] leading-[1.1] tracking-[-0.02em]',\n sub: 'text-[length:var(--fs-sub)] leading-[1.2]',\n} as const\n\n/**\n * A row of counted facts, divided by hairlines and nothing else.\n *\n * A `<dl>`, because that is what this is: each cell is a term and its value,\n * and a grid of `<div>`s tells a screen reader nothing about which number goes\n * with which label.\n *\n * Each divider width names the cells that do NOT open a row rather than adding\n * a rule and taking it back — an `undo` at equal specificity resolves on\n * Tailwind's own sort order, which is not something a layout should depend on.\n * Drawn this way, no edge is ever painted past the last column.\n *\n * @example\n * <FigureBand\n * label=\"At a glance\"\n * figures={[\n * { id: 'posts', label: 'Posts', value: '48', note: '+6 this year' },\n * { id: 'photos', label: 'Frames', value: '1,204' },\n * ]}\n * />\n */\nexport function FigureBand({\n figures,\n scale = 'lead',\n label,\n className,\n ...rest\n}: FigureBandProps) {\n if (figures.length === 0) return null\n\n return (\n <dl\n aria-label={label}\n className={cn(\n // A container, not a viewport reader: four figures across is a decision\n // about how wide THIS band is, and it is wrong in a 390px frame inside a\n // 1440px window.\n // `w-full` is load-bearing: `@container` applies `contain: inline-size`,\n // which computes the element's width WITHOUT looking at its contents.\n // As a shrink-to-fit flex item that resolved to zero, and the band\n // rendered as two 0px columns of overlapping text.\n '@container m-0 w-full grid grid-cols-2 border-y border-(--rule) @5xl:grid-cols-4',\n '[&>div]:border-(--rule) @max-5xl:[&>div:nth-child(even)]:border-s @max-5xl:[&>div:nth-child(n+3)]:border-t @5xl:[&>div:not(:first-child)]:border-s',\n className,\n )}\n {...rest}\n >\n {figures.map((figure) => (\n <div\n key={figure.id}\n className=\"bg-transparent px-[clamp(1rem,calc(3*var(--fluid)),1.625rem)] py-[clamp(1.5rem,calc(4*var(--fluid)),2.25rem)]\"\n >\n <dt className=\"mb-3 eyebrow text-(--ink-3-aa)\">{figure.label}</dt>\n <dd className={cn('m-0 font-heading font-normal text-(--ink)', VALUE_SCALE[scale])}>\n {figure.value}\n </dd>\n {figure.note !== undefined && figure.note !== null && (\n <dd className=\"m-0 mt-2.5 font-sans text-[13px] font-light leading-[1.5] text-(--ink-3-aa)\">\n {figure.note}\n </dd>\n )}\n </div>\n ))}\n </dl>\n )\n}\n\nexport default FigureBand\n"],"mappings":"AAkFQ,SAIE,KAJF;AAjFR,SAAS,UAAU;AA4BnB,MAAM,cAAc;AAAA,EAClB,MAAM;AAAA,EACN,KAAK;AACP;AAuBO,SAAS,WAAW;AAAA,EACzB;AAAA,EACA,QAAQ;AAAA,EACR;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAoB;AAClB,MAAI,QAAQ,WAAW,EAAG,QAAO;AAEjC,SACE;AAAA,IAAC;AAAA;AAAA,MACC,cAAY;AAAA,MACZ,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAQT;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA,MAEH,kBAAQ,IAAI,CAAC,WACZ;AAAA,QAAC;AAAA;AAAA,UAEC,WAAU;AAAA,UAEV;AAAA,gCAAC,QAAG,WAAU,kCAAkC,iBAAO,OAAM;AAAA,YAC7D,oBAAC,QAAG,WAAW,GAAG,6CAA6C,YAAY,KAAK,CAAC,GAC9E,iBAAO,OACV;AAAA,YACC,OAAO,SAAS,UAAa,OAAO,SAAS,QAC5C,oBAAC,QAAG,WAAU,+EACX,iBAAO,MACV;AAAA;AAAA;AAAA,QAVG,OAAO;AAAA,MAYd,CACD;AAAA;AAAA,EACH;AAEJ;AAEA,IAAO,qBAAQ;","names":[]}
@@ -0,0 +1,29 @@
1
+ import * as react from 'react';
2
+ import { ButtonHTMLAttributes, ReactNode } from 'react';
3
+
4
+ interface FloatingIconButtonProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'children'> {
5
+ /**
6
+ * Which bottom corner the control is pinned to, in READING order —
7
+ * `end` is the right in English and the left in Arabic. Naming the sides
8
+ * `left`/`right` would have hard-coded one script's layout into the API.
9
+ */
10
+ position: 'start' | 'end';
11
+ /** Accessible name — the button has no visible text, so this is its only name. */
12
+ label: string;
13
+ children: ReactNode;
14
+ }
15
+ /**
16
+ * A round action pinned to a screen corner.
17
+ *
18
+ * A translucent paper ground with a backdrop blur rather than a drop shadow —
19
+ * the White Reset has no elevation ramp, so what separates the control from
20
+ * the page under it is the blur and the hairline, not a glow.
21
+ *
22
+ * @example
23
+ * <FloatingIconButton position="right" label="Back to top" onClick={scrollUp}>
24
+ * <ArrowUp size={16} />
25
+ * </FloatingIconButton>
26
+ */
27
+ declare function FloatingIconButton({ position, label, children, className, ...rest }: FloatingIconButtonProps): react.JSX.Element;
28
+
29
+ export { FloatingIconButton, type FloatingIconButtonProps, FloatingIconButton as default };
@@ -0,0 +1,32 @@
1
+ "use client";
2
+ import { jsx } from "react/jsx-runtime";
3
+ import { cn } from "../../lib/cn.js";
4
+ const POSITION_CLASS = { start: "bottom-20 start-6", end: "bottom-6 end-6" };
5
+ function FloatingIconButton({
6
+ position,
7
+ label,
8
+ children,
9
+ className,
10
+ ...rest
11
+ }) {
12
+ return /* @__PURE__ */ jsx(
13
+ "button",
14
+ {
15
+ type: "button",
16
+ "aria-label": label,
17
+ className: cn(
18
+ "fixed z-(--z-drawer) flex size-(--control-h-md) items-center justify-center rounded-(--radius-pill) border border-(--rule-2) bg-(--paper)/90 text-(--ink-2) backdrop-blur-sm transition-colors duration-(--duration-fast) hover:border-(--ink) hover:text-(--ink)",
19
+ POSITION_CLASS[position],
20
+ className
21
+ ),
22
+ ...rest,
23
+ children
24
+ }
25
+ );
26
+ }
27
+ var FloatingIconButton_default = FloatingIconButton;
28
+ export {
29
+ FloatingIconButton,
30
+ FloatingIconButton_default as default
31
+ };
32
+ //# sourceMappingURL=FloatingIconButton.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/FloatingIconButton/FloatingIconButton.tsx"],"sourcesContent":["'use client'\n\nimport type { ButtonHTMLAttributes, ReactNode } from 'react'\nimport { cn } from '../../lib/cn'\n\nexport interface FloatingIconButtonProps\n extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'children'> {\n /**\n * Which bottom corner the control is pinned to, in READING order —\n * `end` is the right in English and the left in Arabic. Naming the sides\n * `left`/`right` would have hard-coded one script's layout into the API.\n */\n position: 'start' | 'end'\n /** Accessible name — the button has no visible text, so this is its only name. */\n label: string\n children: ReactNode\n}\n\n/**\n * Full literal class strings per corner. Tailwind only generates classes it can\n * see verbatim in the source, so the side is never interpolated into a string.\n *\n * The start corner sits higher: browser and OS chrome (a download shelf, a\n * status toast) commonly occupies the corner where reading begins, and the\n * global scroll action keeps the far one.\n */\nconst POSITION_CLASS = { start: 'bottom-20 start-6', end: 'bottom-6 end-6' } as const\n\n/**\n * A round action pinned to a screen corner.\n *\n * A translucent paper ground with a backdrop blur rather than a drop shadow —\n * the White Reset has no elevation ramp, so what separates the control from\n * the page under it is the blur and the hairline, not a glow.\n *\n * @example\n * <FloatingIconButton position=\"right\" label=\"Back to top\" onClick={scrollUp}>\n * <ArrowUp size={16} />\n * </FloatingIconButton>\n */\nexport function FloatingIconButton({\n position,\n label,\n children,\n className,\n ...rest\n}: FloatingIconButtonProps) {\n return (\n <button\n type=\"button\"\n aria-label={label}\n className={cn(\n 'fixed z-(--z-drawer) flex size-(--control-h-md) items-center justify-center rounded-(--radius-pill) border border-(--rule-2) bg-(--paper)/90 text-(--ink-2) backdrop-blur-sm transition-colors duration-(--duration-fast) hover:border-(--ink) hover:text-(--ink)',\n POSITION_CLASS[position],\n className,\n )}\n {...rest}\n >\n {children}\n </button>\n )\n}\n\nexport default FloatingIconButton\n"],"mappings":";AAgDI;AA7CJ,SAAS,UAAU;AAuBnB,MAAM,iBAAiB,EAAE,OAAO,qBAAqB,KAAK,iBAAiB;AAcpE,SAAS,mBAAmB;AAAA,EACjC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAA4B;AAC1B,SACE;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,cAAY;AAAA,MACZ,WAAW;AAAA,QACT;AAAA,QACA,eAAe,QAAQ;AAAA,QACvB;AAAA,MACF;AAAA,MACC,GAAG;AAAA,MAEH;AAAA;AAAA,EACH;AAEJ;AAEA,IAAO,6BAAQ;","names":[]}
@@ -0,0 +1,22 @@
1
+ import * as react from 'react';
2
+ import { InputHTMLAttributes, Ref } from 'react';
3
+
4
+ interface InputProps extends InputHTMLAttributes<HTMLInputElement> {
5
+ /** Paints the resting border with `--danger` and reflects `aria-invalid`. */
6
+ invalid?: boolean;
7
+ ref?: Ref<HTMLInputElement>;
8
+ }
9
+ /**
10
+ * A single line of text entry.
11
+ *
12
+ * Shares `CONTROL_BASE` with Textarea and Select, so the three cannot drift
13
+ * apart. Pair with `Field` for the label, hint and error — an input with a
14
+ * placeholder and no label is not labelled, because the placeholder disappears
15
+ * the moment anyone types.
16
+ *
17
+ * @example
18
+ * <Field label="Email" htmlFor="email"><Input id="email" type="email" /></Field>
19
+ */
20
+ declare function Input({ invalid, className, ref, 'aria-invalid': ariaInvalid, ...rest }: InputProps): react.JSX.Element;
21
+
22
+ export { Input, type InputProps, Input as default };
@@ -0,0 +1,21 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { cn } from "../../lib/cn.js";
3
+ import { CONTROL_BASE, CONTROL_BORDER, isInvalid } from "../../lib/control.js";
4
+ function Input({ invalid, className, ref, "aria-invalid": ariaInvalid, ...rest }) {
5
+ const bad = isInvalid(invalid, ariaInvalid);
6
+ return /* @__PURE__ */ jsx(
7
+ "input",
8
+ {
9
+ ref,
10
+ "aria-invalid": ariaInvalid ?? (invalid ? true : void 0),
11
+ className: cn(CONTROL_BASE, bad ? CONTROL_BORDER.invalid : CONTROL_BORDER.resting, className),
12
+ ...rest
13
+ }
14
+ );
15
+ }
16
+ var Input_default = Input;
17
+ export {
18
+ Input,
19
+ Input_default as default
20
+ };
21
+ //# sourceMappingURL=Input.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/Input/Input.tsx"],"sourcesContent":["import type { InputHTMLAttributes, Ref } from 'react'\nimport { cn } from '../../lib/cn'\nimport { CONTROL_BASE, CONTROL_BORDER, isInvalid } from '../../lib/control'\n\nexport interface InputProps extends InputHTMLAttributes<HTMLInputElement> {\n /** Paints the resting border with `--danger` and reflects `aria-invalid`. */\n invalid?: boolean\n ref?: Ref<HTMLInputElement>\n}\n\n/**\n * A single line of text entry.\n *\n * Shares `CONTROL_BASE` with Textarea and Select, so the three cannot drift\n * apart. Pair with `Field` for the label, hint and error — an input with a\n * placeholder and no label is not labelled, because the placeholder disappears\n * the moment anyone types.\n *\n * @example\n * <Field label=\"Email\" htmlFor=\"email\"><Input id=\"email\" type=\"email\" /></Field>\n */\nexport function Input({ invalid, className, ref, 'aria-invalid': ariaInvalid, ...rest }: InputProps) {\n const bad = isInvalid(invalid, ariaInvalid)\n return (\n <input\n ref={ref}\n aria-invalid={ariaInvalid ?? (invalid ? true : undefined)}\n className={cn(CONTROL_BASE, bad ? CONTROL_BORDER.invalid : CONTROL_BORDER.resting, className)}\n {...rest}\n />\n )\n}\n\nexport default Input\n"],"mappings":"AAwBI;AAvBJ,SAAS,UAAU;AACnB,SAAS,cAAc,gBAAgB,iBAAiB;AAmBjD,SAAS,MAAM,EAAE,SAAS,WAAW,KAAK,gBAAgB,aAAa,GAAG,KAAK,GAAe;AACnG,QAAM,MAAM,UAAU,SAAS,WAAW;AAC1C,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,gBAAc,gBAAgB,UAAU,OAAO;AAAA,MAC/C,WAAW,GAAG,cAAc,MAAM,eAAe,UAAU,eAAe,SAAS,SAAS;AAAA,MAC3F,GAAG;AAAA;AAAA,EACN;AAEJ;AAEA,IAAO,gBAAQ;","names":[]}
@@ -0,0 +1,20 @@
1
+ import * as react from 'react';
2
+ import { HTMLAttributes, ReactNode } from 'react';
3
+
4
+ interface KbdProps extends HTMLAttributes<HTMLElement> {
5
+ children: ReactNode;
6
+ }
7
+ /**
8
+ * A key on a keyboard, set as one.
9
+ *
10
+ * Renders a `<kbd>`, which is the element that means this — a `<span>` styled
11
+ * to look like a key tells a screen reader nothing. Sized in `em` so it tracks
12
+ * whatever type it sits beside rather than fixing itself at one px value, which
13
+ * is how the same shortcut ended up three different sizes across a page.
14
+ *
15
+ * @example
16
+ * Press <Kbd>⌘</Kbd> <Kbd>K</Kbd> to open the palette.
17
+ */
18
+ declare function Kbd({ children, className, ...rest }: KbdProps): react.JSX.Element;
19
+
20
+ export { Kbd, type KbdProps, Kbd as default };
@@ -0,0 +1,21 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { cn } from "../../lib/cn.js";
3
+ function Kbd({ children, className, ...rest }) {
4
+ return /* @__PURE__ */ jsx(
5
+ "kbd",
6
+ {
7
+ className: cn(
8
+ "inline-flex min-w-[1.6em] items-center justify-center rounded-(--radius-sm) border border-(--rule-2) px-[0.4em] py-[0.1em] font-mono text-[0.8em] leading-[1.6] text-(--ink-2)",
9
+ className
10
+ ),
11
+ ...rest,
12
+ children
13
+ }
14
+ );
15
+ }
16
+ var Kbd_default = Kbd;
17
+ export {
18
+ Kbd,
19
+ Kbd_default as default
20
+ };
21
+ //# sourceMappingURL=Kbd.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/Kbd/Kbd.tsx"],"sourcesContent":["import type { HTMLAttributes, ReactNode } from 'react'\nimport { cn } from '../../lib/cn'\n\nexport interface KbdProps extends HTMLAttributes<HTMLElement> {\n children: ReactNode\n}\n\n/**\n * A key on a keyboard, set as one.\n *\n * Renders a `<kbd>`, which is the element that means this — a `<span>` styled\n * to look like a key tells a screen reader nothing. Sized in `em` so it tracks\n * whatever type it sits beside rather than fixing itself at one px value, which\n * is how the same shortcut ended up three different sizes across a page.\n *\n * @example\n * Press <Kbd>⌘</Kbd> <Kbd>K</Kbd> to open the palette.\n */\nexport function Kbd({ children, className, ...rest }: KbdProps) {\n return (\n <kbd\n className={cn(\n 'inline-flex min-w-[1.6em] items-center justify-center rounded-(--radius-sm) border border-(--rule-2) px-[0.4em] py-[0.1em] font-mono text-[0.8em] leading-[1.6] text-(--ink-2)',\n className,\n )}\n {...rest}\n >\n {children}\n </kbd>\n )\n}\n\nexport default Kbd\n"],"mappings":"AAoBI;AAnBJ,SAAS,UAAU;AAiBZ,SAAS,IAAI,EAAE,UAAU,WAAW,GAAG,KAAK,GAAa;AAC9D,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA,MAEH;AAAA;AAAA,EACH;AAEJ;AAEA,IAAO,cAAQ;","names":[]}
@@ -0,0 +1,24 @@
1
+ import * as react from 'react';
2
+ import { HTMLAttributes } from 'react';
3
+
4
+ /** The glyph itself, exported so a non-React surface can use the same mark. */
5
+ declare const EXTERNAL_LINK_ARROW = "\u2197";
6
+ type LinkArrowProps = HTMLAttributes<HTMLSpanElement>;
7
+ /**
8
+ * The mark on a link that leaves the page — outbound, or on to a full index.
9
+ *
10
+ * Call sites used to write a bare `↗` inside the link text, which inherited the
11
+ * link's own size (a 17px arrow beside a 17px word, competing with it) and was
12
+ * read aloud as "north east arrow" because nothing hid it.
13
+ *
14
+ * `inline-block` is load-bearing twice over: it stops an underlined parent
15
+ * drawing its rule through the glyph, and it is what `align` is measured
16
+ * against. Sized in `em`, so it tracks whatever type it sits beside; coloured
17
+ * against paper, so a call site on a reversed plate passes its own colour.
18
+ *
19
+ * @example
20
+ * <a href="https://example.com">Read the paper<LinkArrow /></a>
21
+ */
22
+ declare function LinkArrow({ className, ...rest }: LinkArrowProps): react.JSX.Element;
23
+
24
+ export { EXTERNAL_LINK_ARROW, LinkArrow, type LinkArrowProps, LinkArrow as default };
@@ -0,0 +1,24 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { cn } from "../../lib/cn.js";
3
+ const EXTERNAL_LINK_ARROW = "\u2197";
4
+ function LinkArrow({ className, ...rest }) {
5
+ return /* @__PURE__ */ jsx(
6
+ "span",
7
+ {
8
+ "aria-hidden": "true",
9
+ className: cn(
10
+ "ms-[0.22em] inline-block align-[0.28em] font-mono text-[0.68em] leading-none text-(--ink-3-aa)",
11
+ className
12
+ ),
13
+ ...rest,
14
+ children: EXTERNAL_LINK_ARROW
15
+ }
16
+ );
17
+ }
18
+ var LinkArrow_default = LinkArrow;
19
+ export {
20
+ EXTERNAL_LINK_ARROW,
21
+ LinkArrow,
22
+ LinkArrow_default as default
23
+ };
24
+ //# sourceMappingURL=LinkArrow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/LinkArrow/LinkArrow.tsx"],"sourcesContent":["import type { HTMLAttributes } from 'react'\nimport { cn } from '../../lib/cn'\n\n/** The glyph itself, exported so a non-React surface can use the same mark. */\nexport const EXTERNAL_LINK_ARROW = '↗'\n\nexport type LinkArrowProps = HTMLAttributes<HTMLSpanElement>\n\n/**\n * The mark on a link that leaves the page — outbound, or on to a full index.\n *\n * Call sites used to write a bare `↗` inside the link text, which inherited the\n * link's own size (a 17px arrow beside a 17px word, competing with it) and was\n * read aloud as \"north east arrow\" because nothing hid it.\n *\n * `inline-block` is load-bearing twice over: it stops an underlined parent\n * drawing its rule through the glyph, and it is what `align` is measured\n * against. Sized in `em`, so it tracks whatever type it sits beside; coloured\n * against paper, so a call site on a reversed plate passes its own colour.\n *\n * @example\n * <a href=\"https://example.com\">Read the paper<LinkArrow /></a>\n */\nexport function LinkArrow({ className, ...rest }: LinkArrowProps) {\n return (\n <span\n aria-hidden=\"true\"\n className={cn(\n 'ms-[0.22em] inline-block align-[0.28em] font-mono text-[0.68em] leading-none text-(--ink-3-aa)',\n className,\n )}\n {...rest}\n >\n {EXTERNAL_LINK_ARROW}\n </span>\n )\n}\n\nexport default LinkArrow\n"],"mappings":"AAyBI;AAxBJ,SAAS,UAAU;AAGZ,MAAM,sBAAsB;AAmB5B,SAAS,UAAU,EAAE,WAAW,GAAG,KAAK,GAAmB;AAChE,SACE;AAAA,IAAC;AAAA;AAAA,MACC,eAAY;AAAA,MACZ,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA,MAEH;AAAA;AAAA,EACH;AAEJ;AAEA,IAAO,oBAAQ;","names":[]}
@@ -0,0 +1,27 @@
1
+ import * as react from 'react';
2
+ import { SelectHTMLAttributes, Ref } from 'react';
3
+
4
+ interface NativeSelectProps extends SelectHTMLAttributes<HTMLSelectElement> {
5
+ /** Paints the resting border with `--danger` and reflects `aria-invalid`. */
6
+ invalid?: boolean;
7
+ ref?: Ref<HTMLSelectElement>;
8
+ }
9
+ /**
10
+ * A native `<select>`, restyled.
11
+ *
12
+ * The escape hatch, not the default — `Select` is the styled one. Reach for
13
+ * this where the platform's own picker is genuinely better: a very long list on
14
+ * a phone, a form that must work without JavaScript, a page where the last
15
+ * kilobyte matters. The browser gives typeahead and the mobile wheel for free,
16
+ * and those are real.
17
+ *
18
+ * What it cannot do is look like the rest of the system once open. The option
19
+ * list is drawn by the operating system, so it carries none of these tokens —
20
+ * which is exactly why it stopped being the default.
21
+ *
22
+ * @example
23
+ * <NativeSelect defaultValue="au"><option value="au">Australia</option></NativeSelect>
24
+ */
25
+ declare function NativeSelect({ invalid, className, ref, children, 'aria-invalid': ariaInvalid, ...rest }: NativeSelectProps): react.JSX.Element;
26
+
27
+ export { NativeSelect, type NativeSelectProps, NativeSelect as default };