@delacour/react-native-ui 0.1.0-alpha.3

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 (273) hide show
  1. package/README.md +113 -0
  2. package/package.json +125 -0
  3. package/src/components/accordion/AGENTS.md +192 -0
  4. package/src/components/accordion/accordion-content.tsx +125 -0
  5. package/src/components/accordion/accordion-description.tsx +17 -0
  6. package/src/components/accordion/accordion-indicator.tsx +104 -0
  7. package/src/components/accordion/accordion-item.tsx +78 -0
  8. package/src/components/accordion/accordion-title.tsx +18 -0
  9. package/src/components/accordion/accordion-trigger.tsx +148 -0
  10. package/src/components/accordion/accordion.context.tsx +153 -0
  11. package/src/components/accordion/accordion.tsx +253 -0
  12. package/src/components/accordion/accordion.types.ts +11 -0
  13. package/src/components/accordion/accordion.variants.test.ts +435 -0
  14. package/src/components/accordion/accordion.variants.ts +358 -0
  15. package/src/components/accordion/index.ts +45 -0
  16. package/src/components/badge/AGENTS.md +83 -0
  17. package/src/components/badge/badge-close-button.tsx +48 -0
  18. package/src/components/badge/badge-end-content.tsx +15 -0
  19. package/src/components/badge/badge-label.tsx +24 -0
  20. package/src/components/badge/badge-start-content.tsx +16 -0
  21. package/src/components/badge/badge.context.tsx +64 -0
  22. package/src/components/badge/badge.tsx +192 -0
  23. package/src/components/badge/badge.types.ts +10 -0
  24. package/src/components/badge/badge.variants.test.ts +269 -0
  25. package/src/components/badge/badge.variants.ts +238 -0
  26. package/src/components/badge/index.ts +17 -0
  27. package/src/components/bottom-sheet/AGENTS.md +232 -0
  28. package/src/components/bottom-sheet/bottom-sheet-background.tsx +35 -0
  29. package/src/components/bottom-sheet/bottom-sheet-close.tsx +60 -0
  30. package/src/components/bottom-sheet/bottom-sheet-container.tsx +245 -0
  31. package/src/components/bottom-sheet/bottom-sheet-content.tsx +66 -0
  32. package/src/components/bottom-sheet/bottom-sheet-description.tsx +29 -0
  33. package/src/components/bottom-sheet/bottom-sheet-footer.tsx +148 -0
  34. package/src/components/bottom-sheet/bottom-sheet-handle.tsx +47 -0
  35. package/src/components/bottom-sheet/bottom-sheet-overlay.tsx +180 -0
  36. package/src/components/bottom-sheet/bottom-sheet-portal.tsx +72 -0
  37. package/src/components/bottom-sheet/bottom-sheet-scroll-view.tsx +95 -0
  38. package/src/components/bottom-sheet/bottom-sheet-title.tsx +24 -0
  39. package/src/components/bottom-sheet/bottom-sheet-trigger.tsx +73 -0
  40. package/src/components/bottom-sheet/bottom-sheet.context.tsx +135 -0
  41. package/src/components/bottom-sheet/bottom-sheet.tsx +125 -0
  42. package/src/components/bottom-sheet/bottom-sheet.variants.test.ts +276 -0
  43. package/src/components/bottom-sheet/bottom-sheet.variants.ts +199 -0
  44. package/src/components/bottom-sheet/index.ts +40 -0
  45. package/src/components/bottom-sheet/use-bottom-sheet-input.ts +125 -0
  46. package/src/components/button/AGENTS.md +217 -0
  47. package/src/components/button/button-end-content.tsx +15 -0
  48. package/src/components/button/button-group-separator.tsx +37 -0
  49. package/src/components/button/button-group-text.tsx +101 -0
  50. package/src/components/button/button-group.tsx +158 -0
  51. package/src/components/button/button-label.tsx +24 -0
  52. package/src/components/button/button-start-content.tsx +15 -0
  53. package/src/components/button/button.context.tsx +187 -0
  54. package/src/components/button/button.tsx +273 -0
  55. package/src/components/button/button.types.ts +10 -0
  56. package/src/components/button/button.variants.test.ts +811 -0
  57. package/src/components/button/button.variants.ts +480 -0
  58. package/src/components/button/index.ts +50 -0
  59. package/src/components/chart/AGENTS.md +210 -0
  60. package/src/components/chart/chart-area.tsx +93 -0
  61. package/src/components/chart/chart-bar.tsx +103 -0
  62. package/src/components/chart/chart-bars.tsx +75 -0
  63. package/src/components/chart/chart-candlestick.tsx +60 -0
  64. package/src/components/chart/chart-grid.tsx +33 -0
  65. package/src/components/chart/chart-legend.tsx +41 -0
  66. package/src/components/chart/chart-line.tsx +50 -0
  67. package/src/components/chart/chart-scatter.tsx +35 -0
  68. package/src/components/chart/chart-tooltip-dot.tsx +70 -0
  69. package/src/components/chart/chart-tooltip-x.tsx +121 -0
  70. package/src/components/chart/chart-tooltip-y.tsx +55 -0
  71. package/src/components/chart/chart-tooltip.tsx +133 -0
  72. package/src/components/chart/chart-x-axis.tsx +27 -0
  73. package/src/components/chart/chart-y-axis.tsx +21 -0
  74. package/src/components/chart/chart.context.tsx +127 -0
  75. package/src/components/chart/chart.tsx +448 -0
  76. package/src/components/chart/chart.types.ts +108 -0
  77. package/src/components/chart/chart.variants.test.ts +893 -0
  78. package/src/components/chart/chart.variants.ts +726 -0
  79. package/src/components/chart/index.ts +103 -0
  80. package/src/components/chart/pie-chart-center.tsx +40 -0
  81. package/src/components/chart/pie-chart-label.tsx +56 -0
  82. package/src/components/chart/pie-chart-slice.tsx +45 -0
  83. package/src/components/chart/pie-chart-tooltip.tsx +85 -0
  84. package/src/components/chart/pie-chart.context.tsx +77 -0
  85. package/src/components/chart/pie-chart.tsx +247 -0
  86. package/src/components/chart/use-chart-font.ts +25 -0
  87. package/src/components/chart/use-chart-palette.ts +38 -0
  88. package/src/components/checkbox/AGENTS.md +185 -0
  89. package/src/components/checkbox/checkbox-box.tsx +152 -0
  90. package/src/components/checkbox/checkbox-group.tsx +107 -0
  91. package/src/components/checkbox/checkbox-label.tsx +34 -0
  92. package/src/components/checkbox/checkbox.context.tsx +136 -0
  93. package/src/components/checkbox/checkbox.tsx +258 -0
  94. package/src/components/checkbox/checkbox.types.ts +14 -0
  95. package/src/components/checkbox/checkbox.variants.test.ts +634 -0
  96. package/src/components/checkbox/checkbox.variants.ts +484 -0
  97. package/src/components/checkbox/index.ts +42 -0
  98. package/src/components/field/AGENTS.md +100 -0
  99. package/src/components/field/field-content.tsx +24 -0
  100. package/src/components/field/field-description.tsx +29 -0
  101. package/src/components/field/field-error.tsx +61 -0
  102. package/src/components/field/field-group.tsx +22 -0
  103. package/src/components/field/field-label.tsx +33 -0
  104. package/src/components/field/field-legend.tsx +34 -0
  105. package/src/components/field/field-separator.tsx +81 -0
  106. package/src/components/field/field-set.tsx +22 -0
  107. package/src/components/field/field.context.tsx +78 -0
  108. package/src/components/field/field.tsx +144 -0
  109. package/src/components/field/field.types.ts +15 -0
  110. package/src/components/field/field.variants.test.ts +216 -0
  111. package/src/components/field/field.variants.ts +136 -0
  112. package/src/components/field/index.ts +21 -0
  113. package/src/components/icon/AGENTS.md +104 -0
  114. package/src/components/icon/icon.context.tsx +29 -0
  115. package/src/components/icon/icon.tsx +92 -0
  116. package/src/components/icon/icon.variants.test.ts +111 -0
  117. package/src/components/icon/icon.variants.ts +79 -0
  118. package/src/components/icon/index.ts +11 -0
  119. package/src/components/input/AGENTS.md +126 -0
  120. package/src/components/input/index.ts +27 -0
  121. package/src/components/input/input-group-decorator.tsx +84 -0
  122. package/src/components/input/input-group-prefix.tsx +16 -0
  123. package/src/components/input/input-group-suffix.tsx +15 -0
  124. package/src/components/input/input-group.tsx +140 -0
  125. package/src/components/input/input.context.tsx +87 -0
  126. package/src/components/input/input.tsx +184 -0
  127. package/src/components/input/input.types.ts +10 -0
  128. package/src/components/input/input.variants.test.ts +369 -0
  129. package/src/components/input/input.variants.ts +329 -0
  130. package/src/components/list-group/AGENTS.md +67 -0
  131. package/src/components/list-group/index.ts +20 -0
  132. package/src/components/list-group/list-group-item-content.tsx +10 -0
  133. package/src/components/list-group/list-group-item-description.tsx +12 -0
  134. package/src/components/list-group/list-group-item-prefix.tsx +30 -0
  135. package/src/components/list-group/list-group-item-suffix.tsx +55 -0
  136. package/src/components/list-group/list-group-item-title.tsx +17 -0
  137. package/src/components/list-group/list-group-item.tsx +87 -0
  138. package/src/components/list-group/list-group.context.tsx +65 -0
  139. package/src/components/list-group/list-group.tsx +116 -0
  140. package/src/components/list-group/list-group.types.ts +14 -0
  141. package/src/components/list-group/list-group.variants.test.ts +223 -0
  142. package/src/components/list-group/list-group.variants.ts +107 -0
  143. package/src/components/pressable/AGENTS.md +64 -0
  144. package/src/components/pressable/index.ts +9 -0
  145. package/src/components/pressable/pressable.tsx +256 -0
  146. package/src/components/pressable/pressable.variants.test.ts +99 -0
  147. package/src/components/pressable/pressable.variants.ts +63 -0
  148. package/src/components/provider/AGENTS.md +90 -0
  149. package/src/components/provider/index.ts +1 -0
  150. package/src/components/provider/provider.tsx +105 -0
  151. package/src/components/radio/AGENTS.md +247 -0
  152. package/src/components/radio/index.ts +35 -0
  153. package/src/components/radio/radio-group.tsx +126 -0
  154. package/src/components/radio/radio-indicator.tsx +91 -0
  155. package/src/components/radio/radio-label.tsx +39 -0
  156. package/src/components/radio/radio.context.tsx +132 -0
  157. package/src/components/radio/radio.tsx +215 -0
  158. package/src/components/radio/radio.variants.test.ts +581 -0
  159. package/src/components/radio/radio.variants.ts +272 -0
  160. package/src/components/screen/AGENTS.md +287 -0
  161. package/src/components/screen/index.ts +85 -0
  162. package/src/components/screen/screen-chat-list.tsx +485 -0
  163. package/src/components/screen/screen-content.tsx +69 -0
  164. package/src/components/screen/screen-debug.ts +43 -0
  165. package/src/components/screen/screen-error.tsx +61 -0
  166. package/src/components/screen/screen-flat-list.tsx +88 -0
  167. package/src/components/screen/screen-footer-background.tsx +60 -0
  168. package/src/components/screen/screen-footer.tsx +170 -0
  169. package/src/components/screen/screen-header.tsx +36 -0
  170. package/src/components/screen/screen-legend-list.tsx +105 -0
  171. package/src/components/screen/screen-list-component.tsx +23 -0
  172. package/src/components/screen/screen-loading.tsx +50 -0
  173. package/src/components/screen/screen-navbar-back-button.tsx +79 -0
  174. package/src/components/screen/screen-navbar-background.tsx +45 -0
  175. package/src/components/screen/screen-navbar-subtitle.tsx +26 -0
  176. package/src/components/screen/screen-navbar-title.tsx +31 -0
  177. package/src/components/screen/screen-navbar.tsx +154 -0
  178. package/src/components/screen/screen-root.tsx +38 -0
  179. package/src/components/screen/screen-scroll-area.tsx +116 -0
  180. package/src/components/screen/screen-scroll-shadow.tsx +186 -0
  181. package/src/components/screen/screen-section-list.tsx +81 -0
  182. package/src/components/screen/screen-view.tsx +59 -0
  183. package/src/components/screen/screen.context.tsx +167 -0
  184. package/src/components/screen/screen.tsx +114 -0
  185. package/src/components/screen/screen.types.ts +66 -0
  186. package/src/components/screen/screen.variants.test.ts +602 -0
  187. package/src/components/screen/screen.variants.ts +475 -0
  188. package/src/components/screen/use-screen-scroll-insets.ts +218 -0
  189. package/src/components/separator/AGENTS.md +30 -0
  190. package/src/components/separator/index.ts +8 -0
  191. package/src/components/separator/separator.tsx +85 -0
  192. package/src/components/slider/AGENTS.md +274 -0
  193. package/src/components/slider/index.ts +48 -0
  194. package/src/components/slider/slider-fill.tsx +68 -0
  195. package/src/components/slider/slider-output.tsx +55 -0
  196. package/src/components/slider/slider-thumb.tsx +161 -0
  197. package/src/components/slider/slider-track.tsx +233 -0
  198. package/src/components/slider/slider.context.tsx +118 -0
  199. package/src/components/slider/slider.tsx +321 -0
  200. package/src/components/slider/slider.types.ts +26 -0
  201. package/src/components/slider/slider.variants.test.ts +804 -0
  202. package/src/components/slider/slider.variants.ts +607 -0
  203. package/src/components/spinner/AGENTS.md +73 -0
  204. package/src/components/spinner/index.ts +17 -0
  205. package/src/components/spinner/spinner-arc.tsx +90 -0
  206. package/src/components/spinner/spinner-content.tsx +63 -0
  207. package/src/components/spinner/spinner.context.tsx +52 -0
  208. package/src/components/spinner/spinner.tsx +128 -0
  209. package/src/components/spinner/spinner.variants.test.ts +273 -0
  210. package/src/components/spinner/spinner.variants.ts +187 -0
  211. package/src/components/switch/AGENTS.md +213 -0
  212. package/src/components/switch/index.ts +42 -0
  213. package/src/components/switch/switch-content.tsx +111 -0
  214. package/src/components/switch/switch-end-content.tsx +18 -0
  215. package/src/components/switch/switch-start-content.tsx +20 -0
  216. package/src/components/switch/switch-thumb.tsx +102 -0
  217. package/src/components/switch/switch.context.tsx +79 -0
  218. package/src/components/switch/switch.tsx +423 -0
  219. package/src/components/switch/switch.types.ts +14 -0
  220. package/src/components/switch/switch.variants.test.ts +570 -0
  221. package/src/components/switch/switch.variants.ts +511 -0
  222. package/src/components/tabs/AGENTS.md +286 -0
  223. package/src/components/tabs/index.ts +69 -0
  224. package/src/components/tabs/tabs-content.tsx +65 -0
  225. package/src/components/tabs/tabs-indicator.tsx +97 -0
  226. package/src/components/tabs/tabs-label.tsx +62 -0
  227. package/src/components/tabs/tabs-list.tsx +139 -0
  228. package/src/components/tabs/tabs-pager.tsx +59 -0
  229. package/src/components/tabs/tabs-scroll-view.tsx +171 -0
  230. package/src/components/tabs/tabs-separator.tsx +73 -0
  231. package/src/components/tabs/tabs-trigger.tsx +210 -0
  232. package/src/components/tabs/tabs.context.tsx +294 -0
  233. package/src/components/tabs/tabs.tsx +435 -0
  234. package/src/components/tabs/tabs.types.ts +13 -0
  235. package/src/components/tabs/tabs.variants.test.ts +1020 -0
  236. package/src/components/tabs/tabs.variants.ts +672 -0
  237. package/src/components/text/AGENTS.md +95 -0
  238. package/src/components/text/index.ts +25 -0
  239. package/src/components/text/text.context.tsx +60 -0
  240. package/src/components/text/text.tsx +251 -0
  241. package/src/components/text/text.variants.test.ts +422 -0
  242. package/src/components/text/text.variants.ts +282 -0
  243. package/src/display-name.test.ts +145 -0
  244. package/src/docs.test.ts +98 -0
  245. package/src/expo/navigation-theme.tsx +58 -0
  246. package/src/hooks/use-controllable-state.ts +45 -0
  247. package/src/hooks/use-keyboard-state-sync.tsx +147 -0
  248. package/src/hooks/use-navigation-theme.ts +78 -0
  249. package/src/hooks/use-theme-color.ts +44 -0
  250. package/src/icons/central.ts +1 -0
  251. package/src/lib/cn.test.ts +136 -0
  252. package/src/lib/cn.ts +29 -0
  253. package/src/lib/color.test.ts +80 -0
  254. package/src/lib/color.ts +79 -0
  255. package/src/lib/compose-refs.test.ts +64 -0
  256. package/src/lib/compose-refs.ts +37 -0
  257. package/src/lib/keyboard-animation.test.ts +33 -0
  258. package/src/lib/keyboard-animation.ts +28 -0
  259. package/src/lib/merge-props.test.ts +82 -0
  260. package/src/lib/merge-props.ts +47 -0
  261. package/src/lib/navigation-theme.test.ts +49 -0
  262. package/src/lib/navigation-theme.ts +53 -0
  263. package/src/lib/slot.tsx +45 -0
  264. package/src/lib/tv.ts +20 -0
  265. package/src/styles/base.css +2 -0
  266. package/src/styles/geometry.test.ts +117 -0
  267. package/src/styles/index.css +3 -0
  268. package/src/styles/theme-tokens.test.ts +252 -0
  269. package/src/styles/theme.css +447 -0
  270. package/src/styles/tokens.css +132 -0
  271. package/src/styles/tokens.test.ts +181 -0
  272. package/src/styles/tokens.ts +86 -0
  273. package/src/uniwind-env.d.ts +1 -0
@@ -0,0 +1,252 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import { readFileSync } from "node:fs";
3
+ import { join } from "node:path";
4
+
5
+ /**
6
+ * The reader every suite that checks a token against `theme.css` shares, and
7
+ * the tests that pin the file's own shape.
8
+ *
9
+ * It lives in a `.test.ts` on purpose. Nine suites need to know what the
10
+ * palette declares, and nine hand-rolled regexes drifted from each other and
11
+ * from the file. It also reads from disk, so it could never ship to a React
12
+ * Native app — and the registry builder skips `*.test.ts`, which keeps it out
13
+ * of what `delacour add styles` copies. See `packages/cli/src/registry/classify.ts`.
14
+ *
15
+ * Import it from a sibling suite:
16
+ *
17
+ * ```ts
18
+ * import { declaredTokens } from "../../styles/theme-tokens.test";
19
+ * ```
20
+ */
21
+ export const THEME_CSS = readFileSync(join(import.meta.dirname, "theme.css"), "utf-8");
22
+ export const TOKENS_CSS = readFileSync(join(import.meta.dirname, "tokens.css"), "utf-8");
23
+
24
+ /**
25
+ * `--radius` in points.
26
+ *
27
+ * Declared in `rem` because that is what a theme generator emits; React Native
28
+ * has no root font size, and Uniwind resolves one `rem` as sixteen points.
29
+ */
30
+ export const REM_PX = 16;
31
+
32
+ export const RADIUS_BASE_PX = (() => {
33
+ const value = TOKENS_CSS.match(/--radius:\s*([\d.]+)(rem|px);/);
34
+ if (!value) throw new Error("tokens.css declares no --radius");
35
+
36
+ return Number(value[1]) * (value[2] === "rem" ? REM_PX : 1);
37
+ })();
38
+
39
+ /**
40
+ * What one step of the generic corner scale multiplies `--radius` by.
41
+ *
42
+ * The scale is `@theme inline`, so no `--radius-*` variable survives to runtime
43
+ * — only `--radius` does. Anything that has to compute a corner in JavaScript
44
+ * reads the base and applies its own multiplier, and pins that multiplier
45
+ * against this. `Checkbox`'s concentric fill is the one case.
46
+ */
47
+ export function radiusMultiplier(step: string): number {
48
+ const match = TOKENS_CSS.match(new RegExp(`--radius-${step}:\\s*([^;]+);`));
49
+ if (!match?.[1]) throw new Error(`tokens.css declares no --radius-${step}`);
50
+
51
+ const value = match[1].trim();
52
+ if (value === "var(--radius)") return 1;
53
+
54
+ const multiplier = value.match(/calc\(var\(--radius\)\s*\*\s*([\d.]+)\)/)?.[1];
55
+ if (multiplier === undefined) throw new Error(`--radius-${step} is not derived from --radius: ${value}`);
56
+
57
+ return Number(multiplier);
58
+ }
59
+
60
+ /** A step of the generic corner scale in points, at the default `--radius`. */
61
+ export function radiusPx(step: string): number {
62
+ return RADIUS_BASE_PX * radiusMultiplier(step);
63
+ }
64
+
65
+ export type ThemeVariant = "dark" | "light";
66
+
67
+ /**
68
+ * The body of one `@variant` block, brace-matched.
69
+ *
70
+ * Counting braces rather than splitting on the first `}`: the block holds
71
+ * comments and functional values, and a split would silently truncate the
72
+ * palette to whatever came before the first one — which reads as "the theme
73
+ * declares nothing", and every assertion built on it would pass vacuously.
74
+ */
75
+ export function themeBlock(variant: ThemeVariant): string {
76
+ const marker = `@variant ${variant} {`;
77
+ const start = THEME_CSS.indexOf(marker);
78
+ if (start === -1) throw new Error(`theme.css declares no @variant ${variant} block`);
79
+
80
+ let depth = 1;
81
+ let index = start + marker.length;
82
+
83
+ while (index < THEME_CSS.length && depth > 0) {
84
+ const character = THEME_CSS[index];
85
+ if (character === "{") depth += 1;
86
+ if (character === "}") depth -= 1;
87
+ index += 1;
88
+ }
89
+
90
+ if (depth !== 0) throw new Error(`theme.css never closes its @variant ${variant} block`);
91
+
92
+ return THEME_CSS.slice(start + marker.length, index - 1);
93
+ }
94
+
95
+ /**
96
+ * Every token name one variant declares, without its `--` prefix.
97
+ *
98
+ * These are the RAW shadcn names — `destructive`, not `color-destructive`.
99
+ * `theme.css` maps raw onto the `--color-*` namespace through `@theme inline`,
100
+ * and a token missing from a variant resolves to nothing at runtime: the colour
101
+ * silently falls back, in one theme only.
102
+ */
103
+ export function declaredTokens(variant: ThemeVariant): Set<string> {
104
+ const names = new Set<string>();
105
+
106
+ for (const [, name] of themeBlock(variant).matchAll(/^\s*--([\w-]+):/gm)) {
107
+ names.add(name);
108
+ }
109
+
110
+ return names;
111
+ }
112
+
113
+ /** The value one variant gives a token, or `undefined` if it declares none. */
114
+ export function tokenValue(variant: ThemeVariant, token: string): string | undefined {
115
+ return themeBlock(variant)
116
+ .match(new RegExp(`--${token}:\\s*([^;]+);`))?.[1]
117
+ ?.trim();
118
+ }
119
+
120
+ /**
121
+ * How many of the two variants declare a token — 0, 1 or 2.
122
+ *
123
+ * Anything but 2 is a bug a component cannot see: a token declared once
124
+ * resolves in one theme and silently falls back in the other.
125
+ */
126
+ export function declarationCount(token: string): number {
127
+ return (["light", "dark"] as const).filter((variant) => tokenValue(variant, token) !== undefined).length;
128
+ }
129
+
130
+ /** Every name the `@theme inline` block aliases, with its `--` prefix. */
131
+ export function aliasedTokens(): Set<string> {
132
+ const marker = "@theme inline {";
133
+ const block = THEME_CSS.slice(THEME_CSS.indexOf(marker) + marker.length);
134
+ const names = new Set<string>();
135
+
136
+ for (const [, name] of block.matchAll(/^\s*(--[\w-]+):\s*var\(/gm)) {
137
+ names.add(name);
138
+ }
139
+
140
+ return names;
141
+ }
142
+
143
+ /**
144
+ * Declared but deliberately not aliased.
145
+ *
146
+ * A theme generator emits a bare `--shadow` alongside the scale, and dropping
147
+ * it would make a paste lossy — but Tailwind's namespace is `--shadow-*`, so
148
+ * there is no utility for it to back.
149
+ */
150
+ const UNALIASED = new Set(["shadow"]);
151
+
152
+ const LIGHT = declaredTokens("light");
153
+ const DARK = declaredTokens("dark");
154
+
155
+ describe("the theme.css reader", () => {
156
+ // Every assertion below is worth nothing if the parse found no palette.
157
+ test("finds a populated block for both variants", () => {
158
+ expect(LIGHT.size).toBeGreaterThan(30);
159
+ expect(DARK.size).toBeGreaterThan(30);
160
+ });
161
+
162
+ test("brace-matches rather than stopping at the first closing brace", () => {
163
+ expect(LIGHT.has("overlay")).toBe(true);
164
+ expect(DARK.has("overlay")).toBe(true);
165
+ });
166
+ });
167
+
168
+ describe("the palette", () => {
169
+ // Uniwind refuses to build otherwise — "All themes must have the same
170
+ // variables" — so this fails here, in a second, rather than in Metro.
171
+ test("declares exactly the same names in both variants", () => {
172
+ expect([...LIGHT].sort()).toEqual([...DARK].sort());
173
+ });
174
+
175
+ test("gives every token a value in both variants", () => {
176
+ for (const token of LIGHT) {
177
+ expect(tokenValue("light", token)).toBeTruthy();
178
+ expect(tokenValue("dark", token)).toBeTruthy();
179
+ }
180
+ });
181
+
182
+ test("uses shadcn's names, not the ones this package used before", () => {
183
+ for (const shadcn of ["background", "foreground", "primary", "destructive", "border", "input", "ring"]) {
184
+ expect(LIGHT.has(shadcn)).toBe(true);
185
+ }
186
+
187
+ for (const renamed of ["danger", "danger-foreground", "danger-soft", "danger-soft-foreground"]) {
188
+ expect(LIGHT.has(renamed)).toBe(false);
189
+ }
190
+ });
191
+
192
+ test("pairs every -foreground with a surface of the same name", () => {
193
+ for (const token of LIGHT) {
194
+ if (!token.endsWith("-foreground")) continue;
195
+ expect(LIGHT.has(token.slice(0, -"-foreground".length))).toBe(true);
196
+ }
197
+ });
198
+ });
199
+
200
+ describe("the @theme inline block", () => {
201
+ // `inline` emits no variable of its own — it substitutes the raw value into
202
+ // each utility. A token missing here is declared but unreachable: no `bg-*`
203
+ // exists for it, and nothing anywhere reports that.
204
+ test("aliases every token the variants declare", () => {
205
+ for (const token of LIGHT) {
206
+ if (UNALIASED.has(token)) continue;
207
+
208
+ const aliases = aliasedTokens();
209
+ const reachable = aliases.has(`--color-${token}`) || aliases.has(`--${token}`);
210
+ expect(reachable).toBe(true);
211
+ }
212
+ });
213
+
214
+ test("aliases nothing the variants do not declare", () => {
215
+ // The fonts are the exception: they are split by platform rather than by
216
+ // theme, in their own `:root` block, because no one family name works on
217
+ // both iOS and Android.
218
+ const fonts = new Set(["--font-sans", "--font-serif", "--font-mono"]);
219
+
220
+ for (const alias of aliasedTokens()) {
221
+ if (fonts.has(alias)) continue;
222
+
223
+ const token = alias.replace(/^--(color-)?/, "");
224
+ expect(LIGHT.has(token)).toBe(true);
225
+ }
226
+ });
227
+ });
228
+
229
+ describe("the corner scale", () => {
230
+ const STEPS = ["xs", "sm", "md", "lg", "xl", "2xl", "3xl", "4xl"] as const;
231
+
232
+ test("derives every step from --radius", () => {
233
+ for (const step of STEPS) expect(radiusMultiplier(step)).toBeGreaterThan(0);
234
+ });
235
+
236
+ test("ascends, so a larger name is never a tighter corner", () => {
237
+ const multipliers = STEPS.map(radiusMultiplier);
238
+ expect([...multipliers].sort((a, b) => a - b)).toEqual(multipliers);
239
+ });
240
+
241
+ test("puts lg exactly on --radius, which is what a pasted theme sets", () => {
242
+ expect(radiusMultiplier("lg")).toBe(1);
243
+ });
244
+
245
+ // `--radius-button-*` is a shape, not a step: half the button's own height,
246
+ // so the button stays a capsule however a consumer retunes `--radius`.
247
+ test("leaves the button's corner out of it", () => {
248
+ for (const step of ["button-sm", "button-md", "button-lg"]) {
249
+ expect(() => radiusMultiplier(step)).toThrow();
250
+ }
251
+ });
252
+ });
@@ -0,0 +1,447 @@
1
+ /*
2
+ * THE ONE FILE THAT IS YOURS.
3
+ *
4
+ * Everything else in this folder is plumbing: `base.css` pulls in the engines,
5
+ * `tokens.css` names the sizes the utilities are minted from, `index.css`
6
+ * stitches them together. This file is the theme, and it is meant to be
7
+ * replaced whole:
8
+ *
9
+ * - Build one at https://ui.delacour.co.nz/theme and paste the `theme.css`
10
+ * tab over this file. Nothing else to run.
11
+ * - Have a shadcn or tweakcn `globals.css`? Paste it over this file and run
12
+ * `delacour theme`. It rewrites the file in place — see below for why a
13
+ * paste alone is not enough.
14
+ *
15
+ * The palette is shadcn's, in shadcn's two-layer shape. Layer one is a raw
16
+ * semantic name — `--primary`, `--destructive` — declared once per theme.
17
+ * Layer two is the `@theme inline` block at the bottom, which maps each raw
18
+ * name onto the `--color-*` Tailwind reads to mint `bg-primary`,
19
+ * `text-primary`, `border-primary`.
20
+ *
21
+ * Two layers rather than one so a web app's theme can be moved here almost
22
+ * unchanged: shadcn, tweakcn and every theme generator built on them emit
23
+ * exactly these raw names. The one thing that cannot survive a copy/paste is
24
+ * the wrapper: Uniwind reads a theme only from `@variant light` /
25
+ * `@variant dark`, and treats a literal `.dark { … }` as a utility class
26
+ * named `dark` that silently does nothing. `delacour theme` rewrites the
27
+ * wrapper and fills in the tokens shadcn has no name for.
28
+ *
29
+ * `@theme inline` emits NO `--color-*` variable of its own; it substitutes the
30
+ * raw `var(--primary)` into each utility. That is why `useThemeColor` looks up
31
+ * the raw name — see `hooks/use-theme-color.ts`.
32
+ */
33
+ @layer theme {
34
+ :root {
35
+ @variant light {
36
+ /*
37
+ * The page sits BELOW the card, in both themes.
38
+ *
39
+ * shadcn ships `background` and `card` as the same white, because on
40
+ * the web a card is separated by a shadow. Nothing here casts one, so
41
+ * a `ListGroup` on a page was white on white and read as a hairline
42
+ * border around nothing — while the same component in dark, where the
43
+ * ramp does step, read as a raised surface. One component, two
44
+ * different ideas of what a card is.
45
+ *
46
+ * This is the near-white step the ramp already carries for `sidebar`,
47
+ * so it invents no colour and collides with nothing: `secondary` and
48
+ * `muted` sit lower at 0.97, and `card` keeps the white above it.
49
+ * Light elevation is deliberately a fraction of dark's — on a light
50
+ * ground a surface lifts by a percent or two, where in dark it lifts
51
+ * by seven.
52
+ */
53
+ --background: oklch(0.985 0 0);
54
+ --foreground: oklch(0.145 0 0);
55
+ --card: oklch(1 0 0);
56
+ --card-foreground: oklch(0.145 0 0);
57
+ --popover: oklch(1 0 0);
58
+ --popover-foreground: oklch(0.145 0 0);
59
+
60
+ --primary: oklch(0.205 0 0);
61
+ --primary-foreground: oklch(0.985 0 0);
62
+ --secondary: oklch(0.97 0 0);
63
+ --secondary-foreground: oklch(0.205 0 0);
64
+ --muted: oklch(0.97 0 0);
65
+ --muted-foreground: oklch(0.556 0 0);
66
+ --accent: oklch(0.97 0 0);
67
+ --accent-foreground: oklch(0.205 0 0);
68
+
69
+ --destructive: oklch(0.577 0.245 27.325);
70
+ --destructive-foreground: oklch(0.985 0 0);
71
+
72
+ --border: oklch(0.922 0 0);
73
+ --input: oklch(0.922 0 0);
74
+ --ring: oklch(0.708 0 0);
75
+
76
+ /*
77
+ * The neutral ramp shadcn ships today, in both themes: five greys, not
78
+ * five hues. It is what a fresh `shadcn init` writes and what the
79
+ * default preset on /theme resolves to — `emit.test.ts` holds the
80
+ * two equal. An accent recolours all five.
81
+ */
82
+ --chart-1: oklch(0.87 0 0);
83
+ --chart-2: oklch(0.556 0 0);
84
+ --chart-3: oklch(0.439 0 0);
85
+ --chart-4: oklch(0.371 0 0);
86
+ --chart-5: oklch(0.269 0 0);
87
+
88
+ --sidebar: oklch(0.985 0 0);
89
+ --sidebar-foreground: oklch(0.145 0 0);
90
+ --sidebar-primary: oklch(0.205 0 0);
91
+ --sidebar-primary-foreground: oklch(0.985 0 0);
92
+ --sidebar-accent: oklch(0.97 0 0);
93
+ --sidebar-accent-foreground: oklch(0.205 0 0);
94
+ --sidebar-border: oklch(0.922 0 0);
95
+ --sidebar-ring: oklch(0.708 0 0);
96
+
97
+ /*
98
+ * A surface that sits ABOVE `muted`, in both themes.
99
+ *
100
+ * In light that is now exactly what `card` is, so this follows it —
101
+ * it used to follow `background`, which was the same white, and would
102
+ * now sink below the card and land a hair above `muted`. `Tabs`'
103
+ * selected capsule is what this exists for, and it has to read as
104
+ * raised out of a muted track.
105
+ *
106
+ * `card` still cannot do the job in dark, where it is *darker* than
107
+ * `muted` and nothing else in the ramp is lighter — hence the mix
108
+ * there. Derived rather than literal in both, so a pasted palette
109
+ * drags it along.
110
+ */
111
+ --elevated: var(--card);
112
+ --elevated-foreground: var(--foreground);
113
+
114
+ /*
115
+ * A fill quieter than `secondary`. shadcn has no counterpart —
116
+ * its `secondary`, `muted` and `accent` are one colour — so this is
117
+ * derived as half a step further from the page than `secondary`,
118
+ * which quiets it in light and in dark alike.
119
+ */
120
+ --tertiary: color-mix(in oklch, var(--secondary) 50%, var(--background));
121
+ --tertiary-foreground: color-mix(in oklch, var(--foreground) 50%, var(--muted-foreground));
122
+
123
+ /*
124
+ * The three state colours shadcn does not define, and the tinted
125
+ * `-soft` pair every state colour carries.
126
+ *
127
+ * A `-soft` surface is the state colour laid over the page, and its
128
+ * foreground is that colour darkened toward the text — so all eight
129
+ * follow a pasted `--background` and `--foreground` without being
130
+ * restated. The dark variant inverts the second rule: over a dark
131
+ * tint the state colour already reads, and mixing it toward a
132
+ * near-white foreground would only wash it out.
133
+ */
134
+ --success: oklch(0.696 0.17 162.48);
135
+ --success-foreground: oklch(0.985 0 0);
136
+ --warning: oklch(0.769 0.188 70.08);
137
+ --warning-foreground: oklch(0.216 0.006 56.043);
138
+ --info: oklch(0.623 0.188 259.81);
139
+ --info-foreground: oklch(0.985 0 0);
140
+
141
+ --destructive-soft: color-mix(in oklch, var(--destructive) 8%, var(--background));
142
+ --destructive-soft-foreground: color-mix(in oklch, var(--destructive) 70%, var(--foreground));
143
+ --success-soft: color-mix(in oklch, var(--success) 8%, var(--background));
144
+ --success-soft-foreground: color-mix(in oklch, var(--success) 70%, var(--foreground));
145
+ --warning-soft: color-mix(in oklch, var(--warning) 8%, var(--background));
146
+ --warning-soft-foreground: color-mix(in oklch, var(--warning) 70%, var(--foreground));
147
+ --info-soft: color-mix(in oklch, var(--info) 8%, var(--background));
148
+ --info-soft-foreground: color-mix(in oklch, var(--info) 70%, var(--foreground));
149
+
150
+ /*
151
+ * The scrim a BottomSheet draws over the app it covers.
152
+ *
153
+ * A surface token with no `-foreground`: nothing is ever drawn on it,
154
+ * and the sheet in front carries its own. It is not `bg-black/50` at a
155
+ * call site because a pure-black scrim over a near-black dark theme is
156
+ * nearly invisible — the two variants below differ on purpose, and the
157
+ * alpha lives with the colour so a caller never has to restate it.
158
+ *
159
+ * Literal black rather than derived: a scrim is black in both themes,
160
+ * and deriving it from `--foreground` would paint a white one in dark.
161
+ */
162
+ --overlay: oklch(0 0 0 / 45%);
163
+
164
+ /*
165
+ * Carried so a pasted theme survives intact. No component in this
166
+ * package draws a shadow — three tests assert it — so nothing reads
167
+ * these today; a consumer's own `shadow-md` will.
168
+ */
169
+ --shadow: 0 1px 3px 0 hsl(0 0% 0% / 0.1), 0 1px 2px -1px hsl(0 0% 0% / 0.1);
170
+ --shadow-2xs: 0 1px 3px 0 hsl(0 0% 0% / 0.05);
171
+ --shadow-xs: 0 1px 3px 0 hsl(0 0% 0% / 0.05);
172
+ --shadow-sm: 0 1px 3px 0 hsl(0 0% 0% / 0.1), 0 1px 2px -1px hsl(0 0% 0% / 0.1);
173
+ --shadow-md: 0 1px 3px 0 hsl(0 0% 0% / 0.1), 0 2px 4px -1px hsl(0 0% 0% / 0.1);
174
+ --shadow-lg: 0 1px 3px 0 hsl(0 0% 0% / 0.1), 0 4px 6px -1px hsl(0 0% 0% / 0.1);
175
+ --shadow-xl: 0 1px 3px 0 hsl(0 0% 0% / 0.1), 0 8px 10px -1px hsl(0 0% 0% / 0.1);
176
+ --shadow-2xl: 0 1px 3px 0 hsl(0 0% 0% / 0.25);
177
+ }
178
+
179
+ @variant dark {
180
+ --background: oklch(0.145 0 0);
181
+ --foreground: oklch(0.985 0 0);
182
+ --card: oklch(0.205 0 0);
183
+ --card-foreground: oklch(0.985 0 0);
184
+ --popover: oklch(0.205 0 0);
185
+ --popover-foreground: oklch(0.985 0 0);
186
+
187
+ --primary: oklch(0.922 0 0);
188
+ --primary-foreground: oklch(0.205 0 0);
189
+ --secondary: oklch(0.269 0 0);
190
+ --secondary-foreground: oklch(0.985 0 0);
191
+ --muted: oklch(0.269 0 0);
192
+ --muted-foreground: oklch(0.708 0 0);
193
+ --accent: oklch(0.269 0 0);
194
+ --accent-foreground: oklch(0.985 0 0);
195
+
196
+ --destructive: oklch(0.704 0.191 22.216);
197
+ --destructive-foreground: oklch(0.985 0 0);
198
+
199
+ --border: oklch(1 0 0 / 10%);
200
+ --input: oklch(1 0 0 / 15%);
201
+ --ring: oklch(0.556 0 0);
202
+
203
+ --chart-1: oklch(0.87 0 0);
204
+ --chart-2: oklch(0.556 0 0);
205
+ --chart-3: oklch(0.439 0 0);
206
+ --chart-4: oklch(0.371 0 0);
207
+ --chart-5: oklch(0.269 0 0);
208
+
209
+ --sidebar: oklch(0.205 0 0);
210
+ --sidebar-foreground: oklch(0.985 0 0);
211
+ --sidebar-primary: oklch(0.488 0.243 264.376);
212
+ --sidebar-primary-foreground: oklch(0.985 0 0);
213
+ --sidebar-accent: oklch(0.269 0 0);
214
+ --sidebar-accent-foreground: oklch(0.985 0 0);
215
+ --sidebar-border: oklch(1 0 0 / 10%);
216
+ --sidebar-ring: oklch(0.556 0 0);
217
+
218
+ --elevated: color-mix(in oklch, var(--muted) 88%, var(--foreground));
219
+ --elevated-foreground: var(--foreground);
220
+
221
+ --tertiary: color-mix(in oklch, var(--secondary) 50%, var(--background));
222
+ --tertiary-foreground: color-mix(in oklch, var(--foreground) 50%, var(--muted-foreground));
223
+
224
+ --success: oklch(0.696 0.17 162.48);
225
+ --success-foreground: oklch(0.205 0 0);
226
+ --warning: oklch(0.769 0.188 70.08);
227
+ --warning-foreground: oklch(0.216 0.006 56.043);
228
+ --info: oklch(0.623 0.188 259.81);
229
+ --info-foreground: oklch(0.985 0 0);
230
+
231
+ --destructive-soft: color-mix(in oklch, var(--destructive) 14%, var(--background));
232
+ --destructive-soft-foreground: var(--destructive);
233
+ --success-soft: color-mix(in oklch, var(--success) 14%, var(--background));
234
+ --success-soft-foreground: var(--success);
235
+ --warning-soft: color-mix(in oklch, var(--warning) 14%, var(--background));
236
+ --warning-soft-foreground: var(--warning);
237
+ --info-soft: color-mix(in oklch, var(--info) 14%, var(--background));
238
+ --info-soft-foreground: var(--info);
239
+
240
+ --overlay: oklch(0 0 0 / 65%);
241
+
242
+ --shadow: 0 1px 3px 0 hsl(0 0% 0% / 0.1), 0 1px 2px -1px hsl(0 0% 0% / 0.1);
243
+ --shadow-2xs: 0 1px 3px 0 hsl(0 0% 0% / 0.05);
244
+ --shadow-xs: 0 1px 3px 0 hsl(0 0% 0% / 0.05);
245
+ --shadow-sm: 0 1px 3px 0 hsl(0 0% 0% / 0.1), 0 1px 2px -1px hsl(0 0% 0% / 0.1);
246
+ --shadow-md: 0 1px 3px 0 hsl(0 0% 0% / 0.1), 0 2px 4px -1px hsl(0 0% 0% / 0.1);
247
+ --shadow-lg: 0 1px 3px 0 hsl(0 0% 0% / 0.1), 0 4px 6px -1px hsl(0 0% 0% / 0.1);
248
+ --shadow-xl: 0 1px 3px 0 hsl(0 0% 0% / 0.1), 0 8px 10px -1px hsl(0 0% 0% / 0.1);
249
+ --shadow-2xl: 0 1px 3px 0 hsl(0 0% 0% / 0.25);
250
+ }
251
+ }
252
+ }
253
+
254
+ @layer theme {
255
+ :root {
256
+ /*
257
+ * The three font families, split by platform rather than by theme.
258
+ *
259
+ * Tailwind's defaults are comma-separated web stacks, and React Native's
260
+ * `fontFamily` takes a single family name with no fallback list — the
261
+ * whole string would resolve to nothing and quietly fall back to the
262
+ * system sans, which is the opposite of what `Text.Code` is for. This is
263
+ * also why `delacour theme` takes only the FIRST family out of a pasted
264
+ * stack rather than carrying it whole.
265
+ *
266
+ * No single name works on both platforms: iOS has Menlo and Georgia and
267
+ * not `monospace`, Android the reverse. Every name here ships with the
268
+ * OS, so nothing has to be loaded through `expo-font` and a consuming app
269
+ * needs no plugin.
270
+ *
271
+ * This cannot live in `tokens.css`: platform selection needs `@variant`,
272
+ * and `@theme {}` accepts custom properties only. It also needs the
273
+ * `noDuplicateCustomProperties` exemption `biome.jsonc` scopes to this
274
+ * file — the same one `light` and `dark` already rely on.
275
+ */
276
+ @variant ios {
277
+ --font-sans: "System";
278
+ --font-serif: "Georgia";
279
+ --font-mono: "Menlo";
280
+ --font-heading: "System";
281
+ }
282
+
283
+ @variant android {
284
+ --font-sans: "sans-serif";
285
+ --font-serif: "serif";
286
+ --font-mono: "monospace";
287
+ --font-heading: "sans-serif";
288
+ }
289
+ }
290
+ }
291
+
292
+ @layer theme {
293
+ :root {
294
+ /*
295
+ * The geometry, restated so it can be retuned at runtime.
296
+ *
297
+ * Every number here is already declared in `tokens.css`, and that copy
298
+ * is the one `apps/web` reads — the docs site imports only that file.
299
+ * This one exists because of how Uniwind compiles a variable: one
300
+ * declared in a plain `@theme` block is resolved and INLINED at build
301
+ * time, so `h-button-md` would carry a literal 44 and no runtime
302
+ * override could ever reach it. A variable declared inside a `@variant`
303
+ * compiles to a getter against the live store instead. That is the
304
+ * whole difference between a kit whose density can be changed by an app
305
+ * and one whose density is a build artefact.
306
+ *
307
+ * `native` rather than `light`/`dark` on purpose: it is one of Uniwind's
308
+ * registered platform variants, and the store merges it into the base
309
+ * variables every theme inherits — so one block covers both themes,
310
+ * where a theme variant would have to be written out twice and kept in
311
+ * step. None of these are light/dark dependent; a corner does not change
312
+ * shape at dusk.
313
+ *
314
+ * `styles/geometry.test.ts` asserts this block and `tokens.css` agree,
315
+ * because two hand-maintained copies of one number always drift.
316
+ */
317
+ @variant native {
318
+ --radius: 0.625rem;
319
+
320
+ --spacing-button-sm: 36px;
321
+ --spacing-button-md: 44px;
322
+ --spacing-button-lg: 52px;
323
+
324
+ --text-button-sm: 14px;
325
+ --text-button-md: 16px;
326
+ --text-button-lg: 18px;
327
+
328
+ --radius-button-sm: 18px;
329
+ --radius-button-md: 22px;
330
+ --radius-button-lg: 26px;
331
+
332
+ --spacing-input-sm: 36px;
333
+ --spacing-input-md: 44px;
334
+ --spacing-input-lg: 52px;
335
+
336
+ --text-input-sm: 14px;
337
+ --text-input-md: 16px;
338
+ --text-input-lg: 18px;
339
+
340
+ --spacing-icon-xs: 14px;
341
+ --spacing-icon-sm: 16px;
342
+ --spacing-icon-md: 18px;
343
+ --spacing-icon-lg: 20px;
344
+ --spacing-icon-xl: 24px;
345
+ --spacing-icon-2xl: 32px;
346
+
347
+ --spacing-navbar-row: 56px;
348
+ --spacing-screen-gutter: 20px;
349
+
350
+ --spacing-chart-sm: 160px;
351
+ --spacing-chart-md: 220px;
352
+ --spacing-chart-lg: 280px;
353
+ }
354
+ }
355
+ }
356
+
357
+ /*
358
+ * Every raw name above, mapped onto the namespace Tailwind mints utilities
359
+ * from. `inline` is what makes a utility read `var(--primary)` directly rather
360
+ * than a copy taken at `:root`, which is what lets the `@variant` swap above
361
+ * reach it.
362
+ *
363
+ * A name added to a `@variant` block and forgotten here is declared but
364
+ * unreachable — no `bg-*` will exist for it. `styles/theme-tokens.test.ts`
365
+ * fails on the gap.
366
+ */
367
+ @theme inline {
368
+ --color-background: var(--background);
369
+ --color-foreground: var(--foreground);
370
+ --color-card: var(--card);
371
+ --color-card-foreground: var(--card-foreground);
372
+ --color-popover: var(--popover);
373
+ --color-popover-foreground: var(--popover-foreground);
374
+
375
+ --color-primary: var(--primary);
376
+ --color-primary-foreground: var(--primary-foreground);
377
+ --color-secondary: var(--secondary);
378
+ --color-secondary-foreground: var(--secondary-foreground);
379
+ --color-muted: var(--muted);
380
+ --color-muted-foreground: var(--muted-foreground);
381
+ --color-accent: var(--accent);
382
+ --color-accent-foreground: var(--accent-foreground);
383
+
384
+ --color-destructive: var(--destructive);
385
+ --color-destructive-foreground: var(--destructive-foreground);
386
+
387
+ --color-border: var(--border);
388
+ --color-input: var(--input);
389
+ --color-ring: var(--ring);
390
+
391
+ --color-chart-1: var(--chart-1);
392
+ --color-chart-2: var(--chart-2);
393
+ --color-chart-3: var(--chart-3);
394
+ --color-chart-4: var(--chart-4);
395
+ --color-chart-5: var(--chart-5);
396
+
397
+ --color-sidebar: var(--sidebar);
398
+ --color-sidebar-foreground: var(--sidebar-foreground);
399
+ --color-sidebar-primary: var(--sidebar-primary);
400
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
401
+ --color-sidebar-accent: var(--sidebar-accent);
402
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
403
+ --color-sidebar-border: var(--sidebar-border);
404
+ --color-sidebar-ring: var(--sidebar-ring);
405
+
406
+ --color-elevated: var(--elevated);
407
+ --color-elevated-foreground: var(--elevated-foreground);
408
+ --color-tertiary: var(--tertiary);
409
+ --color-tertiary-foreground: var(--tertiary-foreground);
410
+
411
+ --color-destructive-soft: var(--destructive-soft);
412
+ --color-destructive-soft-foreground: var(--destructive-soft-foreground);
413
+ --color-success: var(--success);
414
+ --color-success-foreground: var(--success-foreground);
415
+ --color-success-soft: var(--success-soft);
416
+ --color-success-soft-foreground: var(--success-soft-foreground);
417
+ --color-warning: var(--warning);
418
+ --color-warning-foreground: var(--warning-foreground);
419
+ --color-warning-soft: var(--warning-soft);
420
+ --color-warning-soft-foreground: var(--warning-soft-foreground);
421
+ --color-info: var(--info);
422
+ --color-info-foreground: var(--info-foreground);
423
+ --color-info-soft: var(--info-soft);
424
+ --color-info-soft-foreground: var(--info-soft-foreground);
425
+
426
+ --color-overlay: var(--overlay);
427
+
428
+ /*
429
+ * The fonts are deliberately absent.
430
+ *
431
+ * Tailwind already declares `--font-sans`, `--font-serif` and `--font-mono`
432
+ * in its own theme, so `font-mono` already reads `var(--font-mono)` and the
433
+ * platform block above already overrides it. Aliasing them here the way
434
+ * shadcn does would redefine each one AS ITSELF — Uniwind compiles that to
435
+ * `vars["--font-mono"] => vars["--font-mono"]`, a variable that recurses
436
+ * instead of resolving, and every `font-*` utility silently draws nothing.
437
+ * shadcn needs the alias only because its fonts live outside `@theme`.
438
+ */
439
+
440
+ --shadow-2xs: var(--shadow-2xs);
441
+ --shadow-xs: var(--shadow-xs);
442
+ --shadow-sm: var(--shadow-sm);
443
+ --shadow-md: var(--shadow-md);
444
+ --shadow-lg: var(--shadow-lg);
445
+ --shadow-xl: var(--shadow-xl);
446
+ --shadow-2xl: var(--shadow-2xl);
447
+ }