@dashforge/tw 0.1.0-beta → 0.2.0-beta

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 (185) hide show
  1. package/CHANGELOG.md +108 -0
  2. package/dist/index.d.ts +1 -82
  3. package/dist/index.esm.js +1563 -2
  4. package/dist/src/components/AspectRatio/AspectRatio.d.ts +36 -0
  5. package/dist/src/components/AspectRatio/AspectRatio.d.ts.map +1 -0
  6. package/dist/src/components/AspectRatio/aspectRatio.types.d.ts +50 -0
  7. package/dist/src/components/AspectRatio/aspectRatio.types.d.ts.map +1 -0
  8. package/dist/src/components/Box/Box.d.ts +36 -0
  9. package/dist/src/components/Box/Box.d.ts.map +1 -0
  10. package/dist/src/components/Box/box.types.d.ts +54 -0
  11. package/dist/src/components/Box/box.types.d.ts.map +1 -0
  12. package/dist/src/components/Box/box.variants.d.ts +402 -0
  13. package/dist/src/components/Box/box.variants.d.ts.map +1 -0
  14. package/dist/src/components/Container/Container.d.ts +35 -0
  15. package/dist/src/components/Container/Container.d.ts.map +1 -0
  16. package/dist/src/components/Container/container.types.d.ts +41 -0
  17. package/dist/src/components/Container/container.types.d.ts.map +1 -0
  18. package/dist/src/components/Container/container.variants.d.ts +93 -0
  19. package/dist/src/components/Container/container.variants.d.ts.map +1 -0
  20. package/dist/src/components/Divider/Divider.d.ts +50 -0
  21. package/dist/src/components/Divider/Divider.d.ts.map +1 -0
  22. package/dist/src/components/Divider/divider.types.d.ts +47 -0
  23. package/dist/src/components/Divider/divider.types.d.ts.map +1 -0
  24. package/dist/src/components/Divider/divider.variants.d.ts +129 -0
  25. package/dist/src/components/Divider/divider.variants.d.ts.map +1 -0
  26. package/dist/src/components/Grid/Grid.d.ts +32 -0
  27. package/dist/src/components/Grid/Grid.d.ts.map +1 -0
  28. package/dist/src/components/Grid/grid.types.d.ts +103 -0
  29. package/dist/src/components/Grid/grid.types.d.ts.map +1 -0
  30. package/dist/src/components/Grid/grid.variants.d.ts +453 -0
  31. package/dist/src/components/Grid/grid.variants.d.ts.map +1 -0
  32. package/dist/src/components/Stack/Stack.d.ts +24 -0
  33. package/dist/src/components/Stack/Stack.d.ts.map +1 -0
  34. package/dist/src/components/Stack/stack.types.d.ts +61 -0
  35. package/dist/src/components/Stack/stack.types.d.ts.map +1 -0
  36. package/dist/src/components/Stack/stack.variants.d.ts +163 -0
  37. package/dist/src/components/Stack/stack.variants.d.ts.map +1 -0
  38. package/dist/src/components/Typography/Typography.d.ts +42 -0
  39. package/dist/src/components/Typography/Typography.d.ts.map +1 -0
  40. package/dist/src/components/Typography/typography.types.d.ts +61 -0
  41. package/dist/src/components/Typography/typography.types.d.ts.map +1 -0
  42. package/dist/src/components/Typography/typography.variants.d.ts +167 -0
  43. package/dist/src/components/Typography/typography.variants.d.ts.map +1 -0
  44. package/dist/src/components/VisuallyHidden/VisuallyHidden.d.ts +42 -0
  45. package/dist/src/components/VisuallyHidden/VisuallyHidden.d.ts.map +1 -0
  46. package/dist/src/components/VisuallyHidden/visuallyHidden.types.d.ts +42 -0
  47. package/dist/src/components/VisuallyHidden/visuallyHidden.types.d.ts.map +1 -0
  48. package/dist/src/index.d.ts +22 -0
  49. package/dist/src/index.d.ts.map +1 -1
  50. package/package.json +3 -3
  51. package/src/components/AspectRatio/AspectRatio.test.tsx +173 -0
  52. package/src/components/AspectRatio/AspectRatio.tsx +73 -0
  53. package/src/components/AspectRatio/aspectRatio.types.ts +54 -0
  54. package/src/components/Box/Box.test.tsx +349 -0
  55. package/src/components/Box/Box.tsx +83 -0
  56. package/src/components/Box/box.types.ts +61 -0
  57. package/src/components/Box/box.variants.ts +211 -0
  58. package/src/components/Container/Container.test.tsx +209 -0
  59. package/src/components/Container/Container.tsx +74 -0
  60. package/src/components/Container/container.types.ts +45 -0
  61. package/src/components/Container/container.variants.ts +81 -0
  62. package/src/components/Divider/Divider.test.tsx +241 -0
  63. package/src/components/Divider/Divider.tsx +140 -0
  64. package/src/components/Divider/divider.types.ts +52 -0
  65. package/src/components/Divider/divider.variants.ts +115 -0
  66. package/src/components/Grid/Grid.test.tsx +321 -0
  67. package/src/components/Grid/Grid.tsx +148 -0
  68. package/src/components/Grid/grid.types.ts +112 -0
  69. package/src/components/Grid/grid.variants.ts +133 -0
  70. package/src/components/Stack/Stack.test.tsx +309 -0
  71. package/src/components/Stack/Stack.tsx +115 -0
  72. package/src/components/Stack/stack.types.ts +68 -0
  73. package/src/components/Stack/stack.variants.ts +80 -0
  74. package/src/components/Typography/Typography.test.tsx +230 -0
  75. package/src/components/Typography/Typography.tsx +112 -0
  76. package/src/components/Typography/typography.types.ts +65 -0
  77. package/src/components/Typography/typography.variants.ts +113 -0
  78. package/src/components/VisuallyHidden/VisuallyHidden.test.tsx +86 -0
  79. package/src/components/VisuallyHidden/VisuallyHidden.tsx +59 -0
  80. package/src/components/VisuallyHidden/visuallyHidden.types.ts +45 -0
  81. package/src/index.ts +49 -0
  82. package/dist/components/AppShell/AppShell.d.ts +0 -32
  83. package/dist/components/AppShell/AppShell.d.ts.map +0 -1
  84. package/dist/components/AppShell/appShell.types.d.ts +0 -62
  85. package/dist/components/AppShell/appShell.types.d.ts.map +0 -1
  86. package/dist/components/AppShell/appShell.variants.d.ts +0 -65
  87. package/dist/components/AppShell/appShell.variants.d.ts.map +0 -1
  88. package/dist/components/Autocomplete/Autocomplete.d.ts +0 -32
  89. package/dist/components/Autocomplete/Autocomplete.d.ts.map +0 -1
  90. package/dist/components/Autocomplete/autocomplete.types.d.ts +0 -214
  91. package/dist/components/Autocomplete/autocomplete.types.d.ts.map +0 -1
  92. package/dist/components/Autocomplete/autocomplete.variants.d.ts +0 -214
  93. package/dist/components/Autocomplete/autocomplete.variants.d.ts.map +0 -1
  94. package/dist/components/Breadcrumbs/Breadcrumbs.d.ts +0 -23
  95. package/dist/components/Breadcrumbs/Breadcrumbs.d.ts.map +0 -1
  96. package/dist/components/Breadcrumbs/breadcrumbs.types.d.ts +0 -98
  97. package/dist/components/Breadcrumbs/breadcrumbs.types.d.ts.map +0 -1
  98. package/dist/components/Breadcrumbs/breadcrumbs.variants.d.ts +0 -85
  99. package/dist/components/Breadcrumbs/breadcrumbs.variants.d.ts.map +0 -1
  100. package/dist/components/Button/Button.d.ts +0 -44
  101. package/dist/components/Button/Button.d.ts.map +0 -1
  102. package/dist/components/Button/button.types.d.ts +0 -66
  103. package/dist/components/Button/button.types.d.ts.map +0 -1
  104. package/dist/components/Button/button.variants.d.ts +0 -104
  105. package/dist/components/Button/button.variants.d.ts.map +0 -1
  106. package/dist/components/Checkbox/Checkbox.d.ts +0 -31
  107. package/dist/components/Checkbox/Checkbox.d.ts.map +0 -1
  108. package/dist/components/Checkbox/checkbox.types.d.ts +0 -86
  109. package/dist/components/Checkbox/checkbox.types.d.ts.map +0 -1
  110. package/dist/components/Checkbox/checkbox.variants.d.ts +0 -109
  111. package/dist/components/Checkbox/checkbox.variants.d.ts.map +0 -1
  112. package/dist/components/ConfirmDialog/ConfirmDialog.d.ts +0 -38
  113. package/dist/components/ConfirmDialog/ConfirmDialog.d.ts.map +0 -1
  114. package/dist/components/ConfirmDialog/confirmDialog.types.d.ts +0 -80
  115. package/dist/components/ConfirmDialog/confirmDialog.types.d.ts.map +0 -1
  116. package/dist/components/ConfirmDialog/confirmDialog.variants.d.ts +0 -90
  117. package/dist/components/ConfirmDialog/confirmDialog.variants.d.ts.map +0 -1
  118. package/dist/components/DateTimePicker/DateTimePicker.d.ts +0 -49
  119. package/dist/components/DateTimePicker/DateTimePicker.d.ts.map +0 -1
  120. package/dist/components/DateTimePicker/dateTimePicker.types.d.ts +0 -95
  121. package/dist/components/DateTimePicker/dateTimePicker.types.d.ts.map +0 -1
  122. package/dist/components/DateTimePicker/dateTimePicker.variants.d.ts +0 -165
  123. package/dist/components/DateTimePicker/dateTimePicker.variants.d.ts.map +0 -1
  124. package/dist/components/LeftNav/LeftNav.d.ts +0 -34
  125. package/dist/components/LeftNav/LeftNav.d.ts.map +0 -1
  126. package/dist/components/LeftNav/leftNav.types.d.ts +0 -136
  127. package/dist/components/LeftNav/leftNav.types.d.ts.map +0 -1
  128. package/dist/components/LeftNav/leftNav.variants.d.ts +0 -143
  129. package/dist/components/LeftNav/leftNav.variants.d.ts.map +0 -1
  130. package/dist/components/NumberField/NumberField.d.ts +0 -18
  131. package/dist/components/NumberField/NumberField.d.ts.map +0 -1
  132. package/dist/components/NumberField/numberField.types.d.ts +0 -79
  133. package/dist/components/NumberField/numberField.types.d.ts.map +0 -1
  134. package/dist/components/NumberField/numberField.variants.d.ts +0 -169
  135. package/dist/components/NumberField/numberField.variants.d.ts.map +0 -1
  136. package/dist/components/OTPField/OTPField.d.ts +0 -25
  137. package/dist/components/OTPField/OTPField.d.ts.map +0 -1
  138. package/dist/components/OTPField/otpField.types.d.ts +0 -74
  139. package/dist/components/OTPField/otpField.types.d.ts.map +0 -1
  140. package/dist/components/OTPField/otpField.variants.d.ts +0 -105
  141. package/dist/components/OTPField/otpField.variants.d.ts.map +0 -1
  142. package/dist/components/RadioGroup/RadioGroup.d.ts +0 -35
  143. package/dist/components/RadioGroup/RadioGroup.d.ts.map +0 -1
  144. package/dist/components/RadioGroup/radioGroup.types.d.ts +0 -103
  145. package/dist/components/RadioGroup/radioGroup.types.d.ts.map +0 -1
  146. package/dist/components/RadioGroup/radioGroup.variants.d.ts +0 -166
  147. package/dist/components/RadioGroup/radioGroup.variants.d.ts.map +0 -1
  148. package/dist/components/Snackbar/Snackbar.d.ts +0 -37
  149. package/dist/components/Snackbar/Snackbar.d.ts.map +0 -1
  150. package/dist/components/Snackbar/snackbar.types.d.ts +0 -86
  151. package/dist/components/Snackbar/snackbar.types.d.ts.map +0 -1
  152. package/dist/components/Snackbar/snackbar.variants.d.ts +0 -153
  153. package/dist/components/Snackbar/snackbar.variants.d.ts.map +0 -1
  154. package/dist/components/Switch/Switch.d.ts +0 -15
  155. package/dist/components/Switch/Switch.d.ts.map +0 -1
  156. package/dist/components/Switch/switch.types.d.ts +0 -47
  157. package/dist/components/Switch/switch.types.d.ts.map +0 -1
  158. package/dist/components/Switch/switch.variants.d.ts +0 -105
  159. package/dist/components/Switch/switch.variants.d.ts.map +0 -1
  160. package/dist/components/TextField/TextField.d.ts +0 -25
  161. package/dist/components/TextField/TextField.d.ts.map +0 -1
  162. package/dist/components/TextField/textField.types.d.ts +0 -72
  163. package/dist/components/TextField/textField.types.d.ts.map +0 -1
  164. package/dist/components/TextField/textField.variants.d.ts +0 -160
  165. package/dist/components/TextField/textField.variants.d.ts.map +0 -1
  166. package/dist/components/Textarea/Textarea.d.ts +0 -16
  167. package/dist/components/Textarea/Textarea.d.ts.map +0 -1
  168. package/dist/components/Textarea/textarea.types.d.ts +0 -63
  169. package/dist/components/Textarea/textarea.types.d.ts.map +0 -1
  170. package/dist/components/Textarea/textarea.variants.d.ts +0 -197
  171. package/dist/components/Textarea/textarea.variants.d.ts.map +0 -1
  172. package/dist/components/TopBar/TopBar.d.ts +0 -33
  173. package/dist/components/TopBar/TopBar.d.ts.map +0 -1
  174. package/dist/components/TopBar/topBar.types.d.ts +0 -49
  175. package/dist/components/TopBar/topBar.types.d.ts.map +0 -1
  176. package/dist/components/TopBar/topBar.variants.d.ts +0 -79
  177. package/dist/components/TopBar/topBar.variants.d.ts.map +0 -1
  178. package/dist/components/_shared/resolveValidationState.d.ts +0 -42
  179. package/dist/components/_shared/resolveValidationState.d.ts.map +0 -1
  180. package/dist/hooks/useAccessState.d.ts +0 -37
  181. package/dist/hooks/useAccessState.d.ts.map +0 -1
  182. package/dist/index.d.ts.map +0 -1
  183. package/dist/tsconfig.lib.tsbuildinfo +0 -1
  184. package/dist/utils/cn.d.ts +0 -26
  185. package/dist/utils/cn.d.ts.map +0 -1
package/dist/index.esm.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { jsx, jsxs, Fragment as Fragment$1 } from 'react/jsx-runtime';
2
- import { useMemo, forwardRef, useContext, useId, useRef, useEffect, useCallback, useState, Fragment, createContext } from 'react';
2
+ import { useMemo, forwardRef, useContext, useId, useRef, useEffect, useCallback, useState, Fragment, createContext, Children, isValidElement, cloneElement } from 'react';
3
3
  import { Slot } from '@radix-ui/react-slot';
4
4
  import clsx from 'clsx';
5
5
  import { twMerge } from 'tailwind-merge';
@@ -4926,6 +4926,1567 @@ const SnackbarContext = /*#__PURE__*/ createContext(null);
4926
4926
  });
4927
4927
  }
4928
4928
 
4929
+ /**
4930
+ * `typographyVariants` — the full type scale for the @dashforge/tw library.
4931
+ *
4932
+ * Mirrors the MUI Typography variant set (h1–h6 · subtitle1/2 · body1/2 ·
4933
+ * caption · overline) so the mental model carries over for developers
4934
+ * moving between the two ecosystems. Each variant baseline maps to a
4935
+ * Tailwind utility chain that resolves through the @dashforge/tw-tokens
4936
+ * scale (so the visual stays in sync with the rest of the system when the
4937
+ * token theme is patched).
4938
+ *
4939
+ * Variant axes are intentionally ORTHOGONAL — `variant` chooses the type
4940
+ * scale, `weight` overrides the variant's default weight (useful for "h2
4941
+ * but lighter"), `color` picks the intent, `align` picks the axis. They
4942
+ * never collide, so consumers can mix them freely.
4943
+ *
4944
+ * Two boolean flags (`truncate`, `noWrap`) encode the most common one-line
4945
+ * patterns; `gutterBottom` adds the conventional bottom margin used when a
4946
+ * heading precedes a paragraph block (mirror of MUI's same flag).
4947
+ */ const typographyVariants = tv({
4948
+ base: 'text-inherit',
4949
+ variants: {
4950
+ /*
4951
+ * `variant` is the type-scale axis.
4952
+ *
4953
+ * The default font-weight is baked into each variant (headings come
4954
+ * with semibold/bold by default). Consumers override per-instance via
4955
+ * the `weight` axis below — when set, `weight` wins because it's
4956
+ * declared later in the cn() chain and tailwind-merge resolves the
4957
+ * last `font-*` to win.
4958
+ */ variant: {
4959
+ h1: 'text-5xl font-bold leading-[1.05] tracking-[-0.025em]',
4960
+ h2: 'text-4xl font-bold leading-[1.1] tracking-[-0.022em]',
4961
+ h3: 'text-3xl font-semibold leading-[1.15] tracking-[-0.02em]',
4962
+ h4: 'text-2xl font-semibold leading-snug tracking-[-0.015em]',
4963
+ h5: 'text-xl font-semibold leading-snug',
4964
+ h6: 'text-lg font-semibold leading-normal',
4965
+ subtitle1: 'text-base font-medium leading-relaxed',
4966
+ subtitle2: 'text-sm font-medium leading-relaxed',
4967
+ body1: 'text-base font-normal leading-relaxed',
4968
+ body2: 'text-sm font-normal leading-relaxed',
4969
+ caption: 'text-xs font-normal leading-normal',
4970
+ overline: 'text-xs font-semibold uppercase tracking-[0.12em] leading-normal'
4971
+ },
4972
+ /*
4973
+ * `color` is the intent axis. Pairs with the @dashforge/tw-theme
4974
+ * reactive colour vars so the choice survives theme patches and dark
4975
+ * mode flips. `inherit` is the escape hatch — used inside a Box that
4976
+ * has set its own color (e.g. `<Box variant="solid" color="primary">`
4977
+ * paints white text).
4978
+ */ color: {
4979
+ inherit: 'text-inherit',
4980
+ primary: 'text-primary-700 dark:text-primary-400',
4981
+ secondary: 'text-secondary-700 dark:text-secondary-400',
4982
+ success: 'text-success-700 dark:text-success-400',
4983
+ warning: 'text-warning-700 dark:text-warning-400',
4984
+ danger: 'text-danger-700 dark:text-danger-400',
4985
+ info: 'text-info-700 dark:text-info-400',
4986
+ neutral: 'text-neutral-900 dark:text-neutral-100',
4987
+ muted: 'text-neutral-600 dark:text-neutral-400'
4988
+ },
4989
+ /*
4990
+ * `weight` overrides the variant's default weight. When unset, the
4991
+ * variant's own weight wins. When set, this axis appears LATER in the
4992
+ * cn() chain so tailwind-merge resolves to this value.
4993
+ */ weight: {
4994
+ normal: 'font-normal',
4995
+ medium: 'font-medium',
4996
+ semibold: 'font-semibold',
4997
+ bold: 'font-bold',
4998
+ extrabold: 'font-extrabold'
4999
+ },
5000
+ align: {
5001
+ left: 'text-left',
5002
+ center: 'text-center',
5003
+ right: 'text-right',
5004
+ justify: 'text-justify'
5005
+ },
5006
+ /*
5007
+ * `truncate` collapses to a one-line ellipsis. `noWrap` is the looser
5008
+ * sibling — keeps the text on one line but lets it overflow without
5009
+ * the `…`. Mutually-exclusive intent-wise; if both are passed,
5010
+ * `truncate` wins (later in the cn() chain).
5011
+ */ truncate: {
5012
+ true: 'truncate'
5013
+ },
5014
+ noWrap: {
5015
+ true: 'whitespace-nowrap'
5016
+ },
5017
+ /*
5018
+ * `gutterBottom` adds the conventional bottom margin used when a
5019
+ * heading precedes a paragraph block. Mirror of MUI's same prop —
5020
+ * familiar to developers crossing from the MUI side.
5021
+ */ gutterBottom: {
5022
+ true: 'mb-3'
5023
+ }
5024
+ },
5025
+ defaultVariants: {
5026
+ variant: 'body1',
5027
+ color: 'inherit',
5028
+ align: 'left'
5029
+ }
5030
+ });
5031
+
5032
+ /**
5033
+ * Default HTML tag per variant. Headings get their semantic level (h1→h1
5034
+ * etc.); subtitle/body get `<p>` (block, paragraph semantics);
5035
+ * caption/overline get `<span>` (inline, no implicit block break).
5036
+ *
5037
+ * Override per-instance via the `as` prop — useful when the semantic
5038
+ * heading level should differ from the visual scale (e.g. a hero
5039
+ * rendered as `<h2>` but visually styled `h1`).
5040
+ */ const VARIANT_TO_TAG = {
5041
+ h1: 'h1',
5042
+ h2: 'h2',
5043
+ h3: 'h3',
5044
+ h4: 'h4',
5045
+ h5: 'h5',
5046
+ h6: 'h6',
5047
+ subtitle1: 'p',
5048
+ subtitle2: 'p',
5049
+ body1: 'p',
5050
+ body2: 'p',
5051
+ caption: 'span',
5052
+ overline: 'span'
5053
+ };
5054
+ /**
5055
+ * `<Typography>` — semantic typed text, the foundation of every readable
5056
+ * surface in @dashforge/tw.
5057
+ *
5058
+ * Why this exists:
5059
+ * Tailwind ships a typographic scale (`text-xl`, `font-bold`,
5060
+ * `leading-relaxed`) but leaves the SEMANTIC HTML tag and the
5061
+ * intent-coloured palette to the consumer. That's fine for one-off
5062
+ * marketing surfaces, but at app scale it means every `<h2>` and every
5063
+ * body paragraph re-derives its own utility chain — and they drift.
5064
+ *
5065
+ * Typography moves that decision into a typed prop set: the visual
5066
+ * scale, the intent colour, the alignment, the truncation all live in
5067
+ * `tailwind-variants` and resolve to the same utility chain everywhere
5068
+ * in the app. The default HTML tag is inferred from `variant` so the
5069
+ * semantic layer follows the visual layer; `as` and `asChild` are the
5070
+ * two escape hatches when you need something else.
5071
+ *
5072
+ * Layering:
5073
+ * • Sits BENEATH every component that renders text — `<Button>`'s
5074
+ * label, `<TextField>`'s helper text, MDX prose in our own docs.
5075
+ * • Composes ON TOP of `@dashforge/tw-tokens` colour scales (so
5076
+ * `color="primary"` paints `text-primary-700` in light, `-400` in dark,
5077
+ * reactive to `setMode()`).
5078
+ * • Polymorphic via Radix Slot — pairs cleanly with router `<Link>`,
5079
+ * `<button>`, or `<a>` without injecting an extra wrapper element.
5080
+ *
5081
+ * Polymorphism rules — `as` vs `asChild`:
5082
+ * • `as` swaps the rendered tag (we still render the element ourselves).
5083
+ * • `asChild` removes our element entirely — the single React child
5084
+ * becomes the rendered tag, with our className/ref merged onto it.
5085
+ * This is the Radix Slot pattern; use when you need a router Link
5086
+ * to style as a heading.
5087
+ * When BOTH are passed, `asChild` wins. We considered making this a
5088
+ * compile-time error via discriminated unions, but the API surface
5089
+ * already has 8 axes and adding one more dimension to the props type
5090
+ * would inflate IntelliSense suggestions for marginal benefit. The
5091
+ * runtime preference is documented; the test asserts it.
5092
+ */ const Typography = /*#__PURE__*/ forwardRef(function Typography(props, ref) {
5093
+ var _ref;
5094
+ const { variant = 'body1', color, weight, align, truncate, noWrap, gutterBottom, as, asChild = false, sx, children } = props, rest = _object_without_properties_loose(props, [
5095
+ "variant",
5096
+ "color",
5097
+ "weight",
5098
+ "align",
5099
+ "truncate",
5100
+ "noWrap",
5101
+ "gutterBottom",
5102
+ "as",
5103
+ "asChild",
5104
+ "sx",
5105
+ "children"
5106
+ ]);
5107
+ const classes = cn(typographyVariants({
5108
+ variant,
5109
+ color,
5110
+ weight,
5111
+ align,
5112
+ truncate,
5113
+ noWrap,
5114
+ gutterBottom
5115
+ }), sx);
5116
+ // asChild wins over `as` when both are passed (see component header).
5117
+ if (asChild) {
5118
+ return jsx(Slot, _extends({
5119
+ ref: ref,
5120
+ className: classes
5121
+ }, rest, {
5122
+ children: children
5123
+ }));
5124
+ }
5125
+ // Resolve the rendered tag: explicit `as` > variant default > fallback.
5126
+ const Tag = (_ref = as != null ? as : VARIANT_TO_TAG[variant]) != null ? _ref : 'span';
5127
+ return jsx(Tag, _extends({
5128
+ ref: ref,
5129
+ className: classes
5130
+ }, rest, {
5131
+ children: children
5132
+ }));
5133
+ });
5134
+ Typography.displayName = 'Typography';
5135
+
5136
+ /**
5137
+ * `boxVariants` — the surface primitive for @dashforge/tw.
5138
+ *
5139
+ * Architectural choice (planned with the user — F9 deep dive):
5140
+ *
5141
+ * Box replaces FOUR overlapping concepts from MUI in one component:
5142
+ * • Box (typed div)
5143
+ * • Paper (surface with elevation)
5144
+ * • Card (Paper specialisation)
5145
+ * • Surface (Joy UI's outlined / soft / solid / plain variants)
5146
+ *
5147
+ * The reason for the consolidation: in MUI you have to compose two or
5148
+ * three of these to express even basic intent ("an outlined card with
5149
+ * warning tone"). Here, one `<Box variant="outlined" color="warning">`
5150
+ * says exactly that.
5151
+ *
5152
+ * Variant taxonomy (5 axes, intentionally non-overlapping with Stack/Grid):
5153
+ *
5154
+ * • plain — bare div + padding + radius. The escape hatch.
5155
+ * • outlined — 1px border + subtle bg tint. The "card lite".
5156
+ * • elevated — bg surface + shadow scale (0-5). The "floating panel".
5157
+ * • soft — semi-transparent intent bg + intent text. The "callout".
5158
+ * • solid — solid intent bg + contrasting text. The "CTA banner".
5159
+ *
5160
+ * `color` applies to outlined/soft/solid (each gets the 7 intent
5161
+ * variants). `elevated` is color-agnostic (always neutral surface +
5162
+ * shadow scale). `plain` is everything-agnostic.
5163
+ *
5164
+ * Spacing axes (p/px/py/m/mx/my): mapped explicitly to the 11 token
5165
+ * steps from @dashforge/tw-tokens (0, 0.5, 1, 2, 3, 4, 6, 8, 12, 16, 24).
5166
+ * Tailwind JIT requires literal class strings — building them dynamically
5167
+ * with template literals would purge them. The verbosity below is the
5168
+ * cost of keeping the bundle CSS-pure and rebuild-free.
5169
+ *
5170
+ * What this does NOT do (deliberate, see component docs):
5171
+ * • No display / flex / grid props → use Stack or Grid
5172
+ * • No position / overflow / z-index → use `sx`
5173
+ * • No animation / transition → use `sx`
5174
+ *
5175
+ * The "Box is not flex" rule is the spine of the layout layer. Without
5176
+ * it, every `<div>` in an app gravitates back to Box and the surface
5177
+ * vs layout distinction collapses — exactly the failure mode this
5178
+ * primitive exists to prevent.
5179
+ */ const boxVariants = tv({
5180
+ base: 'block',
5181
+ variants: {
5182
+ /*
5183
+ * Surface variant. Compound with `color` for outlined / soft / solid;
5184
+ * standalone for plain / elevated.
5185
+ */ variant: {
5186
+ plain: '',
5187
+ outlined: 'border',
5188
+ elevated: 'bg-white dark:bg-neutral-900',
5189
+ soft: '',
5190
+ solid: ''
5191
+ },
5192
+ /*
5193
+ * Intent color. Only meaningful when variant is outlined / soft /
5194
+ * solid (resolved via compoundVariants below). For plain / elevated
5195
+ * this axis is ignored at the visual level — but kept in the type
5196
+ * so the prop is always available without conditional typing.
5197
+ */ color: {
5198
+ primary: '',
5199
+ secondary: '',
5200
+ success: '',
5201
+ warning: '',
5202
+ danger: '',
5203
+ info: '',
5204
+ neutral: ''
5205
+ },
5206
+ /*
5207
+ * Shadow scale — relevant for `variant='elevated'`. We keep elevation
5208
+ * as a separate axis (not folded into `variant`) so consumers can
5209
+ * dial it up/down without changing the variant. Default `0` = no
5210
+ * shadow (consistent with MUI's elevation=0).
5211
+ */ elevation: {
5212
+ 0: 'shadow-none',
5213
+ 1: 'shadow-sm',
5214
+ 2: 'shadow',
5215
+ 3: 'shadow-md',
5216
+ 4: 'shadow-lg',
5217
+ 5: 'shadow-xl'
5218
+ },
5219
+ rounded: {
5220
+ none: 'rounded-none',
5221
+ sm: 'rounded-sm',
5222
+ md: 'rounded-md',
5223
+ lg: 'rounded-lg',
5224
+ xl: 'rounded-xl',
5225
+ '2xl': 'rounded-2xl',
5226
+ full: 'rounded-full'
5227
+ },
5228
+ /*
5229
+ * Spacing — six axes (p/px/py/m/mx/my), 11 token steps each.
5230
+ * Literals enumerated explicitly so Tailwind's JIT scanner finds
5231
+ * every class. Token steps mirror @dashforge/tw-tokens spacing scale.
5232
+ */ p: {
5233
+ 0: 'p-0',
5234
+ '0.5': 'p-0.5',
5235
+ 1: 'p-1',
5236
+ 2: 'p-2',
5237
+ 3: 'p-3',
5238
+ 4: 'p-4',
5239
+ 6: 'p-6',
5240
+ 8: 'p-8',
5241
+ 12: 'p-12',
5242
+ 16: 'p-16',
5243
+ 24: 'p-24'
5244
+ },
5245
+ px: {
5246
+ 0: 'px-0',
5247
+ '0.5': 'px-0.5',
5248
+ 1: 'px-1',
5249
+ 2: 'px-2',
5250
+ 3: 'px-3',
5251
+ 4: 'px-4',
5252
+ 6: 'px-6',
5253
+ 8: 'px-8',
5254
+ 12: 'px-12',
5255
+ 16: 'px-16',
5256
+ 24: 'px-24'
5257
+ },
5258
+ py: {
5259
+ 0: 'py-0',
5260
+ '0.5': 'py-0.5',
5261
+ 1: 'py-1',
5262
+ 2: 'py-2',
5263
+ 3: 'py-3',
5264
+ 4: 'py-4',
5265
+ 6: 'py-6',
5266
+ 8: 'py-8',
5267
+ 12: 'py-12',
5268
+ 16: 'py-16',
5269
+ 24: 'py-24'
5270
+ },
5271
+ m: {
5272
+ 0: 'm-0',
5273
+ '0.5': 'm-0.5',
5274
+ 1: 'm-1',
5275
+ 2: 'm-2',
5276
+ 3: 'm-3',
5277
+ 4: 'm-4',
5278
+ 6: 'm-6',
5279
+ 8: 'm-8',
5280
+ 12: 'm-12',
5281
+ 16: 'm-16',
5282
+ 24: 'm-24'
5283
+ },
5284
+ mx: {
5285
+ 0: 'mx-0',
5286
+ '0.5': 'mx-0.5',
5287
+ 1: 'mx-1',
5288
+ 2: 'mx-2',
5289
+ 3: 'mx-3',
5290
+ 4: 'mx-4',
5291
+ 6: 'mx-6',
5292
+ 8: 'mx-8',
5293
+ 12: 'mx-12',
5294
+ 16: 'mx-16',
5295
+ 24: 'mx-24'
5296
+ },
5297
+ my: {
5298
+ 0: 'my-0',
5299
+ '0.5': 'my-0.5',
5300
+ 1: 'my-1',
5301
+ 2: 'my-2',
5302
+ 3: 'my-3',
5303
+ 4: 'my-4',
5304
+ 6: 'my-6',
5305
+ 8: 'my-8',
5306
+ 12: 'my-12',
5307
+ 16: 'my-16',
5308
+ 24: 'my-24'
5309
+ },
5310
+ fullWidth: {
5311
+ true: 'w-full'
5312
+ },
5313
+ fullHeight: {
5314
+ true: 'h-full'
5315
+ }
5316
+ },
5317
+ /*
5318
+ * Compound variants — where surface × color get their actual visual.
5319
+ * Twenty-one entries: 7 (outlined) + 7 (soft) + 7 (solid).
5320
+ * `plain` and `elevated` don't appear here (no color contribution).
5321
+ *
5322
+ * Dark-mode pair is baked in: light-mode picks the 50-300 steps,
5323
+ * dark-mode picks the 800-950 steps — both reactive to setMode()
5324
+ * via the @dashforge/tw-theme CSS variables.
5325
+ */ compoundVariants: [
5326
+ // ─── outlined × color ─────────────────────────────────────────────
5327
+ {
5328
+ variant: 'outlined',
5329
+ color: 'primary',
5330
+ class: 'border-primary-300 bg-primary-50/40 dark:border-primary-800 dark:bg-primary-950/30'
5331
+ },
5332
+ {
5333
+ variant: 'outlined',
5334
+ color: 'secondary',
5335
+ class: 'border-secondary-300 bg-secondary-50/40 dark:border-secondary-800 dark:bg-secondary-950/30'
5336
+ },
5337
+ {
5338
+ variant: 'outlined',
5339
+ color: 'success',
5340
+ class: 'border-success-300 bg-success-50/40 dark:border-success-800 dark:bg-success-950/30'
5341
+ },
5342
+ {
5343
+ variant: 'outlined',
5344
+ color: 'warning',
5345
+ class: 'border-warning-300 bg-warning-50/40 dark:border-warning-800 dark:bg-warning-950/30'
5346
+ },
5347
+ {
5348
+ variant: 'outlined',
5349
+ color: 'danger',
5350
+ class: 'border-danger-300 bg-danger-50/40 dark:border-danger-800 dark:bg-danger-950/30'
5351
+ },
5352
+ {
5353
+ variant: 'outlined',
5354
+ color: 'info',
5355
+ class: 'border-info-300 bg-info-50/40 dark:border-info-800 dark:bg-info-950/30'
5356
+ },
5357
+ {
5358
+ variant: 'outlined',
5359
+ color: 'neutral',
5360
+ class: 'border-neutral-200 bg-white dark:border-neutral-700 dark:bg-neutral-900'
5361
+ },
5362
+ // ─── soft × color ─────────────────────────────────────────────────
5363
+ {
5364
+ variant: 'soft',
5365
+ color: 'primary',
5366
+ class: 'bg-primary-100 text-primary-900 dark:bg-primary-950/50 dark:text-primary-100'
5367
+ },
5368
+ {
5369
+ variant: 'soft',
5370
+ color: 'secondary',
5371
+ class: 'bg-secondary-100 text-secondary-900 dark:bg-secondary-950/50 dark:text-secondary-100'
5372
+ },
5373
+ {
5374
+ variant: 'soft',
5375
+ color: 'success',
5376
+ class: 'bg-success-100 text-success-900 dark:bg-success-950/50 dark:text-success-100'
5377
+ },
5378
+ {
5379
+ variant: 'soft',
5380
+ color: 'warning',
5381
+ class: 'bg-warning-100 text-warning-900 dark:bg-warning-950/50 dark:text-warning-100'
5382
+ },
5383
+ {
5384
+ variant: 'soft',
5385
+ color: 'danger',
5386
+ class: 'bg-danger-100 text-danger-900 dark:bg-danger-950/50 dark:text-danger-100'
5387
+ },
5388
+ {
5389
+ variant: 'soft',
5390
+ color: 'info',
5391
+ class: 'bg-info-100 text-info-900 dark:bg-info-950/50 dark:text-info-100'
5392
+ },
5393
+ {
5394
+ variant: 'soft',
5395
+ color: 'neutral',
5396
+ class: 'bg-neutral-100 text-neutral-900 dark:bg-neutral-800 dark:text-neutral-100'
5397
+ },
5398
+ // ─── solid × color ────────────────────────────────────────────────
5399
+ {
5400
+ variant: 'solid',
5401
+ color: 'primary',
5402
+ class: 'bg-primary-600 text-white dark:bg-primary-500'
5403
+ },
5404
+ {
5405
+ variant: 'solid',
5406
+ color: 'secondary',
5407
+ class: 'bg-secondary-600 text-white dark:bg-secondary-500'
5408
+ },
5409
+ {
5410
+ variant: 'solid',
5411
+ color: 'success',
5412
+ class: 'bg-success-600 text-white dark:bg-success-500'
5413
+ },
5414
+ {
5415
+ variant: 'solid',
5416
+ color: 'warning',
5417
+ class: 'bg-warning-500 text-white dark:bg-warning-600'
5418
+ },
5419
+ {
5420
+ variant: 'solid',
5421
+ color: 'danger',
5422
+ class: 'bg-danger-600 text-white dark:bg-danger-500'
5423
+ },
5424
+ {
5425
+ variant: 'solid',
5426
+ color: 'info',
5427
+ class: 'bg-info-600 text-white dark:bg-info-500'
5428
+ },
5429
+ {
5430
+ variant: 'solid',
5431
+ color: 'neutral',
5432
+ class: 'bg-neutral-900 text-white dark:bg-neutral-100 dark:text-neutral-900'
5433
+ }
5434
+ ],
5435
+ defaultVariants: {
5436
+ variant: 'plain',
5437
+ color: 'neutral',
5438
+ elevation: 0,
5439
+ rounded: 'none'
5440
+ }
5441
+ });
5442
+
5443
+ /**
5444
+ * `<Box>` — the surface primitive of @dashforge/tw.
5445
+ *
5446
+ * What it IS:
5447
+ * A polymorphic container with typed surface variants (plain /
5448
+ * outlined / elevated / soft / solid) × 7 intent colours, plus
5449
+ * spacing / sizing / rounded / elevation as enumerated token-scale
5450
+ * props. Replaces MUI's Box + Paper + Card + Surface in one component.
5451
+ *
5452
+ * What it is NOT (deliberate, enforced by the prop set):
5453
+ * • Not a flex container → use <Stack>
5454
+ * • Not a grid container → use <Grid>
5455
+ * • Not a paragraph → use <Typography>
5456
+ * • Not a button → use <Button>
5457
+ *
5458
+ * The "Box is not flex" rule is the spine of the layout layer. Without
5459
+ * it, every `<div>` in an app gravitates back to Box and the surface
5460
+ * vs layout distinction collapses. Box's job is the SURFACE around
5461
+ * content; Stack/Grid's job is the ARRANGEMENT of content. Two
5462
+ * primitives, two responsibilities.
5463
+ *
5464
+ * When BOTH `as` and `asChild` are passed, `asChild` wins. Same rule
5465
+ * as Typography — documented to avoid the dimension-bloat of a
5466
+ * discriminated union over an 11-axis props type.
5467
+ *
5468
+ * Layering:
5469
+ * • Sits BENEATH every visual chrome — every card, every panel,
5470
+ * every section background.
5471
+ * • Composes ON TOP of @dashforge/tw-tokens (color + spacing + radius
5472
+ * + shadow scales) via the dashforgePreset CSS variables.
5473
+ * • Pairs with Typography for text content, Stack/Grid for layout
5474
+ * children inside it.
5475
+ */ const Box = /*#__PURE__*/ forwardRef(function Box(props, ref) {
5476
+ const { variant, color, elevation, rounded, p, px, py, m, mx, my, fullWidth, fullHeight, as, asChild = false, sx, children } = props, rest = _object_without_properties_loose(props, [
5477
+ "variant",
5478
+ "color",
5479
+ "elevation",
5480
+ "rounded",
5481
+ "p",
5482
+ "px",
5483
+ "py",
5484
+ "m",
5485
+ "mx",
5486
+ "my",
5487
+ "fullWidth",
5488
+ "fullHeight",
5489
+ "as",
5490
+ "asChild",
5491
+ "sx",
5492
+ "children"
5493
+ ]);
5494
+ const classes = cn(boxVariants({
5495
+ variant,
5496
+ color,
5497
+ elevation,
5498
+ rounded,
5499
+ p,
5500
+ px,
5501
+ py,
5502
+ m,
5503
+ mx,
5504
+ my,
5505
+ fullWidth,
5506
+ fullHeight
5507
+ }), sx);
5508
+ if (asChild) {
5509
+ return jsx(Slot, _extends({
5510
+ ref: ref,
5511
+ className: classes
5512
+ }, rest, {
5513
+ children: children
5514
+ }));
5515
+ }
5516
+ const Tag = as != null ? as : 'div';
5517
+ return jsx(Tag, _extends({
5518
+ ref: ref,
5519
+ className: classes
5520
+ }, rest, {
5521
+ children: children
5522
+ }));
5523
+ });
5524
+ Box.displayName = 'Box';
5525
+
5526
+ /**
5527
+ * `stackVariants` — flex container 1D, the layout primitive of @dashforge/tw.
5528
+ *
5529
+ * Architectural role (planned with the user — F9 deep dive):
5530
+ *
5531
+ * Stack is the ONLY way to do flex in this library. Box is the
5532
+ * surface (border / bg / shadow); Stack is the arrangement (direction
5533
+ * / align / justify / gap). Two primitives, two responsibilities,
5534
+ * zero overlap.
5535
+ *
5536
+ * This rules out the MUI failure mode where every `<Box display="flex"
5537
+ * gap={2}>` quietly becomes the de-facto flex container, drowning
5538
+ * the surface vs layout distinction. Here, if you see `<Stack>` in
5539
+ * the JSX, you KNOW it's flex; if you see `<Box>`, you KNOW it's
5540
+ * not. The component name carries the intent.
5541
+ *
5542
+ * Axes:
5543
+ * • direction — row / col (+ reverse variants)
5544
+ * • align / justify — cross-axis / main-axis alignment
5545
+ * • gap — token-scale step (mirror Box spacing scale)
5546
+ * • wrap — flex-wrap
5547
+ * • divider — runtime-only (handled in Stack.tsx, not here)
5548
+ *
5549
+ * Sizing (`fullWidth`, `fullHeight`) is duplicated from Box because
5550
+ * Stack often plays the role of a full-width strip / full-height
5551
+ * column — re-typing `sx="w-full"` every time would be friction.
5552
+ */ const stackVariants = tv({
5553
+ base: 'flex',
5554
+ variants: {
5555
+ direction: {
5556
+ row: 'flex-row',
5557
+ col: 'flex-col',
5558
+ 'row-reverse': 'flex-row-reverse',
5559
+ 'col-reverse': 'flex-col-reverse'
5560
+ },
5561
+ align: {
5562
+ start: 'items-start',
5563
+ center: 'items-center',
5564
+ end: 'items-end',
5565
+ stretch: 'items-stretch',
5566
+ baseline: 'items-baseline'
5567
+ },
5568
+ justify: {
5569
+ start: 'justify-start',
5570
+ center: 'justify-center',
5571
+ end: 'justify-end',
5572
+ between: 'justify-between',
5573
+ around: 'justify-around',
5574
+ evenly: 'justify-evenly'
5575
+ },
5576
+ /*
5577
+ * `gap` — explicit literal mapping for the 11 token-scale steps.
5578
+ * Same set as Box's spacing axes (p, m, etc.) so muscle memory
5579
+ * carries over: `<Stack gap={4}>` aligns visually with `<Box p={4}>`.
5580
+ */ gap: {
5581
+ 0: 'gap-0',
5582
+ '0.5': 'gap-0.5',
5583
+ 1: 'gap-1',
5584
+ 2: 'gap-2',
5585
+ 3: 'gap-3',
5586
+ 4: 'gap-4',
5587
+ 6: 'gap-6',
5588
+ 8: 'gap-8',
5589
+ 12: 'gap-12',
5590
+ 16: 'gap-16',
5591
+ 24: 'gap-24'
5592
+ },
5593
+ wrap: {
5594
+ true: 'flex-wrap'
5595
+ },
5596
+ fullWidth: {
5597
+ true: 'w-full'
5598
+ },
5599
+ fullHeight: {
5600
+ true: 'h-full'
5601
+ }
5602
+ },
5603
+ defaultVariants: {
5604
+ direction: 'col'
5605
+ }
5606
+ });
5607
+
5608
+ /**
5609
+ * Walk the children, inserting `divider` BETWEEN every consecutive pair.
5610
+ *
5611
+ * Implementation notes:
5612
+ * • `React.Children.toArray` assigns auto-keys but does NOT recursively
5613
+ * flatten Fragments — it treats them as opaque single children. So
5614
+ * `<Stack><><a/><b/></><c/></Stack>` is 2 boundaries (fragment + c),
5615
+ * yielding ONE divider. Hoist items out of the fragment when you
5616
+ * need a divider between them. Documented + asserted in the tests.
5617
+ * • Dividers are wrapped in `<Fragment>` with a deterministic key
5618
+ * derived from the boundary index — stable across re-renders so
5619
+ * React reconciles correctly when children re-order.
5620
+ * • If `divider` is a valid element, we `cloneElement` once per
5621
+ * boundary (cheaper than re-rendering the JSX expression N-1 times).
5622
+ * For string/number dividers we wrap in a span automatically.
5623
+ */ function interleaveDividers(children, divider) {
5624
+ const items = Children.toArray(children);
5625
+ if (items.length <= 1) return items;
5626
+ const result = [];
5627
+ items.forEach((child, i)=>{
5628
+ result.push(child);
5629
+ if (i < items.length - 1) {
5630
+ const key = `df-stack-divider-${i}`;
5631
+ if (/*#__PURE__*/ isValidElement(divider)) {
5632
+ result.push(/*#__PURE__*/ cloneElement(divider, {
5633
+ key
5634
+ }));
5635
+ } else {
5636
+ result.push(jsx(Fragment, {
5637
+ children: divider
5638
+ }, key));
5639
+ }
5640
+ }
5641
+ });
5642
+ return result;
5643
+ }
5644
+ /**
5645
+ * `<Stack>` — flex container 1D, the layout primitive.
5646
+ *
5647
+ * This is the ONLY component in @dashforge/tw that does flex. Box
5648
+ * doesn't, Grid does CSS Grid (not flex). The strict naming → engine
5649
+ * mapping is the whole point: when you read `<Stack>` in a JSX tree,
5650
+ * you instantly know it's flex. No `<Box display="flex" ...>` traps.
5651
+ *
5652
+ * Direction defaults to `'col'` (vertical stack) — the most common
5653
+ * case for forms, sidebars, settings panels. Pass `direction="row"`
5654
+ * for horizontal layouts (toolbars, button rows, breadcrumbs).
5655
+ *
5656
+ * The `divider` prop is the runtime-only piece: TV can't encode the
5657
+ * "render this between each child" logic as a class, so we walk the
5658
+ * children at render time. The walk is O(n); for n ≤ ~10 (typical
5659
+ * Stack content) the cost is negligible. For very long Stacks (1000+
5660
+ * items), prefer to render dividers as part of each child instead.
5661
+ *
5662
+ * When `asChild` is true, the divider prop is silently ignored — Slot
5663
+ * requires a single child, and the N-1 insertion has nowhere to act.
5664
+ */ const Stack = /*#__PURE__*/ forwardRef(function Stack(props, ref) {
5665
+ const { direction, align, justify, gap, wrap, fullWidth, fullHeight, divider, as, asChild = false, sx, children } = props, rest = _object_without_properties_loose(props, [
5666
+ "direction",
5667
+ "align",
5668
+ "justify",
5669
+ "gap",
5670
+ "wrap",
5671
+ "fullWidth",
5672
+ "fullHeight",
5673
+ "divider",
5674
+ "as",
5675
+ "asChild",
5676
+ "sx",
5677
+ "children"
5678
+ ]);
5679
+ const classes = cn(stackVariants({
5680
+ direction,
5681
+ align,
5682
+ justify,
5683
+ gap,
5684
+ wrap,
5685
+ fullWidth,
5686
+ fullHeight
5687
+ }), sx);
5688
+ if (asChild) {
5689
+ // Slot expects a single child; divider has no place here.
5690
+ return jsx(Slot, _extends({
5691
+ ref: ref,
5692
+ className: classes
5693
+ }, rest, {
5694
+ children: children
5695
+ }));
5696
+ }
5697
+ const Tag = as != null ? as : 'div';
5698
+ const content = divider != null ? interleaveDividers(children, divider) : children;
5699
+ return jsx(Tag, _extends({
5700
+ ref: ref,
5701
+ className: classes
5702
+ }, rest, {
5703
+ children: content
5704
+ }));
5705
+ });
5706
+ Stack.displayName = 'Stack';
5707
+
5708
+ /**
5709
+ * `gridVariants` — CSS Grid container + item, polymorphic in role.
5710
+ *
5711
+ * Architectural choice (planned with the user — F9 deep dive):
5712
+ *
5713
+ * Pattern is MUI Grid v2 from the API side (`<Grid container>` +
5714
+ * `<Grid xs={6}>`), but engine is REAL CSS Grid under the hood
5715
+ * (`display: grid` + `grid-template-columns: repeat(N, 1fr)` +
5716
+ * `col-span-*`). MUI v2 uses flexbox + basis percentages — historical
5717
+ * IE11 reasons. In 2026, CSS Grid is universally supported AND
5718
+ * Tailwind ships `grid-cols-*` / `col-span-*` natively, so flexbox
5719
+ * would be a downgrade.
5720
+ *
5721
+ * Two distinct shapes coexist in the same TV recipe:
5722
+ *
5723
+ * • container=true — display:grid + cols + gap + autoFlow
5724
+ * • container=false — col-span at each breakpoint (xs/sm/md/lg/xl)
5725
+ *
5726
+ * The discriminated union lives in grid.types.ts (TypeScript-level);
5727
+ * here the TV catalogue exposes every axis so the consumer's choice
5728
+ * triggers the right classes regardless of which role the component
5729
+ * is playing. Unused axes produce no classes (TV silently drops
5730
+ * undefined variants), so a container Grid never accidentally emits
5731
+ * `col-span-6` and an item Grid never accidentally emits `grid`.
5732
+ *
5733
+ * Mapping table size:
5734
+ * • cols : 6 entries (12, 6, 4, 3, 2, 1)
5735
+ * • autoFlow : 5 entries
5736
+ * • span axes (5×14) : 70 entries (xs/sm/md/lg/xl × {1..12, auto, full})
5737
+ * • gap / gapX / gapY : 33 entries (11 steps × 3 axes)
5738
+ *
5739
+ * Total: ~115 literal class names. All explicit, all Tailwind-scannable.
5740
+ * Bundle CSS impact when fully exercised: ~3 KB gz (mostly the
5741
+ * responsive col-span set).
5742
+ */ const gridVariants = tv({
5743
+ base: '',
5744
+ variants: {
5745
+ // ─── CONTAINER role ────────────────────────────────────────────────
5746
+ container: {
5747
+ true: 'grid'
5748
+ },
5749
+ cols: {
5750
+ 1: 'grid-cols-1',
5751
+ 2: 'grid-cols-2',
5752
+ 3: 'grid-cols-3',
5753
+ 4: 'grid-cols-4',
5754
+ 6: 'grid-cols-6',
5755
+ 12: 'grid-cols-12'
5756
+ },
5757
+ autoFlow: {
5758
+ row: 'grid-flow-row',
5759
+ col: 'grid-flow-col',
5760
+ dense: 'grid-flow-dense',
5761
+ 'row-dense': 'grid-flow-row-dense',
5762
+ 'col-dense': 'grid-flow-col-dense'
5763
+ },
5764
+ /*
5765
+ * Container gap — token-scale step. Same set as Box/Stack so muscle
5766
+ * memory carries across primitives.
5767
+ */ spacing: {
5768
+ 0: 'gap-0',
5769
+ '0.5': 'gap-0.5',
5770
+ 1: 'gap-1',
5771
+ 2: 'gap-2',
5772
+ 3: 'gap-3',
5773
+ 4: 'gap-4',
5774
+ 6: 'gap-6',
5775
+ 8: 'gap-8',
5776
+ 12: 'gap-12',
5777
+ 16: 'gap-16',
5778
+ 24: 'gap-24'
5779
+ },
5780
+ spacingX: {
5781
+ 0: 'gap-x-0',
5782
+ '0.5': 'gap-x-0.5',
5783
+ 1: 'gap-x-1',
5784
+ 2: 'gap-x-2',
5785
+ 3: 'gap-x-3',
5786
+ 4: 'gap-x-4',
5787
+ 6: 'gap-x-6',
5788
+ 8: 'gap-x-8',
5789
+ 12: 'gap-x-12',
5790
+ 16: 'gap-x-16',
5791
+ 24: 'gap-x-24'
5792
+ },
5793
+ spacingY: {
5794
+ 0: 'gap-y-0',
5795
+ '0.5': 'gap-y-0.5',
5796
+ 1: 'gap-y-1',
5797
+ 2: 'gap-y-2',
5798
+ 3: 'gap-y-3',
5799
+ 4: 'gap-y-4',
5800
+ 6: 'gap-y-6',
5801
+ 8: 'gap-y-8',
5802
+ 12: 'gap-y-12',
5803
+ 16: 'gap-y-16',
5804
+ 24: 'gap-y-24'
5805
+ },
5806
+ // ─── ITEM role — col-span at each breakpoint ──────────────────────
5807
+ /*
5808
+ * `xs` is the base breakpoint (no prefix) — Tailwind's mobile-first
5809
+ * convention. `sm/md/lg/xl` cascade up from there.
5810
+ *
5811
+ * Special values:
5812
+ * • 'auto' → col-auto (content-sized)
5813
+ * • 'full' → col-span-full (span all columns regardless of count)
5814
+ */ xs: {
5815
+ 1: 'col-span-1',
5816
+ 2: 'col-span-2',
5817
+ 3: 'col-span-3',
5818
+ 4: 'col-span-4',
5819
+ 5: 'col-span-5',
5820
+ 6: 'col-span-6',
5821
+ 7: 'col-span-7',
5822
+ 8: 'col-span-8',
5823
+ 9: 'col-span-9',
5824
+ 10: 'col-span-10',
5825
+ 11: 'col-span-11',
5826
+ 12: 'col-span-12',
5827
+ auto: 'col-auto',
5828
+ full: 'col-span-full'
5829
+ },
5830
+ sm: {
5831
+ 1: 'sm:col-span-1',
5832
+ 2: 'sm:col-span-2',
5833
+ 3: 'sm:col-span-3',
5834
+ 4: 'sm:col-span-4',
5835
+ 5: 'sm:col-span-5',
5836
+ 6: 'sm:col-span-6',
5837
+ 7: 'sm:col-span-7',
5838
+ 8: 'sm:col-span-8',
5839
+ 9: 'sm:col-span-9',
5840
+ 10: 'sm:col-span-10',
5841
+ 11: 'sm:col-span-11',
5842
+ 12: 'sm:col-span-12',
5843
+ auto: 'sm:col-auto',
5844
+ full: 'sm:col-span-full'
5845
+ },
5846
+ md: {
5847
+ 1: 'md:col-span-1',
5848
+ 2: 'md:col-span-2',
5849
+ 3: 'md:col-span-3',
5850
+ 4: 'md:col-span-4',
5851
+ 5: 'md:col-span-5',
5852
+ 6: 'md:col-span-6',
5853
+ 7: 'md:col-span-7',
5854
+ 8: 'md:col-span-8',
5855
+ 9: 'md:col-span-9',
5856
+ 10: 'md:col-span-10',
5857
+ 11: 'md:col-span-11',
5858
+ 12: 'md:col-span-12',
5859
+ auto: 'md:col-auto',
5860
+ full: 'md:col-span-full'
5861
+ },
5862
+ lg: {
5863
+ 1: 'lg:col-span-1',
5864
+ 2: 'lg:col-span-2',
5865
+ 3: 'lg:col-span-3',
5866
+ 4: 'lg:col-span-4',
5867
+ 5: 'lg:col-span-5',
5868
+ 6: 'lg:col-span-6',
5869
+ 7: 'lg:col-span-7',
5870
+ 8: 'lg:col-span-8',
5871
+ 9: 'lg:col-span-9',
5872
+ 10: 'lg:col-span-10',
5873
+ 11: 'lg:col-span-11',
5874
+ 12: 'lg:col-span-12',
5875
+ auto: 'lg:col-auto',
5876
+ full: 'lg:col-span-full'
5877
+ },
5878
+ xl: {
5879
+ 1: 'xl:col-span-1',
5880
+ 2: 'xl:col-span-2',
5881
+ 3: 'xl:col-span-3',
5882
+ 4: 'xl:col-span-4',
5883
+ 5: 'xl:col-span-5',
5884
+ 6: 'xl:col-span-6',
5885
+ 7: 'xl:col-span-7',
5886
+ 8: 'xl:col-span-8',
5887
+ 9: 'xl:col-span-9',
5888
+ 10: 'xl:col-span-10',
5889
+ 11: 'xl:col-span-11',
5890
+ 12: 'xl:col-span-12',
5891
+ auto: 'xl:col-auto',
5892
+ full: 'xl:col-span-full'
5893
+ }
5894
+ }
5895
+ });
5896
+
5897
+ /**
5898
+ * `<Grid>` — CSS Grid container + item, polymorphic in role.
5899
+ *
5900
+ * API surface mirrors MUI Grid v2:
5901
+ * • <Grid container spacing={4} cols={12}> ← container
5902
+ * • <Grid xs={12} md={6}> ← item
5903
+ *
5904
+ * Engine is REAL CSS Grid (not flexbox, unlike MUI v2 internals).
5905
+ * Benefits over flexbox-based implementation:
5906
+ * • Native `gap` (no negative-margin tricks)
5907
+ * • `col-span-N` maps directly to `grid-column: span N`
5908
+ * • Vertical alignment without manual `align-items` per item
5909
+ * • Auto-flow for dense packing without DOM gymnastics
5910
+ *
5911
+ * TypeScript discriminated union (see grid.types.ts) makes mixing
5912
+ * impossible: `<Grid container xs={6}>` won't compile. Same for the
5913
+ * inverse. IntelliSense filters by role.
5914
+ *
5915
+ * Defaults at the component level (not at the TV level — see the
5916
+ * variants file for the rationale):
5917
+ * • container: cols defaults to 12 (MUI convention)
5918
+ * • item: xs defaults to 'full' so a forgotten prop doesn't
5919
+ * collapse the cell to 1/12th of a row
5920
+ *
5921
+ * `asChild` on a CONTAINER is technically allowed but rarely useful:
5922
+ * `display:grid` only applies to direct children, so the slotted
5923
+ * element has to already be a viable container parent. Use sparingly;
5924
+ * test asserts the wrapping still emits the right class chain.
5925
+ */ const Grid = /*#__PURE__*/ forwardRef(function Grid(props, ref) {
5926
+ const { as, asChild = false, sx, children } = props, rest = _object_without_properties_loose(props, [
5927
+ "as",
5928
+ "asChild",
5929
+ "sx",
5930
+ "children"
5931
+ ]);
5932
+ /*
5933
+ * Discriminated union runtime branch.
5934
+ *
5935
+ * TS narrows GridProps to ONE of the two shapes (container or item)
5936
+ * at the call site, but inside this function body we receive the
5937
+ * union — so we manually narrow via a single cast per branch.
5938
+ * `as GridContainerProps` / `as GridItemProps` is sound because
5939
+ * `container === true` is the literal discriminator declared in
5940
+ * the types file; the cast just makes TS read the right branch.
5941
+ */ const isContainer = props.container === true;
5942
+ let classes;
5943
+ let containerPayload = null;
5944
+ let itemPayload = null;
5945
+ if (isContainer) {
5946
+ var _containerPayload_cols;
5947
+ containerPayload = props;
5948
+ /*
5949
+ * Container branch — pass ONLY container axes to TV. Default cols
5950
+ * to 12 (MUI v2 convention) so a forgotten prop doesn't collapse
5951
+ * to grid-cols-1. The spacing axes are passed straight through;
5952
+ * TV silently drops undefined values.
5953
+ */ classes = cn(gridVariants({
5954
+ container: true,
5955
+ cols: (_containerPayload_cols = containerPayload.cols) != null ? _containerPayload_cols : 12,
5956
+ spacing: containerPayload.spacing,
5957
+ spacingX: containerPayload.spacingX,
5958
+ spacingY: containerPayload.spacingY,
5959
+ autoFlow: containerPayload.autoFlow
5960
+ }), sx);
5961
+ } else {
5962
+ var _itemPayload_xs;
5963
+ itemPayload = props;
5964
+ /*
5965
+ * Item branch — default `xs` to 'full' so a forgotten breakpoint
5966
+ * prop doesn't collapse the cell to grid-auto's minimum width.
5967
+ */ classes = cn(gridVariants({
5968
+ xs: (_itemPayload_xs = itemPayload.xs) != null ? _itemPayload_xs : 'full',
5969
+ sm: itemPayload.sm,
5970
+ md: itemPayload.md,
5971
+ lg: itemPayload.lg,
5972
+ xl: itemPayload.xl
5973
+ }), sx);
5974
+ }
5975
+ /*
5976
+ * Strip role-specific props from `rest` before spreading on the DOM
5977
+ * element. Without this, React warns:
5978
+ * "React does not recognize the `cols` prop on a DOM element"
5979
+ */ const domRest = _extends({}, rest);
5980
+ if (containerPayload) {
5981
+ delete domRest.cols;
5982
+ delete domRest.spacing;
5983
+ delete domRest.spacingX;
5984
+ delete domRest.spacingY;
5985
+ delete domRest.autoFlow;
5986
+ }
5987
+ if (itemPayload) {
5988
+ delete domRest.xs;
5989
+ delete domRest.sm;
5990
+ delete domRest.md;
5991
+ delete domRest.lg;
5992
+ delete domRest.xl;
5993
+ }
5994
+ // `container` discriminator itself must never reach the DOM.
5995
+ delete domRest.container;
5996
+ if (asChild) {
5997
+ return jsx(Slot, _extends({
5998
+ ref: ref,
5999
+ className: classes
6000
+ }, domRest, {
6001
+ children: children
6002
+ }));
6003
+ }
6004
+ const Tag = as != null ? as : 'div';
6005
+ return jsx(Tag, _extends({
6006
+ ref: ref,
6007
+ className: classes
6008
+ }, domRest, {
6009
+ children: children
6010
+ }));
6011
+ });
6012
+ Grid.displayName = 'Grid';
6013
+
6014
+ /**
6015
+ * `containerVariants` — centered max-width wrapper for page layouts.
6016
+ *
6017
+ * Architectural role:
6018
+ *
6019
+ * Every web app has the same pattern at the page-root level: a div
6020
+ * that's `mx-auto`, capped at some `max-w-*`, with responsive
6021
+ * horizontal padding. Without Container, every page rewrites:
6022
+ *
6023
+ * <div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
6024
+ *
6025
+ * Container collapses that into one typed prop set, with size names
6026
+ * that mirror Tailwind's breakpoint vocabulary so the muscle memory
6027
+ * carries over (`size="lg"` ↔ `max-w-screen-lg`).
6028
+ *
6029
+ * Size axis — maps to Tailwind's `max-w-screen-*` aliases:
6030
+ * • sm → max-w-screen-sm (640px)
6031
+ * • md → max-w-screen-md (768px)
6032
+ * • lg → max-w-screen-lg (1024px) — most common doc/content cap
6033
+ * • xl → max-w-screen-xl (1280px) — default; comfortable for full apps
6034
+ * • 2xl → max-w-screen-2xl (1536px) — wide dashboards
6035
+ * • fluid → no max-width at all (full bleed, padding still applies)
6036
+ *
6037
+ * Padding axis (`px`):
6038
+ * • true (default) — responsive horizontal padding (px-4 sm:px-6 lg:px-8)
6039
+ * The canonical Tailwind responsive padding ramp. Designed to keep
6040
+ * edges from kissing the viewport on mobile and breathing more on
6041
+ * larger screens.
6042
+ * • false — no padding. Use when the consumer wants full bleed AND
6043
+ * handles edge padding inside (e.g. a hero section with its own
6044
+ * internal spacing scale).
6045
+ *
6046
+ * Center content axis (`centerContent`):
6047
+ * • true — turns the Container into a flex column with items-center
6048
+ * so the page content stacks centered horizontally. Common for
6049
+ * marketing pages, sign-in flows, "single artifact" layouts.
6050
+ * • false (default) — children flow normally (block stacking).
6051
+ */ const containerVariants = tv({
6052
+ base: 'mx-auto w-full',
6053
+ variants: {
6054
+ size: {
6055
+ sm: 'max-w-screen-sm',
6056
+ md: 'max-w-screen-md',
6057
+ lg: 'max-w-screen-lg',
6058
+ xl: 'max-w-screen-xl',
6059
+ '2xl': 'max-w-screen-2xl',
6060
+ fluid: ''
6061
+ },
6062
+ /*
6063
+ * Responsive padding ramp. `false` skips it entirely so the
6064
+ * consumer can supply custom padding via `sx` when needed.
6065
+ */ px: {
6066
+ true: 'px-4 sm:px-6 lg:px-8',
6067
+ false: ''
6068
+ },
6069
+ /*
6070
+ * Stacks children centered. Mutually compatible with all sizes —
6071
+ * a fluid container with centerContent is the canonical "marketing
6072
+ * hero" layout.
6073
+ */ centerContent: {
6074
+ true: 'flex flex-col items-center',
6075
+ false: ''
6076
+ }
6077
+ },
6078
+ defaultVariants: {
6079
+ size: 'xl',
6080
+ px: true,
6081
+ centerContent: false
6082
+ }
6083
+ });
6084
+
6085
+ /**
6086
+ * `<Container>` — centered max-width wrapper for page-level layouts.
6087
+ *
6088
+ * The pattern is universal: every page-root `<div>` in a non-trivial
6089
+ * web app boils down to "mx-auto + max-w-X + responsive horizontal
6090
+ * padding". Container collapses that into a typed prop set so the
6091
+ * decision lives in ONE place per app section (the Container size),
6092
+ * not scattered as utility chains across every page file.
6093
+ *
6094
+ * Default size is `'xl'` (1280px) — comfortable for full app shells
6095
+ * with a left nav + main + optional right rail. Drop to `'lg'` (1024px)
6096
+ * for content-heavy docs/marketing, jump to `'2xl'` (1536px) for wide
6097
+ * dashboards.
6098
+ *
6099
+ * Composition pattern — Container at the page root, Stack/Grid inside:
6100
+ *
6101
+ * <Container size="lg" as="main">
6102
+ * <Stack gap={8}>
6103
+ * <Typography variant="h1">Page title</Typography>
6104
+ * <Grid container spacing={6}>
6105
+ * <Grid xs={12} md={6}>...</Grid>
6106
+ * </Grid>
6107
+ * </Stack>
6108
+ * </Container>
6109
+ *
6110
+ * Container handles the page chrome (centered, padded, capped width);
6111
+ * Stack/Grid handle the actual layout of children. Two concerns, two
6112
+ * primitives.
6113
+ *
6114
+ * Polymorphism rule (same as Typography/Box/Stack/Grid): when both
6115
+ * `as` and `asChild` are passed, `asChild` wins.
6116
+ */ const Container = /*#__PURE__*/ forwardRef(function Container(props, ref) {
6117
+ const { size, px, centerContent, as, asChild = false, sx, children } = props, rest = _object_without_properties_loose(props, [
6118
+ "size",
6119
+ "px",
6120
+ "centerContent",
6121
+ "as",
6122
+ "asChild",
6123
+ "sx",
6124
+ "children"
6125
+ ]);
6126
+ const classes = cn(containerVariants({
6127
+ size,
6128
+ px,
6129
+ centerContent
6130
+ }), sx);
6131
+ if (asChild) {
6132
+ return jsx(Slot, _extends({
6133
+ ref: ref,
6134
+ className: classes
6135
+ }, rest, {
6136
+ children: children
6137
+ }));
6138
+ }
6139
+ const Tag = as != null ? as : 'div';
6140
+ return jsx(Tag, _extends({
6141
+ ref: ref,
6142
+ className: classes
6143
+ }, rest, {
6144
+ children: children
6145
+ }));
6146
+ });
6147
+ Container.displayName = 'Container';
6148
+
6149
+ /**
6150
+ * `dividerVariants` — visual separator with two rendering modes (line-only
6151
+ * vs labeled). Two interconnected TV recipes:
6152
+ *
6153
+ * • `dividerVariants` — root container (block layout, alignment)
6154
+ * • `dividerLineVariants` — the actual line segments (border style,
6155
+ * color, orientation)
6156
+ *
6157
+ * Two recipes (not one with slots) because the labeled mode renders
6158
+ * THREE elements (left line · label · right line) while the line-only
6159
+ * mode is ONE element. Splitting keeps each TV catalogue small and
6160
+ * the type unions narrow.
6161
+ *
6162
+ * Mental model:
6163
+ * • Without `children` → renders an `<hr>` (or a div if vertical)
6164
+ * with the line styles applied directly.
6165
+ * • With `children` → renders a flex row with two `<span>` line
6166
+ * segments either side of the label. The label's flex-shrink keeps
6167
+ * it from being squashed; the line segments share the remaining
6168
+ * space according to `align`.
6169
+ *
6170
+ * a11y: the root always carries `role="separator"` + `aria-orientation`
6171
+ * — handled in Divider.tsx, not in TV (it's a prop, not a class).
6172
+ */ /*
6173
+ * Root container — only relevant when label is present (flex layout).
6174
+ * For line-only, the root IS the line.
6175
+ */ const dividerVariants = tv({
6176
+ base: 'flex items-center',
6177
+ variants: {
6178
+ orientation: {
6179
+ horizontal: 'w-full',
6180
+ vertical: 'h-full flex-col'
6181
+ },
6182
+ /*
6183
+ * Label alignment along the divider's main axis. Implemented by
6184
+ * setting the flex-basis of the two line segments asymmetrically
6185
+ * — handled in the variants for `dividerLineVariants` below via
6186
+ * compound logic. The root just needs the flex layout.
6187
+ */ align: {
6188
+ start: '',
6189
+ center: '',
6190
+ end: ''
6191
+ }
6192
+ },
6193
+ defaultVariants: {
6194
+ orientation: 'horizontal',
6195
+ align: 'center'
6196
+ }
6197
+ });
6198
+ /*
6199
+ * Line segment(s).
6200
+ *
6201
+ * Border styles (solid/dashed/dotted) are applied as `border-t-{style}`
6202
+ * for horizontal, `border-l-{style}` for vertical. Color drives the
6203
+ * border-* color token to the intent.
6204
+ *
6205
+ * The `segment` axis distinguishes whether this is a line-only render
6206
+ * (full width) vs a labeled-mode segment (flex-1 grows to share space).
6207
+ */ const dividerLineVariants = tv({
6208
+ base: '',
6209
+ variants: {
6210
+ orientation: {
6211
+ horizontal: 'h-0 border-t',
6212
+ vertical: 'w-0 border-l self-stretch'
6213
+ },
6214
+ variant: {
6215
+ solid: 'border-solid',
6216
+ dashed: 'border-dashed',
6217
+ dotted: 'border-dotted'
6218
+ },
6219
+ color: {
6220
+ neutral: 'border-neutral-200 dark:border-neutral-800',
6221
+ primary: 'border-primary-300 dark:border-primary-700',
6222
+ secondary: 'border-secondary-300 dark:border-secondary-700',
6223
+ success: 'border-success-300 dark:border-success-700',
6224
+ warning: 'border-warning-300 dark:border-warning-700',
6225
+ danger: 'border-danger-300 dark:border-danger-700',
6226
+ info: 'border-info-300 dark:border-info-700'
6227
+ },
6228
+ /*
6229
+ * `segment` controls whether the line spans full width (line-only
6230
+ * mode) or grows to fill space (labeled-mode flex segment).
6231
+ */ segment: {
6232
+ full: 'w-full',
6233
+ grow: 'flex-1'
6234
+ }
6235
+ },
6236
+ defaultVariants: {
6237
+ orientation: 'horizontal',
6238
+ variant: 'solid',
6239
+ color: 'neutral',
6240
+ segment: 'full'
6241
+ }
6242
+ });
6243
+
6244
+ /**
6245
+ * `<Divider>` — visual separator. Two modes:
6246
+ *
6247
+ * 1. Line-only (no children):
6248
+ *
6249
+ * <Divider />
6250
+ * → <hr role="separator" aria-orientation="horizontal" />
6251
+ *
6252
+ * Renders a single horizontal line via `border-t-*` on an `<hr>`
6253
+ * (which by default has no `display`, no margin in our reset, just
6254
+ * acts as the border carrier).
6255
+ *
6256
+ * 2. Labeled (with children):
6257
+ *
6258
+ * <Divider><Typography variant="overline">OR</Typography></Divider>
6259
+ * → <div role="separator" aria-orientation="horizontal">
6260
+ * <span aria-hidden className="flex-1 border-t" />
6261
+ * <span className="px-3">OR</span>
6262
+ * <span aria-hidden className="flex-1 border-t" />
6263
+ * </div>
6264
+ *
6265
+ * Two line segments share the available space; the label sits
6266
+ * between them with horizontal padding for breathing room. Each
6267
+ * line segment is `aria-hidden` (the role="separator" on the root
6268
+ * conveys the separator semantics — duplicating it would confuse
6269
+ * screen readers).
6270
+ *
6271
+ * Alignment:
6272
+ * • align="start" → label hugs the left, right segment grows
6273
+ * • align="center" (default) → both segments equal
6274
+ * • align="end" → label hugs the right, left segment grows
6275
+ *
6276
+ * Implemented by setting `flex-basis: 0` to either segment to keep
6277
+ * it minimal. We use `basis-[2rem]` (32px short stub) rather than
6278
+ * `basis-0` so a tiny line still hints at the divider on the squashed
6279
+ * side — pure flex-1 vs basis-0 would collapse it invisibly.
6280
+ *
6281
+ * Vertical orientation:
6282
+ * The line orientation flips (border-l instead of border-t). Vertical
6283
+ * labeled dividers are RARE in practice — supported for parity but
6284
+ * the typical use is line-only between flex row items.
6285
+ *
6286
+ * a11y:
6287
+ * • Root element gets `role="separator"` (or implicit via `<hr>`)
6288
+ * • `aria-orientation` is set explicitly so AT knows the axis
6289
+ * • Labeled mode: label has no extra role; the visual layout speaks
6290
+ */ const Divider = /*#__PURE__*/ forwardRef(function Divider(props, ref) {
6291
+ const { orientation = 'horizontal', align = 'center', variant = 'solid', color = 'neutral', children, flexItem, sx } = props, rest = _object_without_properties_loose(props, [
6292
+ "orientation",
6293
+ "align",
6294
+ "variant",
6295
+ "color",
6296
+ "children",
6297
+ "flexItem",
6298
+ "sx"
6299
+ ]);
6300
+ // ─── Mode 1: line-only (no children) ─────────────────────────────
6301
+ if (children == null) {
6302
+ const lineClasses = cn(dividerLineVariants({
6303
+ orientation,
6304
+ variant,
6305
+ color,
6306
+ segment: 'full'
6307
+ }), sx);
6308
+ // Horizontal → <hr>: zero default styling once we strip the UA
6309
+ // default `<hr>` border via `border-0` (in `dividerLineVariants`
6310
+ // base) and apply our own `border-t-*`. Vertical → <div> because
6311
+ // <hr> can't be vertical cross-browser-consistently.
6312
+ if (orientation === 'horizontal') {
6313
+ return jsx("hr", _extends({
6314
+ ref: ref,
6315
+ role: "separator",
6316
+ "aria-orientation": "horizontal",
6317
+ className: cn('border-0', lineClasses)
6318
+ }, rest));
6319
+ }
6320
+ return jsx("div", _extends({
6321
+ ref: ref,
6322
+ role: "separator",
6323
+ "aria-orientation": "vertical",
6324
+ className: lineClasses
6325
+ }, rest));
6326
+ }
6327
+ // ─── Mode 2: labeled (with children) ─────────────────────────────
6328
+ const rootClasses = cn(dividerVariants({
6329
+ orientation,
6330
+ align
6331
+ }), sx);
6332
+ /*
6333
+ * Segment widths per `align`:
6334
+ * • center → both flex-1 (equal share)
6335
+ * • start → left short stub, right grows
6336
+ * • end → left grows, right short stub
6337
+ *
6338
+ * "Short stub" = 2rem so the divider visually exists on the squashed
6339
+ * side without dominating the label.
6340
+ */ const isHorizontal = orientation === 'horizontal';
6341
+ const leftStubClass = align === 'start' ? isHorizontal ? 'basis-8 grow-0' : 'basis-8 grow-0' : 'flex-1';
6342
+ const rightStubClass = align === 'end' ? isHorizontal ? 'basis-8 grow-0' : 'basis-8 grow-0' : 'flex-1';
6343
+ const lineCommon = dividerLineVariants({
6344
+ orientation,
6345
+ variant,
6346
+ color,
6347
+ segment: 'grow'
6348
+ });
6349
+ const labelPadding = isHorizontal ? 'px-3' : 'py-3';
6350
+ return jsxs("div", _extends({
6351
+ ref: ref,
6352
+ role: "separator",
6353
+ "aria-orientation": orientation,
6354
+ className: rootClasses
6355
+ }, rest, {
6356
+ children: [
6357
+ jsx("span", {
6358
+ "aria-hidden": "true",
6359
+ className: cn(lineCommon, leftStubClass)
6360
+ }),
6361
+ jsx("span", {
6362
+ className: cn('shrink-0 text-sm text-neutral-500 dark:text-neutral-400', labelPadding),
6363
+ children: children
6364
+ }),
6365
+ jsx("span", {
6366
+ "aria-hidden": "true",
6367
+ className: cn(lineCommon, rightStubClass)
6368
+ })
6369
+ ]
6370
+ }));
6371
+ });
6372
+ Divider.displayName = 'Divider';
6373
+
6374
+ /**
6375
+ * `<AspectRatio>` — locks the aspect ratio of its child container,
6376
+ * regardless of width. The classic use is responsive images and
6377
+ * embedded media: an `<img>` that takes 100% of the available width
6378
+ * but always renders at 16:9 (or 1:1, or whatever the source ratio is)
6379
+ * — no jumping layouts during image load, no whitespace below the
6380
+ * media, no JS measurement.
6381
+ *
6382
+ * Implementation: native CSS `aspect-ratio` property. Supported in
6383
+ * every browser shipped from 2021 onward (Chrome 88, Firefox 89,
6384
+ * Safari 15, Edge 88). No padding-bottom hack — that workaround
6385
+ * predates the native property and brings ugly absolute-positioning
6386
+ * requirements on the child.
6387
+ *
6388
+ * Why a component if it's "just one CSS property"?
6389
+ * Two reasons:
6390
+ * 1. Discoverability — `<AspectRatio ratio={16/9}>` documents the
6391
+ * intent at the call site. `style={{ aspectRatio: '16/9' }}` is
6392
+ * the same thing functionally, but harder to spot in a 200-line
6393
+ * component file.
6394
+ * 2. Composition — pairs naturally with `sx="rounded-xl overflow-hidden"`
6395
+ * for the canonical "rounded clipped media" pattern. Forgetting
6396
+ * the `overflow-hidden` is the #1 mistake we want to prevent
6397
+ * through documentation (it's in this component's MDX, at the
6398
+ * top of the Notes).
6399
+ *
6400
+ * Child contract:
6401
+ * The single child is expected to fill the container — typically
6402
+ * `<img>` / `<video>` with `className="w-full h-full object-cover"`.
6403
+ * We don't force this via CSS (the consumer might want a centered
6404
+ * icon instead of a filling image) — but it's the 99% case, and the
6405
+ * docs show it first.
6406
+ */ const AspectRatio = /*#__PURE__*/ forwardRef(function AspectRatio(props, ref) {
6407
+ const { ratio = 1, as, sx, style, children } = props, rest = _object_without_properties_loose(props, [
6408
+ "ratio",
6409
+ "as",
6410
+ "sx",
6411
+ "style",
6412
+ "children"
6413
+ ]);
6414
+ /*
6415
+ * Normalise to a CSS `aspect-ratio` string. The CSS property
6416
+ * accepts both `16/9` and `16 / 9` (with spaces), but for safety
6417
+ * we convert numbers to the canonical `N / 1` form — `aspectRatio: 1.7777`
6418
+ * works too, but produces an arbitrary-looking value in DevTools.
6419
+ */ const aspectRatioValue = typeof ratio === 'number' ? `${ratio} / 1` : ratio;
6420
+ const mergedStyle = _extends({
6421
+ aspectRatio: aspectRatioValue
6422
+ }, style);
6423
+ const classes = cn('w-full', sx);
6424
+ const Tag = as != null ? as : 'div';
6425
+ return jsx(Tag, _extends({
6426
+ ref: ref,
6427
+ className: classes,
6428
+ style: mergedStyle
6429
+ }, rest, {
6430
+ children: children
6431
+ }));
6432
+ });
6433
+ AspectRatio.displayName = 'AspectRatio';
6434
+
6435
+ /**
6436
+ * `<VisuallyHidden>` — the accessibility primitive.
6437
+ *
6438
+ * Hides children from sighted users (zero pixels rendered, no layout
6439
+ * impact) while keeping them in the accessibility tree. Screen readers
6440
+ * speak the content; voice control software uses it for click targets;
6441
+ * keyboard users see nothing — same as if it weren't there.
6442
+ *
6443
+ * Implementation: Tailwind's built-in `sr-only` utility, which expands to:
6444
+ *
6445
+ * .sr-only {
6446
+ * position: absolute;
6447
+ * width: 1px;
6448
+ * height: 1px;
6449
+ * padding: 0;
6450
+ * margin: -1px;
6451
+ * overflow: hidden;
6452
+ * clip: rect(0,0,0,0);
6453
+ * white-space: nowrap;
6454
+ * border-width: 0;
6455
+ * }
6456
+ *
6457
+ * This is the canonical "visually-hidden but screen-reader-accessible"
6458
+ * pattern, also known as the WebAIM clip technique. Critically:
6459
+ *
6460
+ * • NOT `display: none` (removes from a11y tree)
6461
+ * • NOT `visibility: hidden` (also removes from a11y tree)
6462
+ * • NOT `opacity: 0` (technically still rendered, doesn't help AT)
6463
+ * • NOT `width/height: 0` (collapses, some AT skips it)
6464
+ *
6465
+ * Default tag is `<span>` (inline) — the 99% case is "label inside a
6466
+ * button or link". For block content, override with `as="div"`, but
6467
+ * be aware nesting block inside inline is invalid HTML.
6468
+ *
6469
+ * The component is intentionally tiny (~30 LoC total): one className,
6470
+ * one tag. The value is the COMPONENT NAME — `<VisuallyHidden>` reads
6471
+ * as an intentional a11y decision in code review, while
6472
+ * `className="sr-only"` looks like a typo or a forgotten utility.
6473
+ */ const VisuallyHidden = /*#__PURE__*/ forwardRef(function VisuallyHidden(props, ref) {
6474
+ const { as, sx, children } = props, rest = _object_without_properties_loose(props, [
6475
+ "as",
6476
+ "sx",
6477
+ "children"
6478
+ ]);
6479
+ const Tag = as != null ? as : 'span';
6480
+ const classes = cn('sr-only', sx);
6481
+ return jsx(Tag, _extends({
6482
+ ref: ref,
6483
+ className: classes
6484
+ }, rest, {
6485
+ children: children
6486
+ }));
6487
+ });
6488
+ VisuallyHidden.displayName = 'VisuallyHidden';
6489
+
4929
6490
  /**
4930
6491
  * @dashforge/tw
4931
6492
  *
@@ -4955,4 +6516,4 @@ const SnackbarContext = /*#__PURE__*/ createContext(null);
4955
6516
  * Package version (synced with `package.json` at publish time).
4956
6517
  */ const VERSION = '0.1.0-beta';
4957
6518
 
4958
- export { AppShell, Autocomplete, Breadcrumbs, Button, Checkbox, ConfirmDialogProvider, DateTimePicker, LeftNav, NumberField, OTPField, RadioGroup, SnackbarProvider, Switch, TextField, Textarea, TopBar, VERSION, appShellVariants, autocompleteVariants, breadcrumbsVariants, buttonVariants, checkboxVariants, cn, confirmDialogVariants, dateTimePickerVariants, isoToInputValue, leftNavVariants, numberFieldVariants, otpFieldVariants, radioGroupVariants, snackbarVariants, switchVariants, textFieldVariants, textareaVariants, topBarVariants, useAccessState, useConfirm, useSnackbar };
6519
+ export { AppShell, AspectRatio, Autocomplete, Box, Breadcrumbs, Button, Checkbox, ConfirmDialogProvider, Container, DateTimePicker, Divider, Grid, LeftNav, NumberField, OTPField, RadioGroup, SnackbarProvider, Stack, Switch, TextField, Textarea, TopBar, Typography, VERSION, VisuallyHidden, appShellVariants, autocompleteVariants, boxVariants, breadcrumbsVariants, buttonVariants, checkboxVariants, cn, confirmDialogVariants, containerVariants, dateTimePickerVariants, dividerLineVariants, dividerVariants, gridVariants, isoToInputValue, leftNavVariants, numberFieldVariants, otpFieldVariants, radioGroupVariants, snackbarVariants, stackVariants, switchVariants, textFieldVariants, textareaVariants, topBarVariants, typographyVariants, useAccessState, useConfirm, useSnackbar };