@hideyukimori/nene2-ui 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +192 -0
  3. package/dist/data/DataTable.d.ts +32 -0
  4. package/dist/data/DataTable.js +17 -0
  5. package/dist/data/DataTable.js.map +1 -0
  6. package/dist/data/DetailList.d.ts +14 -0
  7. package/dist/data/DetailList.js +9 -0
  8. package/dist/data/DetailList.js.map +1 -0
  9. package/dist/data/Pagination.d.ts +26 -0
  10. package/dist/data/Pagination.js +20 -0
  11. package/dist/data/Pagination.js.map +1 -0
  12. package/dist/feedback/Badge.d.ts +14 -0
  13. package/dist/feedback/Badge.js +18 -0
  14. package/dist/feedback/Badge.js.map +1 -0
  15. package/dist/feedback/InlineAlert.d.ts +15 -0
  16. package/dist/feedback/InlineAlert.js +18 -0
  17. package/dist/feedback/InlineAlert.js.map +1 -0
  18. package/dist/feedback/ToastProvider.d.ts +31 -0
  19. package/dist/feedback/ToastProvider.js +56 -0
  20. package/dist/feedback/ToastProvider.js.map +1 -0
  21. package/dist/feedback/toast-context.d.ts +20 -0
  22. package/dist/feedback/toast-context.js +17 -0
  23. package/dist/feedback/toast-context.js.map +1 -0
  24. package/dist/forms/FormField.d.ts +47 -0
  25. package/dist/forms/FormField.js +25 -0
  26. package/dist/forms/FormField.js.map +1 -0
  27. package/dist/forms/field-context.d.ts +54 -0
  28. package/dist/forms/field-context.js +51 -0
  29. package/dist/forms/field-context.js.map +1 -0
  30. package/dist/index.d.ts +34 -0
  31. package/dist/index.js +47 -0
  32. package/dist/index.js.map +1 -0
  33. package/dist/layout/Box.d.ts +20 -0
  34. package/dist/layout/Box.js +17 -0
  35. package/dist/layout/Box.js.map +1 -0
  36. package/dist/layout/Card.d.ts +16 -0
  37. package/dist/layout/Card.js +13 -0
  38. package/dist/layout/Card.js.map +1 -0
  39. package/dist/layout/Grid.d.ts +15 -0
  40. package/dist/layout/Grid.js +7 -0
  41. package/dist/layout/Grid.js.map +1 -0
  42. package/dist/layout/PageHeader.d.ts +7 -0
  43. package/dist/layout/PageHeader.js +5 -0
  44. package/dist/layout/PageHeader.js.map +1 -0
  45. package/dist/layout/Section.d.ts +16 -0
  46. package/dist/layout/Section.js +14 -0
  47. package/dist/layout/Section.js.map +1 -0
  48. package/dist/layout/Stack.d.ts +20 -0
  49. package/dist/layout/Stack.js +24 -0
  50. package/dist/layout/Stack.js.map +1 -0
  51. package/dist/lib/cx.d.ts +12 -0
  52. package/dist/lib/cx.js +15 -0
  53. package/dist/lib/cx.js.map +1 -0
  54. package/dist/lib/source-probe.d.ts +28 -0
  55. package/dist/lib/source-probe.js +29 -0
  56. package/dist/lib/source-probe.js.map +1 -0
  57. package/dist/lib/spacing.d.ts +54 -0
  58. package/dist/lib/spacing.js +85 -0
  59. package/dist/lib/spacing.js.map +1 -0
  60. package/dist/lib/states.d.ts +44 -0
  61. package/dist/lib/states.js +45 -0
  62. package/dist/lib/states.js.map +1 -0
  63. package/dist/overlay/ConfirmDialog.d.ts +24 -0
  64. package/dist/overlay/ConfirmDialog.js +17 -0
  65. package/dist/overlay/ConfirmDialog.js.map +1 -0
  66. package/dist/overlay/Modal.d.ts +24 -0
  67. package/dist/overlay/Modal.js +47 -0
  68. package/dist/overlay/Modal.js.map +1 -0
  69. package/dist/primitives/Button.d.ts +6 -0
  70. package/dist/primitives/Button.js +13 -0
  71. package/dist/primitives/Button.js.map +1 -0
  72. package/dist/primitives/Checkbox.d.ts +14 -0
  73. package/dist/primitives/Checkbox.js +19 -0
  74. package/dist/primitives/Checkbox.js.map +1 -0
  75. package/dist/primitives/Icon.d.ts +38 -0
  76. package/dist/primitives/Icon.js +38 -0
  77. package/dist/primitives/Icon.js.map +1 -0
  78. package/dist/primitives/Input.d.ts +7 -0
  79. package/dist/primitives/Input.js +15 -0
  80. package/dist/primitives/Input.js.map +1 -0
  81. package/dist/primitives/Radio.d.ts +19 -0
  82. package/dist/primitives/Radio.js +20 -0
  83. package/dist/primitives/Radio.js.map +1 -0
  84. package/dist/primitives/Select.d.ts +9 -0
  85. package/dist/primitives/Select.js +15 -0
  86. package/dist/primitives/Select.js.map +1 -0
  87. package/dist/primitives/Spinner.d.ts +5 -0
  88. package/dist/primitives/Spinner.js +5 -0
  89. package/dist/primitives/Spinner.js.map +1 -0
  90. package/dist/primitives/Switch.d.ts +19 -0
  91. package/dist/primitives/Switch.js +19 -0
  92. package/dist/primitives/Switch.js.map +1 -0
  93. package/dist/primitives/Text.d.ts +7 -0
  94. package/dist/primitives/Text.js +6 -0
  95. package/dist/primitives/Text.js.map +1 -0
  96. package/dist/primitives/Textarea.d.ts +11 -0
  97. package/dist/primitives/Textarea.js +19 -0
  98. package/dist/primitives/Textarea.js.map +1 -0
  99. package/dist/states/EmptyState.d.ts +5 -0
  100. package/dist/states/EmptyState.js +5 -0
  101. package/dist/states/EmptyState.js.map +1 -0
  102. package/dist/states/ErrorState.d.ts +8 -0
  103. package/dist/states/ErrorState.js +6 -0
  104. package/dist/states/ErrorState.js.map +1 -0
  105. package/dist/states/LoadingState.d.ts +18 -0
  106. package/dist/states/LoadingState.js +19 -0
  107. package/dist/states/LoadingState.js.map +1 -0
  108. package/dist/theme/tokens.d.ts +19 -0
  109. package/dist/theme/tokens.js +20 -0
  110. package/dist/theme/tokens.js.map +1 -0
  111. package/package.json +41 -0
  112. package/themes/default.css +60 -0
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Resolve a responsive prop against one of the tables below.
3
+ * Returns a class string; an undefined value contributes nothing.
4
+ */
5
+ export function resolve(value, table) {
6
+ if (value === undefined)
7
+ return '';
8
+ if (typeof value !== 'object')
9
+ return table[String(value)]?.base ?? '';
10
+ return ['base', 'sm', 'md', 'lg']
11
+ .map((bp) => {
12
+ const v = value[bp];
13
+ return v === undefined ? '' : (table[String(v)]?.[bp] ?? '');
14
+ })
15
+ .filter(Boolean)
16
+ .join(' ');
17
+ }
18
+ /** `gap` on a flex or grid container. */
19
+ export const GAP = {
20
+ none: { base: 'gap-0', sm: 'sm:gap-0', md: 'md:gap-0', lg: 'lg:gap-0' },
21
+ '3xs': { base: 'gap-x-3xs', sm: 'sm:gap-x-3xs', md: 'md:gap-x-3xs', lg: 'lg:gap-x-3xs' },
22
+ '2xs': { base: 'gap-x-2xs', sm: 'sm:gap-x-2xs', md: 'md:gap-x-2xs', lg: 'lg:gap-x-2xs' },
23
+ xs: { base: 'gap-x-xs', sm: 'sm:gap-x-xs', md: 'md:gap-x-xs', lg: 'lg:gap-x-xs' },
24
+ sm: { base: 'gap-x-sm', sm: 'sm:gap-x-sm', md: 'md:gap-x-sm', lg: 'lg:gap-x-sm' },
25
+ md: { base: 'gap-x-md', sm: 'sm:gap-x-md', md: 'md:gap-x-md', lg: 'lg:gap-x-md' },
26
+ lg: { base: 'gap-x-lg', sm: 'sm:gap-x-lg', md: 'md:gap-x-lg', lg: 'lg:gap-x-lg' },
27
+ xl: { base: 'gap-x-xl', sm: 'sm:gap-x-xl', md: 'md:gap-x-xl', lg: 'lg:gap-x-xl' },
28
+ '2xl': { base: 'gap-x-2xl', sm: 'sm:gap-x-2xl', md: 'md:gap-x-2xl', lg: 'lg:gap-x-2xl' },
29
+ };
30
+ /** Padding on all four sides. */
31
+ export const PAD = {
32
+ none: { base: 'p-0', sm: 'sm:p-0', md: 'md:p-0', lg: 'lg:p-0' },
33
+ '3xs': { base: 'p-x-3xs', sm: 'sm:p-x-3xs', md: 'md:p-x-3xs', lg: 'lg:p-x-3xs' },
34
+ '2xs': { base: 'p-x-2xs', sm: 'sm:p-x-2xs', md: 'md:p-x-2xs', lg: 'lg:p-x-2xs' },
35
+ xs: { base: 'p-x-xs', sm: 'sm:p-x-xs', md: 'md:p-x-xs', lg: 'lg:p-x-xs' },
36
+ sm: { base: 'p-x-sm', sm: 'sm:p-x-sm', md: 'md:p-x-sm', lg: 'lg:p-x-sm' },
37
+ md: { base: 'p-x-md', sm: 'sm:p-x-md', md: 'md:p-x-md', lg: 'lg:p-x-md' },
38
+ lg: { base: 'p-x-lg', sm: 'sm:p-x-lg', md: 'md:p-x-lg', lg: 'lg:p-x-lg' },
39
+ xl: { base: 'p-x-xl', sm: 'sm:p-x-xl', md: 'md:p-x-xl', lg: 'lg:p-x-xl' },
40
+ '2xl': { base: 'p-x-2xl', sm: 'sm:p-x-2xl', md: 'md:p-x-2xl', lg: 'lg:p-x-2xl' },
41
+ };
42
+ /** Inline (left/right) padding. */
43
+ export const PAD_X = {
44
+ none: { base: 'px-0', sm: 'sm:px-0', md: 'md:px-0', lg: 'lg:px-0' },
45
+ '3xs': { base: 'px-x-3xs', sm: 'sm:px-x-3xs', md: 'md:px-x-3xs', lg: 'lg:px-x-3xs' },
46
+ '2xs': { base: 'px-x-2xs', sm: 'sm:px-x-2xs', md: 'md:px-x-2xs', lg: 'lg:px-x-2xs' },
47
+ xs: { base: 'px-x-xs', sm: 'sm:px-x-xs', md: 'md:px-x-xs', lg: 'lg:px-x-xs' },
48
+ sm: { base: 'px-x-sm', sm: 'sm:px-x-sm', md: 'md:px-x-sm', lg: 'lg:px-x-sm' },
49
+ md: { base: 'px-x-md', sm: 'sm:px-x-md', md: 'md:px-x-md', lg: 'lg:px-x-md' },
50
+ lg: { base: 'px-x-lg', sm: 'sm:px-x-lg', md: 'md:px-x-lg', lg: 'lg:px-x-lg' },
51
+ xl: { base: 'px-x-xl', sm: 'sm:px-x-xl', md: 'md:px-x-xl', lg: 'lg:px-x-xl' },
52
+ '2xl': { base: 'px-x-2xl', sm: 'sm:px-x-2xl', md: 'md:px-x-2xl', lg: 'lg:px-x-2xl' },
53
+ };
54
+ /** Block (top/bottom) padding. */
55
+ export const PAD_Y = {
56
+ none: { base: 'py-0', sm: 'sm:py-0', md: 'md:py-0', lg: 'lg:py-0' },
57
+ '3xs': { base: 'py-x-3xs', sm: 'sm:py-x-3xs', md: 'md:py-x-3xs', lg: 'lg:py-x-3xs' },
58
+ '2xs': { base: 'py-x-2xs', sm: 'sm:py-x-2xs', md: 'md:py-x-2xs', lg: 'lg:py-x-2xs' },
59
+ xs: { base: 'py-x-xs', sm: 'sm:py-x-xs', md: 'md:py-x-xs', lg: 'lg:py-x-xs' },
60
+ sm: { base: 'py-x-sm', sm: 'sm:py-x-sm', md: 'md:py-x-sm', lg: 'lg:py-x-sm' },
61
+ md: { base: 'py-x-md', sm: 'sm:py-x-md', md: 'md:py-x-md', lg: 'lg:py-x-md' },
62
+ lg: { base: 'py-x-lg', sm: 'sm:py-x-lg', md: 'md:py-x-lg', lg: 'lg:py-x-lg' },
63
+ xl: { base: 'py-x-xl', sm: 'sm:py-x-xl', md: 'md:py-x-xl', lg: 'lg:py-x-xl' },
64
+ '2xl': { base: 'py-x-2xl', sm: 'sm:py-x-2xl', md: 'md:py-x-2xl', lg: 'lg:py-x-2xl' },
65
+ };
66
+ /**
67
+ * Grid column counts. 1–12 rather than only the six the fleet currently uses
68
+ * (1, 2, 3, 4, 5, 7), because a column count is a structural fact about a layout,
69
+ * not a design value — capping it would only push callers back to `grid-cols-*`.
70
+ */
71
+ export const COLS = {
72
+ 1: { base: 'grid-cols-1', sm: 'sm:grid-cols-1', md: 'md:grid-cols-1', lg: 'lg:grid-cols-1' },
73
+ 2: { base: 'grid-cols-2', sm: 'sm:grid-cols-2', md: 'md:grid-cols-2', lg: 'lg:grid-cols-2' },
74
+ 3: { base: 'grid-cols-3', sm: 'sm:grid-cols-3', md: 'md:grid-cols-3', lg: 'lg:grid-cols-3' },
75
+ 4: { base: 'grid-cols-4', sm: 'sm:grid-cols-4', md: 'md:grid-cols-4', lg: 'lg:grid-cols-4' },
76
+ 5: { base: 'grid-cols-5', sm: 'sm:grid-cols-5', md: 'md:grid-cols-5', lg: 'lg:grid-cols-5' },
77
+ 6: { base: 'grid-cols-6', sm: 'sm:grid-cols-6', md: 'md:grid-cols-6', lg: 'lg:grid-cols-6' },
78
+ 7: { base: 'grid-cols-7', sm: 'sm:grid-cols-7', md: 'md:grid-cols-7', lg: 'lg:grid-cols-7' },
79
+ 8: { base: 'grid-cols-8', sm: 'sm:grid-cols-8', md: 'md:grid-cols-8', lg: 'lg:grid-cols-8' },
80
+ 9: { base: 'grid-cols-9', sm: 'sm:grid-cols-9', md: 'md:grid-cols-9', lg: 'lg:grid-cols-9' },
81
+ 10: { base: 'grid-cols-10', sm: 'sm:grid-cols-10', md: 'md:grid-cols-10', lg: 'lg:grid-cols-10' },
82
+ 11: { base: 'grid-cols-11', sm: 'sm:grid-cols-11', md: 'md:grid-cols-11', lg: 'lg:grid-cols-11' },
83
+ 12: { base: 'grid-cols-12', sm: 'sm:grid-cols-12', md: 'md:grid-cols-12', lg: 'lg:grid-cols-12' },
84
+ };
85
+ //# sourceMappingURL=spacing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spacing.js","sourceRoot":"","sources":["../../src/lib/spacing.ts"],"names":[],"mappings":"AA2BA;;;GAGG;AACH,MAAM,UAAU,OAAO,CACrB,KAAgC,EAChC,KAA+B;IAE/B,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,EAAE,CAAC;IACnC,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,IAAI,EAAE,CAAC;IACvE,OAAQ,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAW;SACzC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE;QACV,MAAM,CAAC,GAAG,KAAK,CAAC,EAAE,CAAC,CAAC;QACpB,OAAO,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;IAC/D,CAAC,CAAC;SACD,MAAM,CAAC,OAAO,CAAC;SACf,IAAI,CAAC,GAAG,CAAC,CAAC;AACf,CAAC;AAED,yCAAyC;AACzC,MAAM,CAAC,MAAM,GAAG,GAA4B;IAC1C,IAAI,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,UAAU,EAAE;IACvE,KAAK,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE,EAAE,cAAc,EAAE,EAAE,EAAE,cAAc,EAAE,EAAE,EAAE,cAAc,EAAE;IACxF,KAAK,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE,EAAE,cAAc,EAAE,EAAE,EAAE,cAAc,EAAE,EAAE,EAAE,cAAc,EAAE;IACxF,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,aAAa,EAAE,EAAE,EAAE,aAAa,EAAE,EAAE,EAAE,aAAa,EAAE;IACjF,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,aAAa,EAAE,EAAE,EAAE,aAAa,EAAE,EAAE,EAAE,aAAa,EAAE;IACjF,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,aAAa,EAAE,EAAE,EAAE,aAAa,EAAE,EAAE,EAAE,aAAa,EAAE;IACjF,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,aAAa,EAAE,EAAE,EAAE,aAAa,EAAE,EAAE,EAAE,aAAa,EAAE;IACjF,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,aAAa,EAAE,EAAE,EAAE,aAAa,EAAE,EAAE,EAAE,aAAa,EAAE;IACjF,KAAK,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE,EAAE,cAAc,EAAE,EAAE,EAAE,cAAc,EAAE,EAAE,EAAE,cAAc,EAAE;CACzF,CAAC;AAEF,iCAAiC;AACjC,MAAM,CAAC,MAAM,GAAG,GAA4B;IAC1C,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,QAAQ,EAAE;IAC/D,KAAK,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE,YAAY,EAAE;IAChF,KAAK,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE,YAAY,EAAE;IAChF,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,WAAW,EAAE;IACzE,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,WAAW,EAAE;IACzE,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,WAAW,EAAE;IACzE,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,WAAW,EAAE;IACzE,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,WAAW,EAAE;IACzE,KAAK,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE,YAAY,EAAE;CACjF,CAAC;AAEF,mCAAmC;AACnC,MAAM,CAAC,MAAM,KAAK,GAA4B;IAC5C,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,SAAS,EAAE;IACnE,KAAK,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,aAAa,EAAE,EAAE,EAAE,aAAa,EAAE,EAAE,EAAE,aAAa,EAAE;IACpF,KAAK,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,aAAa,EAAE,EAAE,EAAE,aAAa,EAAE,EAAE,EAAE,aAAa,EAAE;IACpF,EAAE,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE,YAAY,EAAE;IAC7E,EAAE,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE,YAAY,EAAE;IAC7E,EAAE,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE,YAAY,EAAE;IAC7E,EAAE,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE,YAAY,EAAE;IAC7E,EAAE,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE,YAAY,EAAE;IAC7E,KAAK,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,aAAa,EAAE,EAAE,EAAE,aAAa,EAAE,EAAE,EAAE,aAAa,EAAE;CACrF,CAAC;AAEF,kCAAkC;AAClC,MAAM,CAAC,MAAM,KAAK,GAA4B;IAC5C,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,SAAS,EAAE;IACnE,KAAK,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,aAAa,EAAE,EAAE,EAAE,aAAa,EAAE,EAAE,EAAE,aAAa,EAAE;IACpF,KAAK,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,aAAa,EAAE,EAAE,EAAE,aAAa,EAAE,EAAE,EAAE,aAAa,EAAE;IACpF,EAAE,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE,YAAY,EAAE;IAC7E,EAAE,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE,YAAY,EAAE;IAC7E,EAAE,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE,YAAY,EAAE;IAC7E,EAAE,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE,YAAY,EAAE;IAC7E,EAAE,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE,YAAY,EAAE;IAC7E,KAAK,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,aAAa,EAAE,EAAE,EAAE,aAAa,EAAE,EAAE,EAAE,aAAa,EAAE;CACrF,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,MAAM,IAAI,GAA6B;IAC5C,CAAC,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,EAAE,EAAE,gBAAgB,EAAE,EAAE,EAAE,gBAAgB,EAAE,EAAE,EAAE,gBAAgB,EAAE;IAC5F,CAAC,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,EAAE,EAAE,gBAAgB,EAAE,EAAE,EAAE,gBAAgB,EAAE,EAAE,EAAE,gBAAgB,EAAE;IAC5F,CAAC,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,EAAE,EAAE,gBAAgB,EAAE,EAAE,EAAE,gBAAgB,EAAE,EAAE,EAAE,gBAAgB,EAAE;IAC5F,CAAC,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,EAAE,EAAE,gBAAgB,EAAE,EAAE,EAAE,gBAAgB,EAAE,EAAE,EAAE,gBAAgB,EAAE;IAC5F,CAAC,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,EAAE,EAAE,gBAAgB,EAAE,EAAE,EAAE,gBAAgB,EAAE,EAAE,EAAE,gBAAgB,EAAE;IAC5F,CAAC,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,EAAE,EAAE,gBAAgB,EAAE,EAAE,EAAE,gBAAgB,EAAE,EAAE,EAAE,gBAAgB,EAAE;IAC5F,CAAC,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,EAAE,EAAE,gBAAgB,EAAE,EAAE,EAAE,gBAAgB,EAAE,EAAE,EAAE,gBAAgB,EAAE;IAC5F,CAAC,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,EAAE,EAAE,gBAAgB,EAAE,EAAE,EAAE,gBAAgB,EAAE,EAAE,EAAE,gBAAgB,EAAE;IAC5F,CAAC,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,EAAE,EAAE,gBAAgB,EAAE,EAAE,EAAE,gBAAgB,EAAE,EAAE,EAAE,gBAAgB,EAAE;IAC5F,EAAE,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE,EAAE,EAAE,iBAAiB,EAAE,EAAE,EAAE,iBAAiB,EAAE,EAAE,EAAE,iBAAiB,EAAE;IACjG,EAAE,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE,EAAE,EAAE,iBAAiB,EAAE,EAAE,EAAE,iBAAiB,EAAE,EAAE,EAAE,iBAAiB,EAAE;IACjG,EAAE,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE,EAAE,EAAE,iBAAiB,EAAE,EAAE,EAAE,iBAAiB,EAAE,EAAE,EAAE,iBAAiB,EAAE;CAClG,CAAC"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Interaction states shared by every control in the kit (#300).
3
+ *
4
+ * 🔴 Why this exists at all: v0.1 shipped `Button`, `Input` and `Select` with **no disabled
5
+ * and no focus styling whatsoever**. The screens that used them wrote `disabled:opacity-50`
6
+ * and `focus:ring-1` themselves — 39 times across the fleet. That was never a deviation to
7
+ * be linted away; it was products patching a hole the kit had left open. Ban those classes
8
+ * before filling the hole and you ship a button that is disabled but does not look it.
9
+ *
10
+ * 🔴 `focus-visible`, not `focus`. `:focus` also fires on a mouse click, leaving a ring
11
+ * behind after every press — which is exactly why the fleet's screens ended up carrying
12
+ * both `focus:` (11 uses) and `focus-visible:` (5). The kit picks one, and picks the one
13
+ * that only shows the ring to keyboard users.
14
+ *
15
+ * 🔴 `outline`, not `ring`. Tailwind's `ring-*` is a box-shadow, so a parent with
16
+ * `overflow-hidden` clips it away entirely. This kit's own `Card` does not clip — but the
17
+ * products' existing containers do: `overflow-hidden` appears 12 times in nene-records and
18
+ * twice each in nene-vault and nene-deal, which are the first two ships to be migrated
19
+ * (measured 2026-08-23). `outline` is not clipped and carries its own offset.
20
+ *
21
+ * 🔴 The ring is `text-primary`, not `accent`. An accent ring is *the same colour as a
22
+ * primary button* — contrast 1.00:1 against its own fill, and 1.11:1 against a danger
23
+ * button (computed from the theme's oklch values, 2026-08-23). The offset keeps the ring
24
+ * off the fill, so what it actually sits against is the page, and both colours clear 3:1
25
+ * there; but a ring that disappears the moment the offset does is one layout change away
26
+ * from being invisible. `text-primary` measures 15.55:1 against `surface`, 16.00:1 against
27
+ * `surface-raised`, and still 3.30:1 / 2.97:1 against the accent and danger fills.
28
+ *
29
+ * No single colour clears 3:1 against *both* fills — accent and danger are both mid
30
+ * luminance — which is why nene-deal moved to a two-tone box-shadow ring (#183, after a
31
+ * 1.38:1 indicator went unnoticed because a contrast checker cannot see it). Here the
32
+ * offset provides the second tone: fill, page-coloured gap, dark ring, page.
33
+ *
34
+ * 🔴 `outline-offset` is therefore load-bearing, not decoration. Do not set it to 0.
35
+ */
36
+ /** Keyboard focus indicator. Colour comes from the theme, never from a caller. */
37
+ export declare const FOCUS_CLASS = "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-text-primary";
38
+ /**
39
+ * Disabled appearance. The opacity is a theme token (`--opacity-x-disabled`) because a
40
+ * literal here would be a design value living outside themes/default.css.
41
+ */
42
+ export declare const DISABLED_CLASS = "disabled:opacity-x-disabled disabled:cursor-not-allowed";
43
+ /** Controls that can be focused and disabled — every interactive primitive in the kit. */
44
+ export declare const CONTROL_CLASS = "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-text-primary disabled:opacity-x-disabled disabled:cursor-not-allowed";
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Interaction states shared by every control in the kit (#300).
3
+ *
4
+ * 🔴 Why this exists at all: v0.1 shipped `Button`, `Input` and `Select` with **no disabled
5
+ * and no focus styling whatsoever**. The screens that used them wrote `disabled:opacity-50`
6
+ * and `focus:ring-1` themselves — 39 times across the fleet. That was never a deviation to
7
+ * be linted away; it was products patching a hole the kit had left open. Ban those classes
8
+ * before filling the hole and you ship a button that is disabled but does not look it.
9
+ *
10
+ * 🔴 `focus-visible`, not `focus`. `:focus` also fires on a mouse click, leaving a ring
11
+ * behind after every press — which is exactly why the fleet's screens ended up carrying
12
+ * both `focus:` (11 uses) and `focus-visible:` (5). The kit picks one, and picks the one
13
+ * that only shows the ring to keyboard users.
14
+ *
15
+ * 🔴 `outline`, not `ring`. Tailwind's `ring-*` is a box-shadow, so a parent with
16
+ * `overflow-hidden` clips it away entirely. This kit's own `Card` does not clip — but the
17
+ * products' existing containers do: `overflow-hidden` appears 12 times in nene-records and
18
+ * twice each in nene-vault and nene-deal, which are the first two ships to be migrated
19
+ * (measured 2026-08-23). `outline` is not clipped and carries its own offset.
20
+ *
21
+ * 🔴 The ring is `text-primary`, not `accent`. An accent ring is *the same colour as a
22
+ * primary button* — contrast 1.00:1 against its own fill, and 1.11:1 against a danger
23
+ * button (computed from the theme's oklch values, 2026-08-23). The offset keeps the ring
24
+ * off the fill, so what it actually sits against is the page, and both colours clear 3:1
25
+ * there; but a ring that disappears the moment the offset does is one layout change away
26
+ * from being invisible. `text-primary` measures 15.55:1 against `surface`, 16.00:1 against
27
+ * `surface-raised`, and still 3.30:1 / 2.97:1 against the accent and danger fills.
28
+ *
29
+ * No single colour clears 3:1 against *both* fills — accent and danger are both mid
30
+ * luminance — which is why nene-deal moved to a two-tone box-shadow ring (#183, after a
31
+ * 1.38:1 indicator went unnoticed because a contrast checker cannot see it). Here the
32
+ * offset provides the second tone: fill, page-coloured gap, dark ring, page.
33
+ *
34
+ * 🔴 `outline-offset` is therefore load-bearing, not decoration. Do not set it to 0.
35
+ */
36
+ /** Keyboard focus indicator. Colour comes from the theme, never from a caller. */
37
+ export const FOCUS_CLASS = 'focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-text-primary';
38
+ /**
39
+ * Disabled appearance. The opacity is a theme token (`--opacity-x-disabled`) because a
40
+ * literal here would be a design value living outside themes/default.css.
41
+ */
42
+ export const DISABLED_CLASS = 'disabled:opacity-x-disabled disabled:cursor-not-allowed';
43
+ /** Controls that can be focused and disabled — every interactive primitive in the kit. */
44
+ export const CONTROL_CLASS = `${FOCUS_CLASS} ${DISABLED_CLASS}`;
45
+ //# sourceMappingURL=states.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"states.js","sourceRoot":"","sources":["../../src/lib/states.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,kFAAkF;AAClF,MAAM,CAAC,MAAM,WAAW,GACtB,2FAA2F,CAAC;AAE9F;;;GAGG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,yDAAyD,CAAC;AAExF,0FAA0F;AAC1F,MAAM,CAAC,MAAM,aAAa,GAAG,GAAG,WAAW,IAAI,cAAc,EAAE,CAAC"}
@@ -0,0 +1,24 @@
1
+ export interface ConfirmDialogProps {
2
+ open: boolean;
3
+ /** Localized title. */
4
+ title: string;
5
+ /** Localized body. */
6
+ message: string;
7
+ /** Localized label for the affirmative control. */
8
+ confirmLabel: string;
9
+ /** Localized label for the dismissive control. */
10
+ cancelLabel: string;
11
+ onConfirm: () => void;
12
+ onCancel: () => void;
13
+ /** `danger` for destructive confirmations. Chooses a variant, never a colour. */
14
+ tone?: 'default' | 'danger';
15
+ }
16
+ /**
17
+ * "Are you sure?" — the same dialog five ships wrote separately.
18
+ *
19
+ * 🔴 Cancel comes first in the DOM. It is the safe choice, so it is the one that should be
20
+ * reached first by keyboard and the one focus lands on when the dialog opens. A destructive
21
+ * confirmation whose dangerous button is the default is a footgun, and it is exactly the
22
+ * kind of detail that differs between five independent implementations.
23
+ */
24
+ export declare function ConfirmDialog({ open, title, message, confirmLabel, cancelLabel, onConfirm, onCancel, tone, }: ConfirmDialogProps): import("react").JSX.Element;
@@ -0,0 +1,17 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Button } from '../primitives/Button.js';
3
+ import { Stack } from '../layout/Stack.js';
4
+ import { Text } from '../primitives/Text.js';
5
+ import { Modal } from './Modal.js';
6
+ /**
7
+ * "Are you sure?" — the same dialog five ships wrote separately.
8
+ *
9
+ * 🔴 Cancel comes first in the DOM. It is the safe choice, so it is the one that should be
10
+ * reached first by keyboard and the one focus lands on when the dialog opens. A destructive
11
+ * confirmation whose dangerous button is the default is a footgun, and it is exactly the
12
+ * kind of detail that differs between five independent implementations.
13
+ */
14
+ export function ConfirmDialog({ open, title, message, confirmLabel, cancelLabel, onConfirm, onCancel, tone = 'default', }) {
15
+ return (_jsx(Modal, { open: open, onClose: onCancel, title: title, children: _jsxs(Stack, { gap: "xs", children: [_jsx(Text, { children: message }), _jsxs(Stack, { direction: "horizontal", gap: "2xs", justify: "end", children: [_jsx(Button, { variant: "secondary", onClick: onCancel, children: cancelLabel }), _jsx(Button, { variant: tone === 'danger' ? 'danger' : 'primary', onClick: onConfirm, children: confirmLabel })] })] }) }));
16
+ }
17
+ //# sourceMappingURL=ConfirmDialog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ConfirmDialog.js","sourceRoot":"","sources":["../../src/overlay/ConfirmDialog.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACjD,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAC7C,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAkBnC;;;;;;;GAOG;AACH,MAAM,UAAU,aAAa,CAAC,EAC5B,IAAI,EACJ,KAAK,EACL,OAAO,EACP,YAAY,EACZ,WAAW,EACX,SAAS,EACT,QAAQ,EACR,IAAI,GAAG,SAAS,GACG;IACnB,OAAO,CACL,KAAC,KAAK,IAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,YAChD,MAAC,KAAK,IAAC,GAAG,EAAC,IAAI,aACb,KAAC,IAAI,cAAE,OAAO,GAAQ,EACtB,MAAC,KAAK,IAAC,SAAS,EAAC,YAAY,EAAC,GAAG,EAAC,KAAK,EAAC,OAAO,EAAC,KAAK,aACnD,KAAC,MAAM,IAAC,OAAO,EAAC,WAAW,EAAC,OAAO,EAAE,QAAQ,YAC1C,WAAW,GACL,EACT,KAAC,MAAM,IAAC,OAAO,EAAE,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,EAAE,OAAO,EAAE,SAAS,YAC1E,YAAY,GACN,IACH,IACF,GACF,CACT,CAAC;AACJ,CAAC"}
@@ -0,0 +1,24 @@
1
+ import { type ReactNode } from 'react';
2
+ export interface ModalProps {
3
+ open: boolean;
4
+ /** Called for every dismissal the browser owns: Esc, the close control, the backdrop. */
5
+ onClose: () => void;
6
+ /** Localized title. Also names the dialog for assistive tech. */
7
+ title: string;
8
+ children: ReactNode;
9
+ }
10
+ /**
11
+ * A modal dialog built on the native `<dialog>` element.
12
+ *
13
+ * 🔴 Why native. Focus trapping, Esc-to-dismiss, the backdrop, and rendering above every
14
+ * stacking context are all things the browser already does correctly. Eight ships wrote
15
+ * their own modal, which means the fleet currently maintains eight focus traps — the single
16
+ * hardest piece of interaction code to get right, reimplemented per product.
17
+ *
18
+ * 🔴 `showModal()` is called imperatively rather than through the `open` attribute, because
19
+ * only `showModal()` puts the dialog in the top layer and traps focus; setting `open`
20
+ * renders it inline and non-modal. jsdom 25.0.1 does not implement `showModal` at all
21
+ * (measured 2026-08-23), and neither do browsers older than the feature, so the fallback
22
+ * sets `open` — degraded but visible, rather than a dialog that never appears.
23
+ */
24
+ export declare function Modal({ open, onClose, title, children }: ModalProps): import("react").JSX.Element;
@@ -0,0 +1,47 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useEffect, useRef } from 'react';
3
+ import { cx } from '../lib/cx.js';
4
+ /**
5
+ * A modal dialog built on the native `<dialog>` element.
6
+ *
7
+ * 🔴 Why native. Focus trapping, Esc-to-dismiss, the backdrop, and rendering above every
8
+ * stacking context are all things the browser already does correctly. Eight ships wrote
9
+ * their own modal, which means the fleet currently maintains eight focus traps — the single
10
+ * hardest piece of interaction code to get right, reimplemented per product.
11
+ *
12
+ * 🔴 `showModal()` is called imperatively rather than through the `open` attribute, because
13
+ * only `showModal()` puts the dialog in the top layer and traps focus; setting `open`
14
+ * renders it inline and non-modal. jsdom 25.0.1 does not implement `showModal` at all
15
+ * (measured 2026-08-23), and neither do browsers older than the feature, so the fallback
16
+ * sets `open` — degraded but visible, rather than a dialog that never appears.
17
+ */
18
+ export function Modal({ open, onClose, title, children }) {
19
+ const ref = useRef(null);
20
+ useEffect(() => {
21
+ const el = ref.current;
22
+ if (el === null)
23
+ return;
24
+ if (open) {
25
+ if (typeof el.showModal === 'function') {
26
+ if (!el.open)
27
+ el.showModal();
28
+ }
29
+ else {
30
+ el.setAttribute('open', '');
31
+ }
32
+ return;
33
+ }
34
+ if (typeof el.close === 'function') {
35
+ if (el.open)
36
+ el.close();
37
+ }
38
+ else {
39
+ el.removeAttribute('open');
40
+ }
41
+ }, [open]);
42
+ return (_jsx("dialog", { ref: ref, "aria-label": title,
43
+ // The browser fires `close` for Esc as well as for close(); routing both through the
44
+ // caller keeps `open` from drifting out of sync with what is on screen.
45
+ onClose: onClose, onCancel: onClose, className: cx('bg-surface-raised text-text-primary border border-border rounded-x-md', 'p-x-sm font-sans backdrop:bg-text-primary/50'), children: children }));
46
+ }
47
+ //# sourceMappingURL=Modal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Modal.js","sourceRoot":"","sources":["../../src/overlay/Modal.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,EAAkB,MAAM,OAAO,CAAC;AAC1D,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AAWlC;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,KAAK,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAc;IAClE,MAAM,GAAG,GAAG,MAAM,CAAoB,IAAI,CAAC,CAAC;IAE5C,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,EAAE,GAAG,GAAG,CAAC,OAAO,CAAC;QACvB,IAAI,EAAE,KAAK,IAAI;YAAE,OAAO;QAExB,IAAI,IAAI,EAAE,CAAC;YACT,IAAI,OAAO,EAAE,CAAC,SAAS,KAAK,UAAU,EAAE,CAAC;gBACvC,IAAI,CAAC,EAAE,CAAC,IAAI;oBAAE,EAAE,CAAC,SAAS,EAAE,CAAC;YAC/B,CAAC;iBAAM,CAAC;gBACN,EAAE,CAAC,YAAY,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;YAC9B,CAAC;YACD,OAAO;QACT,CAAC;QAED,IAAI,OAAO,EAAE,CAAC,KAAK,KAAK,UAAU,EAAE,CAAC;YACnC,IAAI,EAAE,CAAC,IAAI;gBAAE,EAAE,CAAC,KAAK,EAAE,CAAC;QAC1B,CAAC;aAAM,CAAC;YACN,EAAE,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;QAC7B,CAAC;IACH,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;IAEX,OAAO,CACL,iBACE,GAAG,EAAE,GAAG,gBACI,KAAK;QACjB,qFAAqF;QACrF,wEAAwE;QACxE,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,OAAO,EACjB,SAAS,EAAE,EAAE,CACX,uEAAuE,EACvE,8CAA8C,CAC/C,YAEA,QAAQ,GACF,CACV,CAAC;AACJ,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { ButtonHTMLAttributes, ReactNode } from 'react';
2
+ export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
3
+ variant?: 'primary' | 'secondary' | 'danger';
4
+ children: ReactNode;
5
+ }
6
+ export declare function Button({ variant, children, type, className, ...rest }: ButtonProps): import("react").JSX.Element;
@@ -0,0 +1,13 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { cx } from '../lib/cx.js';
3
+ import { CONTROL_CLASS } from '../lib/states.js';
4
+ const VARIANT_CLASS = {
5
+ primary: 'bg-accent text-on-accent',
6
+ secondary: 'bg-surface-raised text-text-primary border border-border',
7
+ danger: 'bg-danger text-on-accent',
8
+ };
9
+ const BASE_CLASS = `rounded-x-md px-x-inline-md py-x-stack-sm font-sans font-medium ${CONTROL_CLASS}`;
10
+ export function Button({ variant = 'primary', children, type = 'button', className, ...rest }) {
11
+ return (_jsx("button", { type: type, className: cx(BASE_CLASS, VARIANT_CLASS[variant], className), ...rest, children: children }));
12
+ }
13
+ //# sourceMappingURL=Button.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Button.js","sourceRoot":"","sources":["../../src/primitives/Button.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AAClC,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAOjD,MAAM,aAAa,GAAwD;IACzE,OAAO,EAAE,0BAA0B;IACnC,SAAS,EAAE,0DAA0D;IACrE,MAAM,EAAE,0BAA0B;CACnC,CAAC;AAEF,MAAM,UAAU,GAAG,mEAAmE,aAAa,EAAE,CAAC;AAEtG,MAAM,UAAU,MAAM,CAAC,EACrB,OAAO,GAAG,SAAS,EACnB,QAAQ,EACR,IAAI,GAAG,QAAQ,EACf,SAAS,EACT,GAAG,IAAI,EACK;IACZ,OAAO,CACL,iBAAQ,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,CAAC,UAAU,EAAE,aAAa,CAAC,OAAO,CAAC,EAAE,SAAS,CAAC,KAAM,IAAI,YACvF,QAAQ,GACF,CACV,CAAC;AACJ,CAAC"}
@@ -0,0 +1,14 @@
1
+ import { type InputHTMLAttributes, type ReactNode } from 'react';
2
+ export interface CheckboxProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'type'> {
3
+ /** Localized label. Rendered inside the control's own `<label>`. */
4
+ label: ReactNode;
5
+ }
6
+ /**
7
+ * A checkbox and its label, as one part.
8
+ *
9
+ * 🔴 The label is not optional and not a sibling. The recurring shape in the fleet is an
10
+ * `<input>` next to a `<label>` whose `htmlFor` is missing or stale — which loses nothing
11
+ * visible, and loses the ability to click the text to toggle the box, plus the name the
12
+ * control is announced by. Making the label a prop means it cannot be forgotten.
13
+ */
14
+ export declare const Checkbox: import("react").ForwardRefExoticComponent<CheckboxProps & import("react").RefAttributes<HTMLInputElement>>;
@@ -0,0 +1,19 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { forwardRef } from 'react';
3
+ import { cx } from '../lib/cx.js';
4
+ import { CONTROL_CLASS } from '../lib/states.js';
5
+ import { useFieldWiring } from '../forms/field-context.js';
6
+ const BOX_CLASS = `rounded-x-md border border-border accent-accent ${CONTROL_CLASS}`;
7
+ /**
8
+ * A checkbox and its label, as one part.
9
+ *
10
+ * 🔴 The label is not optional and not a sibling. The recurring shape in the fleet is an
11
+ * `<input>` next to a `<label>` whose `htmlFor` is missing or stale — which loses nothing
12
+ * visible, and loses the ability to click the text to toggle the box, plus the name the
13
+ * control is announced by. Making the label a prop means it cannot be forgotten.
14
+ */
15
+ export const Checkbox = forwardRef(function Checkbox({ label, className, id, 'aria-invalid': ariaInvalid, 'aria-describedby': ariaDescribedBy, 'aria-required': ariaRequired, ...rest }, ref) {
16
+ const wiring = useFieldWiring({ id, ariaInvalid, ariaDescribedBy, ariaRequired });
17
+ return (_jsxs("label", { className: "inline-flex items-center gap-x-2xs font-sans text-text-primary", children: [_jsx("input", { ref: ref, type: "checkbox", className: cx(BOX_CLASS, className), ...wiring, ...rest }), label] }));
18
+ });
19
+ //# sourceMappingURL=Checkbox.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Checkbox.js","sourceRoot":"","sources":["../../src/primitives/Checkbox.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,UAAU,EAA4C,MAAM,OAAO,CAAC;AAC7E,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AAClC,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAO3D,MAAM,SAAS,GAAG,mDAAmD,aAAa,EAAE,CAAC;AAErF;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,UAAU,CAAkC,SAAS,QAAQ,CACnF,EACE,KAAK,EACL,SAAS,EACT,EAAE,EACF,cAAc,EAAE,WAAW,EAC3B,kBAAkB,EAAE,eAAe,EACnC,eAAe,EAAE,YAAY,EAC7B,GAAG,IAAI,EACR,EACD,GAAG;IAEH,MAAM,MAAM,GAAG,cAAc,CAAC,EAAE,EAAE,EAAE,WAAW,EAAE,eAAe,EAAE,YAAY,EAAE,CAAC,CAAC;IAElF,OAAO,CACL,iBAAO,SAAS,EAAC,gEAAgE,aAC/E,gBAAO,GAAG,EAAE,GAAG,EAAE,IAAI,EAAC,UAAU,EAAC,SAAS,EAAE,EAAE,CAAC,SAAS,EAAE,SAAS,CAAC,KAAM,MAAM,KAAM,IAAI,GAAI,EAC7F,KAAK,IACA,CACT,CAAC;AACJ,CAAC,CAAC,CAAC"}
@@ -0,0 +1,38 @@
1
+ import type { ReactNode, SVGProps } from 'react';
2
+ type IconSize = 'sm' | 'md' | 'lg';
3
+ interface IconBase extends Omit<SVGProps<SVGSVGElement>, 'children' | 'aria-label'> {
4
+ /** The `<path>`, `<circle>`… of a 24×24 icon. The kit ships no artwork. */
5
+ children: ReactNode;
6
+ size?: IconSize;
7
+ }
8
+ interface MeaningfulIcon extends IconBase {
9
+ /** Localized name. Present when the icon carries meaning of its own. */
10
+ label: string;
11
+ decorative?: never;
12
+ }
13
+ interface DecorativeIcon extends IconBase {
14
+ /** The icon repeats something already in text, so it is hidden from assistive tech. */
15
+ decorative: true;
16
+ label?: never;
17
+ }
18
+ export type IconProps = MeaningfulIcon | DecorativeIcon;
19
+ /**
20
+ * The semantics around an icon — not the icon.
21
+ *
22
+ * 🔴 The kit ships no artwork and takes on no icon library, because the fleet has none:
23
+ * across all ten products the dependency count for lucide / heroicons / feather / phosphor
24
+ * / tabler is zero, and every icon is an inline `<svg>` (222 of them, measured 2026-08-23).
25
+ * Choosing a set would mean adding a dependency to seventeen ships to solve a problem they
26
+ * do not have.
27
+ *
28
+ * 🔴 What is not consistent is the meaning. Of those 222, **101 declare neither
29
+ * `aria-hidden` nor `role="img"`** — they do not say whether they are decoration or
30
+ * content, so assistive technology is left to guess, and the page looks entirely correct
31
+ * either way. Three carry `role="img"`; none carries a `<title>`.
32
+ *
33
+ * So `label` and `decorative` are mutually exclusive and one of them is required, enforced
34
+ * by the type. An icon that can be rendered without saying which it is would reproduce the
35
+ * 101 exactly.
36
+ */
37
+ export declare function Icon({ children, size, className, ...rest }: IconProps): import("react").JSX.Element;
38
+ export {};
@@ -0,0 +1,38 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { cx } from '../lib/cx.js';
3
+ const SIZE_CLASS = {
4
+ sm: 'h-4 w-4',
5
+ md: 'h-5 w-5',
6
+ lg: 'h-6 w-6',
7
+ };
8
+ /**
9
+ * The semantics around an icon — not the icon.
10
+ *
11
+ * 🔴 The kit ships no artwork and takes on no icon library, because the fleet has none:
12
+ * across all ten products the dependency count for lucide / heroicons / feather / phosphor
13
+ * / tabler is zero, and every icon is an inline `<svg>` (222 of them, measured 2026-08-23).
14
+ * Choosing a set would mean adding a dependency to seventeen ships to solve a problem they
15
+ * do not have.
16
+ *
17
+ * 🔴 What is not consistent is the meaning. Of those 222, **101 declare neither
18
+ * `aria-hidden` nor `role="img"`** — they do not say whether they are decoration or
19
+ * content, so assistive technology is left to guess, and the page looks entirely correct
20
+ * either way. Three carry `role="img"`; none carries a `<title>`.
21
+ *
22
+ * So `label` and `decorative` are mutually exclusive and one of them is required, enforced
23
+ * by the type. An icon that can be rendered without saying which it is would reproduce the
24
+ * 101 exactly.
25
+ */
26
+ export function Icon({ children, size = 'md', className, ...rest }) {
27
+ const { label, ...svgProps } = rest;
28
+ delete svgProps.decorative;
29
+ // 🔴 Falls back to decoration, not to `role="img"`. A JS caller or a stale build can reach
30
+ // here with neither prop, and a nameless `role="img"` is worse than silence: assistive
31
+ // technology announces "image" and stops. An empty string counts as missing too — that is
32
+ // what an unresolved translation key looks like at runtime.
33
+ const decorative = label === undefined || label === '';
34
+ return (_jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 1.5, className: cx(SIZE_CLASS[size], 'shrink-0', className), ...(decorative
35
+ ? { 'aria-hidden': true, focusable: false }
36
+ : { role: 'img', 'aria-label': label }), ...svgProps, children: children }));
37
+ }
38
+ //# sourceMappingURL=Icon.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Icon.js","sourceRoot":"","sources":["../../src/primitives/Icon.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AAwBlC,MAAM,UAAU,GAA6B;IAC3C,EAAE,EAAE,SAAS;IACb,EAAE,EAAE,SAAS;IACb,EAAE,EAAE,SAAS;CACd,CAAC;AAEF;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,IAAI,CAAC,EAAE,QAAQ,EAAE,IAAI,GAAG,IAAI,EAAE,SAAS,EAAE,GAAG,IAAI,EAAa;IAC3E,MAAM,EAAE,KAAK,EAAE,GAAG,QAAQ,EAAE,GAAG,IAEJ,CAAC;IAC5B,OAAQ,QAAqC,CAAC,UAAU,CAAC;IAEzD,2FAA2F;IAC3F,uFAAuF;IACvF,0FAA0F;IAC1F,4DAA4D;IAC5D,MAAM,UAAU,GAAG,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,EAAE,CAAC;IAEvD,OAAO,CACL,cACE,OAAO,EAAC,WAAW,EACnB,IAAI,EAAC,MAAM,EACX,MAAM,EAAC,cAAc,EACrB,WAAW,EAAE,GAAG,EAChB,SAAS,EAAE,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,UAAU,EAAE,SAAS,CAAC,KAClD,CAAC,UAAU;YACb,CAAC,CAAC,EAAE,aAAa,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE;YAC3C,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC,KACrC,QAAQ,YAEX,QAAQ,GACL,CACP,CAAC;AACJ,CAAC"}
@@ -0,0 +1,7 @@
1
+ import { type InputHTMLAttributes } from 'react';
2
+ export type InputProps = InputHTMLAttributes<HTMLInputElement>;
3
+ /**
4
+ * Text input primitive. forwardRef so it works directly with react-hook-form `register`.
5
+ * Visual values come from theme tokens only.
6
+ */
7
+ export declare const Input: import("react").ForwardRefExoticComponent<InputProps & import("react").RefAttributes<HTMLInputElement>>;
@@ -0,0 +1,15 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { forwardRef } from 'react';
3
+ import { cx } from '../lib/cx.js';
4
+ import { CONTROL_CLASS } from '../lib/states.js';
5
+ import { useFieldWiring } from '../forms/field-context.js';
6
+ const BASE_CLASS = `w-full rounded-x-md border border-border bg-surface-raised px-x-inline-sm py-x-stack-sm font-sans text-text-primary placeholder:text-text-muted ${CONTROL_CLASS}`;
7
+ /**
8
+ * Text input primitive. forwardRef so it works directly with react-hook-form `register`.
9
+ * Visual values come from theme tokens only.
10
+ */
11
+ export const Input = forwardRef(function Input({ className, id, 'aria-invalid': ariaInvalid, 'aria-describedby': ariaDescribedBy, 'aria-required': ariaRequired, ...rest }, ref) {
12
+ const wiring = useFieldWiring({ id, ariaInvalid, ariaDescribedBy, ariaRequired });
13
+ return _jsx("input", { ref: ref, className: cx(BASE_CLASS, className), ...wiring, ...rest });
14
+ });
15
+ //# sourceMappingURL=Input.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Input.js","sourceRoot":"","sources":["../../src/primitives/Input.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,UAAU,EAA4B,MAAM,OAAO,CAAC;AAC7D,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AAClC,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAI3D,MAAM,UAAU,GAAG,mJAAmJ,aAAa,EAAE,CAAC;AAEtL;;;GAGG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG,UAAU,CAA+B,SAAS,KAAK,CAC1E,EACE,SAAS,EACT,EAAE,EACF,cAAc,EAAE,WAAW,EAC3B,kBAAkB,EAAE,eAAe,EACnC,eAAe,EAAE,YAAY,EAC7B,GAAG,IAAI,EACR,EACD,GAAG;IAEH,MAAM,MAAM,GAAG,cAAc,CAAC,EAAE,EAAE,EAAE,WAAW,EAAE,eAAe,EAAE,YAAY,EAAE,CAAC,CAAC;IAElF,OAAO,gBAAO,GAAG,EAAE,GAAG,EAAE,SAAS,EAAE,EAAE,CAAC,UAAU,EAAE,SAAS,CAAC,KAAM,MAAM,KAAM,IAAI,GAAI,CAAC;AACzF,CAAC,CAAC,CAAC"}
@@ -0,0 +1,19 @@
1
+ import { type InputHTMLAttributes, type ReactNode } from 'react';
2
+ export interface RadioProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'type'> {
3
+ /** Localized label. Rendered inside the control's own `<label>`. */
4
+ label: ReactNode;
5
+ /** Radios only mean anything in a group; `name` is what makes them one. */
6
+ name: string;
7
+ }
8
+ /**
9
+ * One option in a radio group, with its label.
10
+ *
11
+ * 🔴 `name` is required, unlike on a bare `<input type="radio">`. A radio without a name is
12
+ * its own group of one: it can be checked but never unchecked, and arrow keys do not move
13
+ * between the options. Nothing about the rendered page shows this, so it survives review.
14
+ *
15
+ * 🔴 This does not take the `FormField` wiring. A group of radios shares one label and one
16
+ * error, so the id and `aria-describedby` belong on a `<fieldset>` around the group, not on
17
+ * each option — putting them here would give every option the same id.
18
+ */
19
+ export declare const Radio: import("react").ForwardRefExoticComponent<RadioProps & import("react").RefAttributes<HTMLInputElement>>;
@@ -0,0 +1,20 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { forwardRef } from 'react';
3
+ import { cx } from '../lib/cx.js';
4
+ import { CONTROL_CLASS } from '../lib/states.js';
5
+ const DOT_CLASS = `border border-border accent-accent ${CONTROL_CLASS}`;
6
+ /**
7
+ * One option in a radio group, with its label.
8
+ *
9
+ * 🔴 `name` is required, unlike on a bare `<input type="radio">`. A radio without a name is
10
+ * its own group of one: it can be checked but never unchecked, and arrow keys do not move
11
+ * between the options. Nothing about the rendered page shows this, so it survives review.
12
+ *
13
+ * 🔴 This does not take the `FormField` wiring. A group of radios shares one label and one
14
+ * error, so the id and `aria-describedby` belong on a `<fieldset>` around the group, not on
15
+ * each option — putting them here would give every option the same id.
16
+ */
17
+ export const Radio = forwardRef(function Radio({ label, name, className, ...rest }, ref) {
18
+ return (_jsxs("label", { className: "inline-flex items-center gap-x-2xs font-sans text-text-primary", children: [_jsx("input", { ref: ref, type: "radio", name: name, className: cx(DOT_CLASS, className), ...rest }), label] }));
19
+ });
20
+ //# sourceMappingURL=Radio.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Radio.js","sourceRoot":"","sources":["../../src/primitives/Radio.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,UAAU,EAA4C,MAAM,OAAO,CAAC;AAC7E,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AAClC,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AASjD,MAAM,SAAS,GAAG,sCAAsC,aAAa,EAAE,CAAC;AAExE;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG,UAAU,CAA+B,SAAS,KAAK,CAC1E,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,IAAI,EAAE,EACnC,GAAG;IAEH,OAAO,CACL,iBAAO,SAAS,EAAC,gEAAgE,aAC/E,gBAAO,GAAG,EAAE,GAAG,EAAE,IAAI,EAAC,OAAO,EAAC,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,CAAC,SAAS,EAAE,SAAS,CAAC,KAAM,IAAI,GAAI,EAC1F,KAAK,IACA,CACT,CAAC;AACJ,CAAC,CAAC,CAAC"}
@@ -0,0 +1,9 @@
1
+ import { type ReactNode, type SelectHTMLAttributes } from 'react';
2
+ export interface SelectProps extends SelectHTMLAttributes<HTMLSelectElement> {
3
+ children: ReactNode;
4
+ }
5
+ /**
6
+ * Select primitive. forwardRef so it works directly with react-hook-form `register`.
7
+ * Visual values come from theme tokens only.
8
+ */
9
+ export declare const Select: import("react").ForwardRefExoticComponent<SelectProps & import("react").RefAttributes<HTMLSelectElement>>;
@@ -0,0 +1,15 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { forwardRef } from 'react';
3
+ import { cx } from '../lib/cx.js';
4
+ import { CONTROL_CLASS } from '../lib/states.js';
5
+ import { useFieldWiring } from '../forms/field-context.js';
6
+ const BASE_CLASS = `w-full rounded-x-md border border-border bg-surface-raised px-x-inline-sm py-x-stack-sm font-sans text-text-primary ${CONTROL_CLASS}`;
7
+ /**
8
+ * Select primitive. forwardRef so it works directly with react-hook-form `register`.
9
+ * Visual values come from theme tokens only.
10
+ */
11
+ export const Select = forwardRef(function Select({ children, className, id, 'aria-invalid': ariaInvalid, 'aria-describedby': ariaDescribedBy, 'aria-required': ariaRequired, ...rest }, ref) {
12
+ const wiring = useFieldWiring({ id, ariaInvalid, ariaDescribedBy, ariaRequired });
13
+ return (_jsx("select", { ref: ref, className: cx(BASE_CLASS, className), ...wiring, ...rest, children: children }));
14
+ });
15
+ //# sourceMappingURL=Select.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Select.js","sourceRoot":"","sources":["../../src/primitives/Select.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,UAAU,EAA6C,MAAM,OAAO,CAAC;AAC9E,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AAClC,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAM3D,MAAM,UAAU,GAAG,uHAAuH,aAAa,EAAE,CAAC;AAE1J;;;GAGG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG,UAAU,CAAiC,SAAS,MAAM,CAC9E,EACE,QAAQ,EACR,SAAS,EACT,EAAE,EACF,cAAc,EAAE,WAAW,EAC3B,kBAAkB,EAAE,eAAe,EACnC,eAAe,EAAE,YAAY,EAC7B,GAAG,IAAI,EACR,EACD,GAAG;IAEH,MAAM,MAAM,GAAG,cAAc,CAAC,EAAE,EAAE,EAAE,WAAW,EAAE,eAAe,EAAE,YAAY,EAAE,CAAC,CAAC;IAElF,OAAO,CACL,iBAAQ,GAAG,EAAE,GAAG,EAAE,SAAS,EAAE,EAAE,CAAC,UAAU,EAAE,SAAS,CAAC,KAAM,MAAM,KAAM,IAAI,YACzE,QAAQ,GACF,CACV,CAAC;AACJ,CAAC,CAAC,CAAC"}
@@ -0,0 +1,5 @@
1
+ export interface SpinnerProps {
2
+ /** Localized loading text. The kit never ships strings — see README. */
3
+ label: string;
4
+ }
5
+ export declare function Spinner({ label }: SpinnerProps): import("react").JSX.Element;
@@ -0,0 +1,5 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ export function Spinner({ label }) {
3
+ return (_jsx("output", { className: "font-sans text-text-muted", "aria-live": "polite", children: label }));
4
+ }
5
+ //# sourceMappingURL=Spinner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Spinner.js","sourceRoot":"","sources":["../../src/primitives/Spinner.tsx"],"names":[],"mappings":";AAKA,MAAM,UAAU,OAAO,CAAC,EAAE,KAAK,EAAgB;IAC7C,OAAO,CACL,iBAAQ,SAAS,EAAC,2BAA2B,eAAW,QAAQ,YAC7D,KAAK,GACC,CACV,CAAC;AACJ,CAAC"}