@eifi1/ui-kit 0.48.2 → 0.49.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (292) hide show
  1. package/README.md +26 -12
  2. package/dist/admin/admin-action-confirm.js +1 -1
  3. package/dist/admin/admin-action-confirm.js.map +1 -1
  4. package/dist/admin/invitations-panel.js +5 -1
  5. package/dist/admin/invitations-panel.js.map +1 -1
  6. package/dist/admin/user-roster.d.ts +11 -5
  7. package/dist/admin/user-roster.js +15 -4
  8. package/dist/admin/user-roster.js.map +1 -1
  9. package/dist/auth/accept-invitation.js +2 -2
  10. package/dist/auth/accept-invitation.js.map +1 -1
  11. package/dist/auth/not-found-page.js +1 -1
  12. package/dist/auth/not-found-page.js.map +1 -1
  13. package/dist/auth/reset-password-form.js +2 -2
  14. package/dist/auth/reset-password-form.js.map +1 -1
  15. package/dist/auth/verify-email.js +2 -2
  16. package/dist/auth/verify-email.js.map +1 -1
  17. package/dist/billing/plan-card.js +2 -2
  18. package/dist/billing/plan-card.js.map +1 -1
  19. package/dist/components/breadcrumbs.d.ts +19 -1
  20. package/dist/components/breadcrumbs.js +11 -4
  21. package/dist/components/breadcrumbs.js.map +1 -1
  22. package/dist/components/button-look.d.ts +37 -7
  23. package/dist/components/button-look.js +33 -6
  24. package/dist/components/button-look.js.map +1 -1
  25. package/dist/components/button.d.ts +4 -1
  26. package/dist/components/button.js +2 -2
  27. package/dist/components/button.js.map +1 -1
  28. package/dist/components/calendar-heatmap.js +2 -1
  29. package/dist/components/calendar-heatmap.js.map +1 -1
  30. package/dist/components/chart-edge.d.ts +10 -0
  31. package/dist/components/chart-edge.js +57 -0
  32. package/dist/components/chart-edge.js.map +1 -0
  33. package/dist/components/chart.js +2 -1
  34. package/dist/components/chart.js.map +1 -1
  35. package/dist/components/chip.d.ts +8 -1
  36. package/dist/components/chip.js +4 -1
  37. package/dist/components/chip.js.map +1 -1
  38. package/dist/components/confirm-context.d.ts +5 -0
  39. package/dist/components/confirm-context.js +7 -0
  40. package/dist/components/confirm-context.js.map +1 -0
  41. package/dist/components/confirm-dialog.js +3 -3
  42. package/dist/components/confirm-dialog.js.map +1 -1
  43. package/dist/components/danger-confirm.d.ts +2 -2
  44. package/dist/components/danger-confirm.js +1 -1
  45. package/dist/components/danger-confirm.js.map +1 -1
  46. package/dist/components/data-table-fit.js +21 -3
  47. package/dist/components/data-table-fit.js.map +1 -1
  48. package/dist/components/data-table-mobile.d.ts +48 -2
  49. package/dist/components/data-table-mobile.js +87 -10
  50. package/dist/components/data-table-mobile.js.map +1 -1
  51. package/dist/components/data-table-row-actions.d.ts +2 -1
  52. package/dist/components/data-table-row-actions.js +7 -0
  53. package/dist/components/data-table-row-actions.js.map +1 -1
  54. package/dist/components/data-table-row.d.ts +1 -1
  55. package/dist/components/data-table-row.js.map +1 -1
  56. package/dist/components/data-table.d.ts +34 -1
  57. package/dist/components/data-table.js +34 -9
  58. package/dist/components/data-table.js.map +1 -1
  59. package/dist/components/error-boundary.js +2 -2
  60. package/dist/components/error-boundary.js.map +1 -1
  61. package/dist/components/floating-panel.d.ts +36 -13
  62. package/dist/components/floating-panel.js +27 -7
  63. package/dist/components/floating-panel.js.map +1 -1
  64. package/dist/components/form-actions.d.ts +1 -1
  65. package/dist/components/form-actions.js +1 -1
  66. package/dist/components/form-actions.js.map +1 -1
  67. package/dist/components/icon-button.d.ts +11 -0
  68. package/dist/components/icon-button.js +7 -5
  69. package/dist/components/icon-button.js.map +1 -1
  70. package/dist/components/nav-pills.d.ts +8 -1
  71. package/dist/components/nav-pills.js +61 -38
  72. package/dist/components/nav-pills.js.map +1 -1
  73. package/dist/components/page-header-context.d.ts +8 -0
  74. package/dist/components/page-header-context.js +7 -0
  75. package/dist/components/page-header-context.js.map +1 -0
  76. package/dist/components/page-header.d.ts +14 -2
  77. package/dist/components/page-header.js +10 -1
  78. package/dist/components/page-header.js.map +1 -1
  79. package/dist/components/pie-chart.js +2 -1
  80. package/dist/components/pie-chart.js.map +1 -1
  81. package/dist/components/progress-bar.js +5 -4
  82. package/dist/components/progress-bar.js.map +1 -1
  83. package/dist/components/reauth-dialog.js +1 -1
  84. package/dist/components/reauth-dialog.js.map +1 -1
  85. package/dist/components/row-actions-labels.js +3 -1
  86. package/dist/components/row-actions-labels.js.map +1 -1
  87. package/dist/components/row-actions.d.ts +43 -9
  88. package/dist/components/row-actions.js +78 -15
  89. package/dist/components/row-actions.js.map +1 -1
  90. package/dist/components/section-nav.d.ts +37 -0
  91. package/dist/components/section-nav.js +33 -0
  92. package/dist/components/section-nav.js.map +1 -0
  93. package/dist/components/series-chart.js +8 -3
  94. package/dist/components/series-chart.js.map +1 -1
  95. package/dist/components/share-card.js +1 -1
  96. package/dist/components/share-card.js.map +1 -1
  97. package/dist/components/signature-pad.js +1 -1
  98. package/dist/components/signature-pad.js.map +1 -1
  99. package/dist/components/sparkline.js +2 -1
  100. package/dist/components/sparkline.js.map +1 -1
  101. package/dist/components/stat-tile.js +14 -7
  102. package/dist/components/stat-tile.js.map +1 -1
  103. package/dist/components/status-dot.d.ts +5 -1
  104. package/dist/components/status-dot.js +4 -1
  105. package/dist/components/status-dot.js.map +1 -1
  106. package/dist/components/tooltip.js +1 -1
  107. package/dist/components/tooltip.js.map +1 -1
  108. package/dist/components/translation-review-editor.js +2 -2
  109. package/dist/components/translation-review-editor.js.map +1 -1
  110. package/dist/components/translation-review.js +2 -2
  111. package/dist/components/translation-review.js.map +1 -1
  112. package/dist/components/treemap.d.ts +5 -2
  113. package/dist/components/treemap.js +26 -6
  114. package/dist/components/treemap.js.map +1 -1
  115. package/dist/components/use-table-state.js +37 -6
  116. package/dist/components/use-table-state.js.map +1 -1
  117. package/dist/data-table.js.map +1 -1
  118. package/dist/demo/demo-banner.js +1 -1
  119. package/dist/demo/demo-banner.js.map +1 -1
  120. package/dist/demo/demo-ended.js +1 -1
  121. package/dist/demo/demo-ended.js.map +1 -1
  122. package/dist/demo/demo-start.js +1 -1
  123. package/dist/demo/demo-start.js.map +1 -1
  124. package/dist/eslint/index.js +1 -1
  125. package/dist/feedback/feedback-inbox.js +1 -1
  126. package/dist/feedback/feedback-inbox.js.map +1 -1
  127. package/dist/hooks/use-dismiss.d.ts +9 -1
  128. package/dist/hooks/use-dismiss.js +25 -5
  129. package/dist/hooks/use-dismiss.js.map +1 -1
  130. package/dist/hooks/use-document-title.d.ts +14 -0
  131. package/dist/hooks/use-document-title.js +22 -0
  132. package/dist/hooks/use-document-title.js.map +1 -0
  133. package/dist/i18n/german.js +6 -2
  134. package/dist/i18n/german.js.map +1 -1
  135. package/dist/i18n/kit-labels.d.ts +31 -1
  136. package/dist/i18n/kit-labels.js +43 -2
  137. package/dist/i18n/kit-labels.js.map +1 -1
  138. package/dist/i18n/locales/en.js +6 -2
  139. package/dist/i18n/locales/en.js.map +2 -2
  140. package/dist/i18n/locales/es.js +6 -2
  141. package/dist/i18n/locales/es.js.map +1 -1
  142. package/dist/i18n/locales/fr.js +6 -2
  143. package/dist/i18n/locales/fr.js.map +1 -1
  144. package/dist/i18n/locales/hu.js +6 -2
  145. package/dist/i18n/locales/hu.js.map +1 -1
  146. package/dist/i18n/locales/it.js +6 -2
  147. package/dist/i18n/locales/it.js.map +1 -1
  148. package/dist/i18n/locales/zh.js +6 -2
  149. package/dist/i18n/locales/zh.js.map +1 -1
  150. package/dist/i18n/review.js +2 -0
  151. package/dist/i18n/review.js.map +1 -1
  152. package/dist/i18n/shared-labels.js +1 -0
  153. package/dist/i18n/shared-labels.js.map +1 -1
  154. package/dist/index.d.ts +8 -1
  155. package/dist/index.js +11 -0
  156. package/dist/index.js.map +1 -1
  157. package/dist/landing/landing-actions.js +2 -2
  158. package/dist/landing/landing-actions.js.map +1 -1
  159. package/dist/landing/public-header.js +2 -2
  160. package/dist/landing/public-header.js.map +1 -1
  161. package/dist/lib/clipboard-minus.d.ts +27 -0
  162. package/dist/lib/clipboard-minus.js +63 -0
  163. package/dist/lib/clipboard-minus.js.map +1 -0
  164. package/dist/lib/document-title.d.ts +17 -0
  165. package/dist/lib/document-title.js +29 -0
  166. package/dist/lib/document-title.js.map +1 -0
  167. package/dist/lib/float-clearance.js +8 -2
  168. package/dist/lib/float-clearance.js.map +1 -1
  169. package/dist/lib/format.d.ts +35 -10
  170. package/dist/lib/format.js +55 -11
  171. package/dist/lib/format.js.map +1 -1
  172. package/dist/lib/minus.d.ts +22 -0
  173. package/dist/lib/minus.js +14 -0
  174. package/dist/lib/minus.js.map +1 -0
  175. package/dist/lib/status-tone.d.ts +30 -0
  176. package/dist/lib/status-tone.js +17 -0
  177. package/dist/lib/status-tone.js.map +1 -0
  178. package/dist/settings/settings-layout.js +3 -2
  179. package/dist/settings/settings-layout.js.map +1 -1
  180. package/dist/shell/account-menu-language.d.ts +1 -1
  181. package/dist/shell/account-menu-language.js +9 -1
  182. package/dist/shell/account-menu-language.js.map +1 -1
  183. package/dist/shell/app-shell-labels.js +2 -1
  184. package/dist/shell/app-shell-labels.js.map +1 -1
  185. package/dist/shell/app-shell.d.ts +58 -16
  186. package/dist/shell/app-shell.js +151 -43
  187. package/dist/shell/app-shell.js.map +1 -1
  188. package/dist/shell/language-code.d.ts +3 -0
  189. package/dist/shell/language-code.js +8 -0
  190. package/dist/shell/language-code.js.map +1 -0
  191. package/dist/shell/topbar-controls.d.ts +14 -6
  192. package/dist/shell/topbar-controls.js +14 -17
  193. package/dist/shell/topbar-controls.js.map +1 -1
  194. package/dist/testing/css-variables.generated.d.ts +1 -1
  195. package/dist/testing/css-variables.generated.js +1 -1
  196. package/dist/testing/css-variables.generated.js.map +1 -1
  197. package/dist/theme/chart-palette.d.ts +82 -15
  198. package/dist/theme/chart-palette.js +51 -24
  199. package/dist/theme/chart-palette.js.map +1 -1
  200. package/dist/theme/palette-presets.d.ts +9 -0
  201. package/dist/theme/palette-presets.js +77 -62
  202. package/dist/theme/palette-presets.js.map +1 -1
  203. package/dist/tour/tour.js +1 -1
  204. package/dist/tour/tour.js.map +1 -1
  205. package/dist/wizard/stepper-nav.d.ts +1 -1
  206. package/dist/wizard/stepper-nav.js +3 -3
  207. package/dist/wizard/stepper-nav.js.map +1 -1
  208. package/package.json +1 -1
  209. package/src/admin/admin-action-confirm.tsx +1 -1
  210. package/src/admin/invitations-panel.tsx +7 -3
  211. package/src/admin/user-roster.tsx +23 -6
  212. package/src/auth/accept-invitation.tsx +3 -3
  213. package/src/auth/not-found-page.tsx +1 -1
  214. package/src/auth/reset-password-form.tsx +3 -3
  215. package/src/auth/verify-email.tsx +3 -3
  216. package/src/billing/plan-card.tsx +2 -2
  217. package/src/components/breadcrumbs.tsx +32 -4
  218. package/src/components/button-look.ts +86 -15
  219. package/src/components/button.tsx +6 -3
  220. package/src/components/calendar-heatmap.tsx +2 -1
  221. package/src/components/chart-edge.ts +73 -0
  222. package/src/components/chart.tsx +3 -1
  223. package/src/components/chip.tsx +10 -1
  224. package/src/components/confirm-context.ts +7 -0
  225. package/src/components/confirm-dialog.tsx +3 -3
  226. package/src/components/danger-confirm.tsx +3 -3
  227. package/src/components/data-table-fit.ts +41 -6
  228. package/src/components/data-table-mobile.tsx +162 -11
  229. package/src/components/data-table-row-actions.tsx +9 -1
  230. package/src/components/data-table-row.tsx +2 -0
  231. package/src/components/data-table.tsx +82 -18
  232. package/src/components/error-boundary.tsx +2 -2
  233. package/src/components/floating-panel.tsx +67 -16
  234. package/src/components/form-actions.tsx +2 -2
  235. package/src/components/icon-button.tsx +21 -8
  236. package/src/components/nav-pills.tsx +29 -2
  237. package/src/components/page-header-context.ts +8 -0
  238. package/src/components/page-header.tsx +34 -2
  239. package/src/components/pie-chart.tsx +2 -1
  240. package/src/components/progress-bar.tsx +6 -3
  241. package/src/components/reauth-dialog.tsx +1 -1
  242. package/src/components/row-actions-labels.ts +2 -0
  243. package/src/components/row-actions.tsx +154 -33
  244. package/src/components/section-nav.tsx +65 -0
  245. package/src/components/series-chart.tsx +12 -3
  246. package/src/components/share-card.tsx +1 -1
  247. package/src/components/signature-pad.tsx +1 -1
  248. package/src/components/sparkline.tsx +2 -1
  249. package/src/components/stat-tile.tsx +19 -7
  250. package/src/components/status-dot.tsx +7 -1
  251. package/src/components/tooltip.tsx +3 -1
  252. package/src/components/translation-review-editor.tsx +2 -2
  253. package/src/components/translation-review.tsx +2 -2
  254. package/src/components/treemap.tsx +34 -8
  255. package/src/components/use-table-state.ts +63 -8
  256. package/src/data-table.ts +3 -3
  257. package/src/demo/demo-banner.tsx +1 -1
  258. package/src/demo/demo-ended.tsx +1 -1
  259. package/src/demo/demo-start.tsx +1 -1
  260. package/src/feedback/feedback-inbox.tsx +1 -1
  261. package/src/hooks/use-dismiss.ts +43 -4
  262. package/src/hooks/use-document-title.ts +31 -0
  263. package/src/i18n/german.ts +4 -0
  264. package/src/i18n/kit-labels.tsx +76 -1
  265. package/src/i18n/locales/es.ts +4 -0
  266. package/src/i18n/locales/fr.ts +4 -0
  267. package/src/i18n/locales/hu.ts +4 -0
  268. package/src/i18n/locales/it.ts +4 -0
  269. package/src/i18n/locales/zh.ts +4 -0
  270. package/src/i18n/review.ts +2 -0
  271. package/src/i18n/shared-labels.ts +1 -0
  272. package/src/index.ts +13 -0
  273. package/src/landing/landing-actions.tsx +2 -2
  274. package/src/landing/public-header.tsx +2 -2
  275. package/src/lib/clipboard-minus.ts +94 -0
  276. package/src/lib/document-title.ts +41 -0
  277. package/src/lib/float-clearance.ts +19 -3
  278. package/src/lib/format.ts +115 -30
  279. package/src/lib/minus.ts +40 -0
  280. package/src/lib/status-tone.ts +43 -0
  281. package/src/settings/settings-layout.tsx +3 -2
  282. package/src/shell/account-menu-language.tsx +14 -2
  283. package/src/shell/app-shell-labels.ts +1 -0
  284. package/src/shell/app-shell.tsx +291 -68
  285. package/src/shell/language-code.ts +5 -0
  286. package/src/shell/topbar-controls.tsx +35 -15
  287. package/src/testing/css-variables.generated.ts +6 -6
  288. package/src/theme/chart-palette.ts +102 -20
  289. package/src/theme/palette-presets.ts +121 -84
  290. package/src/tour/tour.tsx +1 -1
  291. package/src/wizard/stepper-nav.tsx +4 -4
  292. package/tokens.css +28 -18
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/shell/topbar-controls.tsx"],"sourcesContent":["import { Check, Globe, Moon, Palette, Sun } from \"lucide-react\";\nimport { cn } from \"../lib/cn\";\nimport { HoverMenu } from \"../components/hover-menu\";\nimport type { PalettePreset } from \"../theme/palette-presets\";\nimport type { ThemeMode } from \"../theme/theme-store\";\nimport { Tooltip } from \"../components/tooltip\";\nimport { DEFAULT_TOP_BAR_LABELS, useKitLabels } from \"../i18n/kit-labels\";\n\n/** Shared square icon-button styling for top-bar triggers, so app-owned controls\n * (account/feedback menus) line up with the shared ones. */\nexport const TOPBAR_TRIGGER_CLASS =\n \"size-9 rounded-md flex items-center justify-center text-secondary hover:text-primary hover:bg-hover\";\n\n/** Shared row styling for the top-bar hover-menu items. `text-start`, so a label that\n * wraps aligns to the reading edge in RTL too. */\nexport const TOPBAR_MENU_ITEM_CLASS =\n \"flex w-full items-center justify-between gap-3 px-3 py-2 text-sm text-start text-secondary hover:bg-hover\";\n\n/** The small uppercase heading row the top-bar menus share (`heading`). */\nfunction MenuHeading({ children }: { children: string }) {\n return (\n <li className=\"px-3 py-1 text-micro font-semibold uppercase tracking-wide text-placeholder\">\n {children}\n </li>\n );\n}\n\n/**\n * Sun/Moon toggle for the light/dark theme. Controlled: the app owns the mode, and the\n * toggle keeps no storage key of its own (docs/top-bar-harmonization.md §6.4).\n *\n * `mode` is the theme ON SCREEN — the resolved one, so with a \"system\" preference it is\n * whatever the OS gives. A tap asks for the opposite of that: `onChange(\"light\")` while\n * dark is shown, `onChange(\"dark\")` while light is. The app writes it as its theme\n * preference (an explicit light or dark), where Settings → Appearance keeps the three-way\n * choice. `onToggle` is the older way to hear the tap, without the next mode; both fire.\n */\nexport function ThemeToggle({\n mode,\n onToggle,\n onChange,\n \"aria-label\": ariaLabel,\n title,\n className,\n}: {\n /** The resolved light/dark on screen (`useThemeStore`'s `mode`, not its preference). */\n mode: ThemeMode;\n /** Hears a tap. Kept for 0.41's callers; `onChange` says what to switch to. */\n onToggle?: () => void;\n /** 0.42: the mode to switch to — always the opposite of `mode`. */\n onChange?: (next: ThemeMode) => void;\n /** Default: `topBar.theme` from the {@link UiKitProvider}, else English. */\n \"aria-label\"?: string;\n title?: string;\n className?: string;\n}) {\n // This had no default at all, so a toggle rendered without `aria-label` was an\n // icon button with no name — a reader heard \"button\" and nothing else.\n const labels = useKitLabels(\"topBar\", DEFAULT_TOP_BAR_LABELS, { theme: ariaLabel });\n const Icon = mode === \"dark\" ? Moon : Sun;\n const button = (\n <button\n type=\"button\"\n onClick={() => {\n onChange?.(mode === \"dark\" ? \"light\" : \"dark\");\n onToggle?.();\n }}\n aria-label={labels.theme}\n className={cn(TOPBAR_TRIGGER_CLASS, className)}\n >\n <Icon className=\"size-5\" />\n </button>\n );\n // The name on hover and focus even without a `title` (0.48, keksdose dev#610); a bubble\n // that only repeats it is visual, in a fragment as on IconButton — see OptionSwitcherMenu.\n const tip = title ?? labels.theme;\n return (\n <Tooltip label={tip} portal>\n {tip === labels.theme ? <>{button}</> : button}\n </Tooltip>\n );\n}\n\n/** Appearance-preset switcher: a hover menu of palette presets with per-theme\n * colour swatches. Controlled — the app owns the active id and applies it. */\nexport function PaletteMenu({\n palettes,\n activeId,\n mode,\n onSelect,\n \"aria-label\": ariaLabel,\n heading,\n}: {\n palettes: PalettePreset[];\n activeId: string;\n mode: ThemeMode;\n onSelect: (id: string) => void;\n /** Default: `topBar.palette` from the {@link UiKitProvider}, else English. */\n \"aria-label\"?: string;\n /** Optional small heading row above the list. */\n heading?: string;\n}) {\n const labels = useKitLabels(\"topBar\", DEFAULT_TOP_BAR_LABELS, { palette: ariaLabel });\n const activeName = palettes.find((p) => p.id === activeId)?.name ?? activeId;\n return (\n <HoverMenu\n aria-label={labels.palette}\n trigger={({ toggle }) => (\n <Tooltip label={activeName} portal>\n <button\n type=\"button\"\n onClick={toggle}\n aria-label={labels.palette}\n className={TOPBAR_TRIGGER_CLASS}\n >\n {/* A DATA colour, not a semantic one: the palette mark is the\n \"colour\" control, so it carries a colour, and which hue is\n arbitrary. `--chart-1` is the violet end of the CVD-safe\n categorical ramp and flips per theme — a fixed `violet-500` did\n not, and stayed a cool violet island on a warm page. */}\n <Palette className=\"size-5 text-[var(--chart-1)]\" />\n </button>\n </Tooltip>\n )}\n >\n {(close) => (\n <ul className=\"w-60 py-1\">\n {heading && <MenuHeading>{heading}</MenuHeading>}\n {palettes.map((p) => {\n const ts = p[mode];\n const swatches = [ts.bgPage, ts.brand, ts.moneyIncome, ts.moneyExpense, ts.moneyNet];\n return (\n <li key={p.id}>\n <button\n type=\"button\"\n onClick={() => {\n onSelect(p.id);\n close();\n }}\n className={TOPBAR_MENU_ITEM_CLASS}\n >\n <span className=\"flex min-w-0 items-center gap-2\">\n <span className=\"flex shrink-0 gap-0.5\">\n {/* Deliberately a neutral at 10% and not a token: this hairline\n sits on an ARBITRARY preset colour, so it has to read against\n whatever that preset paints, which no themed border does. */}\n {swatches.map((c, i) => (\n <span\n key={i}\n className=\"size-3.5 rounded-sm ring-1 ring-inset ring-black/10 dark:ring-white/10\"\n style={{ background: c }}\n />\n ))}\n </span>\n <span className=\"flex min-w-0 flex-col text-start\">\n <span className=\"truncate\">{p.name}</span>\n <span className=\"truncate text-caption text-placeholder\">\n {p.blurb}\n </span>\n </span>\n </span>\n {activeId === p.id && (\n <Check className=\"size-4 shrink-0 text-secondary\" />\n )}\n </button>\n </li>\n );\n })}\n </ul>\n )}\n </HoverMenu>\n );\n}\n\nexport interface LanguageOption {\n code: string;\n label: string;\n /** ISO 3166-1 alpha-2 country code (lowercase) for the flag-icons flag. */\n country: string;\n}\n\n/** Language switcher: a hover menu of options with flags and a check on the\n * active one. Controlled — the app owns the current code and change handler.\n *\n * The trigger is the *active* option's flag, not an icon: lucide's `Languages`\n * glyph (文A) is the same mark Chrome puts in the omnibox for \"translate this\n * page\", so it read as machine-translation rather than \"pick the UI language\"\n * (Keksdose feedback #121). The flag also shows which language is currently on,\n * which no icon can, and costs the same width on a crowded mobile bar. A globe\n * covers the case where `current` matches nothing in `options`. */\nexport function LanguageMenu({\n options,\n current,\n onChange,\n \"aria-label\": ariaLabel,\n heading,\n}: {\n options: LanguageOption[];\n current: string | undefined;\n onChange: (code: string) => void;\n /** Default: `topBar.language` from the {@link UiKitProvider}, else English. */\n \"aria-label\"?: string;\n /** Optional small heading row above the list — as on `TopBarActionMenu`,\n * `OptionSwitcherMenu` and `PaletteMenu`. */\n heading?: string;\n}) {\n const labels = useKitLabels(\"topBar\", DEFAULT_TOP_BAR_LABELS, { language: ariaLabel });\n const active = options.find((o) => o.code === current);\n return (\n <HoverMenu\n aria-label={labels.language}\n trigger={({ toggle }) => (\n <Tooltip label={active?.label ?? labels.language} portal>\n <button\n type=\"button\"\n onClick={toggle}\n aria-label={labels.language}\n className={TOPBAR_TRIGGER_CLASS}\n >\n {active ? (\n <span\n aria-hidden\n className={`fi fi-${active.country} inline-block h-[0.9375rem] w-5 shrink-0 rounded-sm shadow-[0_0_0_1px_rgba(0,0,0,0.08)]`}\n />\n ) : (\n <Globe className=\"size-5\" />\n )}\n </button>\n </Tooltip>\n )}\n >\n {(close) => (\n <ul className=\"py-1\">\n {heading && <MenuHeading>{heading}</MenuHeading>}\n {options.map((lang) => {\n const isActive = lang.code === current;\n return (\n <li key={lang.code}>\n <button\n type=\"button\"\n onClick={() => {\n onChange(lang.code);\n close();\n }}\n className={TOPBAR_MENU_ITEM_CLASS}\n >\n <span className=\"flex items-center gap-2\">\n <span\n aria-hidden\n className={`fi fi-${lang.country} inline-block h-[0.9375rem] w-5 shrink-0 rounded-sm shadow-[0_0_0_1px_rgba(0,0,0,0.08)]`}\n />\n {lang.label}\n </span>\n {isActive && <Check className=\"size-4 text-secondary\" />}\n </button>\n </li>\n );\n })}\n </ul>\n )}\n </HoverMenu>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/shell/topbar-controls.tsx"],"sourcesContent":["import { Check, Globe, Moon, Palette, Sun } from \"lucide-react\";\nimport { cn } from \"../lib/cn\";\nimport { HoverMenu } from \"../components/hover-menu\";\nimport type { PalettePreset } from \"../theme/palette-presets\";\nimport type { ThemeMode } from \"../theme/theme-store\";\nimport { Tooltip } from \"../components/tooltip\";\nimport { DEFAULT_TOP_BAR_LABELS, useKitLabels } from \"../i18n/kit-labels\";\nimport { languageCodeLabel } from \"./language-code\";\n\n/** Shared square icon-button styling for top-bar triggers, so app-owned controls\n * (account/feedback menus) line up with the shared ones. */\nexport const TOPBAR_TRIGGER_CLASS =\n \"size-9 rounded-md flex items-center justify-center text-secondary hover:text-primary hover:bg-hover\";\n\n/** Shared row styling for the top-bar hover-menu items. `text-start`, so a label that\n * wraps aligns to the reading edge in RTL too. */\nexport const TOPBAR_MENU_ITEM_CLASS =\n \"flex w-full items-center justify-between gap-3 px-3 py-2 text-sm text-start text-secondary hover:bg-hover\";\n\n/** The small uppercase heading row the top-bar menus share (`heading`). */\nfunction MenuHeading({ children }: { children: string }) {\n return (\n <li className=\"px-3 py-1 text-micro font-semibold uppercase tracking-wide text-placeholder\">\n {children}\n </li>\n );\n}\n\n/**\n * Sun/Moon toggle for the light/dark theme. Controlled: the app owns the mode, and the\n * toggle keeps no storage key of its own (docs/top-bar-harmonization.md §6.4).\n *\n * `mode` is the theme ON SCREEN — the resolved one, so with a \"system\" preference it is\n * whatever the OS gives. A tap asks for the opposite of that: `onChange(\"light\")` while\n * dark is shown, `onChange(\"dark\")` while light is. The app writes it as its theme\n * preference (an explicit light or dark), where Settings → Appearance keeps the three-way\n * choice. `onToggle` is the older way to hear the tap, without the next mode; both fire.\n */\nexport function ThemeToggle({\n mode,\n onToggle,\n onChange,\n \"aria-label\": ariaLabel,\n title,\n className,\n}: {\n /** The resolved light/dark on screen (`useThemeStore`'s `mode`, not its preference). */\n mode: ThemeMode;\n /** Hears a tap. Kept for 0.41's callers; `onChange` says what to switch to. */\n onToggle?: () => void;\n /** 0.42: the mode to switch to — always the opposite of `mode`. */\n onChange?: (next: ThemeMode) => void;\n /** Default: `topBar.theme` from the {@link UiKitProvider}, else English. */\n \"aria-label\"?: string;\n title?: string;\n className?: string;\n}) {\n // This had no default at all, so a toggle rendered without `aria-label` was an\n // icon button with no name — a reader heard \"button\" and nothing else.\n const labels = useKitLabels(\"topBar\", DEFAULT_TOP_BAR_LABELS, { theme: ariaLabel });\n const Icon = mode === \"dark\" ? Moon : Sun;\n const button = (\n <button\n type=\"button\"\n onClick={() => {\n onChange?.(mode === \"dark\" ? \"light\" : \"dark\");\n onToggle?.();\n }}\n aria-label={labels.theme}\n className={cn(TOPBAR_TRIGGER_CLASS, className)}\n >\n <Icon className=\"size-5\" />\n </button>\n );\n // The name on hover and focus even without a `title` (0.48, keksdose dev#610); a bubble\n // that only repeats it is visual, in a fragment as on IconButton — see OptionSwitcherMenu.\n const tip = title ?? labels.theme;\n return (\n <Tooltip label={tip} portal>\n {tip === labels.theme ? <>{button}</> : button}\n </Tooltip>\n );\n}\n\n/** Appearance-preset switcher: a hover menu of palette presets with per-theme\n * colour swatches. Controlled — the app owns the active id and applies it. */\nexport function PaletteMenu({\n palettes,\n activeId,\n mode,\n onSelect,\n \"aria-label\": ariaLabel,\n heading,\n}: {\n palettes: PalettePreset[];\n activeId: string;\n mode: ThemeMode;\n onSelect: (id: string) => void;\n /** Default: `topBar.palette` from the {@link UiKitProvider}, else English. */\n \"aria-label\"?: string;\n /** Optional small heading row above the list. */\n heading?: string;\n}) {\n const labels = useKitLabels(\"topBar\", DEFAULT_TOP_BAR_LABELS, { palette: ariaLabel });\n const activeName = palettes.find((p) => p.id === activeId)?.name ?? activeId;\n return (\n <HoverMenu\n aria-label={labels.palette}\n trigger={({ toggle }) => (\n <Tooltip label={activeName} portal>\n <button\n type=\"button\"\n onClick={toggle}\n aria-label={labels.palette}\n className={TOPBAR_TRIGGER_CLASS}\n >\n {/* A DATA colour, not a semantic one: the palette mark is the\n \"colour\" control, so it carries a colour, and which hue is\n arbitrary. `--chart-1` is the violet end of the CVD-safe\n categorical ramp and flips per theme — a fixed `violet-500` did\n not, and stayed a cool violet island on a warm page. */}\n <Palette className=\"size-5 text-[var(--chart-1)]\" />\n </button>\n </Tooltip>\n )}\n >\n {(close) => (\n <ul className=\"w-60 py-1\">\n {heading && <MenuHeading>{heading}</MenuHeading>}\n {palettes.map((p) => {\n const ts = p[mode];\n const swatches = [ts.bgPage, ts.brand, ts.moneyIncome, ts.moneyExpense, ts.moneyNet];\n return (\n <li key={p.id}>\n <button\n type=\"button\"\n onClick={() => {\n onSelect(p.id);\n close();\n }}\n className={TOPBAR_MENU_ITEM_CLASS}\n >\n <span className=\"flex min-w-0 items-center gap-2\">\n <span className=\"flex shrink-0 gap-0.5\">\n {/* Deliberately a neutral at 10% and not a token: this hairline\n sits on an ARBITRARY preset colour, so it has to read against\n whatever that preset paints, which no themed border does. */}\n {swatches.map((c, i) => (\n <span\n key={i}\n className=\"size-3.5 rounded-sm ring-1 ring-inset ring-black/10 dark:ring-white/10\"\n style={{ background: c }}\n />\n ))}\n </span>\n <span className=\"flex min-w-0 flex-col text-start\">\n <span className=\"truncate\">{p.name}</span>\n <span className=\"truncate text-caption text-placeholder\">\n {p.blurb}\n </span>\n </span>\n </span>\n {activeId === p.id && (\n <Check className=\"size-4 shrink-0 text-secondary\" />\n )}\n </button>\n </li>\n );\n })}\n </ul>\n )}\n </HoverMenu>\n );\n}\n\nexport interface LanguageOption {\n code: string;\n label: string;\n /** ISO 3166-1 alpha-2 country code (lowercase) for the flag-icons flag. */\n country: string;\n}\n\n/** What {@link LanguageMenu}'s trigger shows of the active language (0.49 KIT-10). */\nexport type LanguageMenuTrigger = \"flag+code\" | \"code\" | \"flag\";\n\n/** The trigger with a code: as tall as the others, as wide as flag and code need. */\nconst LANGUAGE_TRIGGER_WIDE_CLASS = TOPBAR_TRIGGER_CLASS.replace(\"size-9\", \"h-9 min-w-9 gap-1.5 px-2\");\n\nconst FLAG_CLASS =\n \"inline-block h-[0.9375rem] w-5 shrink-0 rounded-sm shadow-[0_0_0_1px_rgba(0,0,0,0.08)]\";\n\n/** Language switcher: a hover menu of options with flags and a check on the\n * active one. Controlled — the app owns the current code and change handler.\n *\n * The trigger is the *active* option's flag and its language code in capitals\n * (\"DE\", \"FR\", \"IT\"; `trigger`, 0.49 KIT-10), not an icon: lucide's `Languages`\n * glyph (文A) is the same mark Chrome puts in the omnibox for \"translate this\n * page\", so it read as machine-translation rather than \"pick the UI language\"\n * (Keksdose feedback #121). A flag alone could not tell de-CH, fr-CH and it-CH\n * apart — one Swiss flag each — so the code is beside it. It is named \"Choose\n * language, current: Deutsch\" (`topBar.languageCurrent`). A globe covers the\n * case where `current` matches nothing in `options`. */\nexport function LanguageMenu({\n options,\n current,\n onChange,\n \"aria-label\": ariaLabel,\n heading,\n trigger = \"flag+code\",\n}: {\n options: LanguageOption[];\n current: string | undefined;\n onChange: (code: string) => void;\n /** The menu's name, and the trigger's as given. Default: the menu `topBar.language`,\n * the trigger `topBar.languageCurrent(<the active label>)`, from the\n * {@link UiKitProvider}, else English. */\n \"aria-label\"?: string;\n /** Optional small heading row above the list — as on `TopBarActionMenu`,\n * `OptionSwitcherMenu` and `PaletteMenu`. */\n heading?: string;\n /** What the trigger shows: `\"flag+code\"` (default), `\"code\"` or `\"flag\"`. */\n trigger?: LanguageMenuTrigger;\n}) {\n const labels = useKitLabels(\"topBar\", DEFAULT_TOP_BAR_LABELS, { language: ariaLabel });\n const active = options.find((o) => o.code === current);\n const triggerName = ariaLabel ?? (active ? labels.languageCurrent(active.label) : labels.language);\n const showsCode = active !== undefined && trigger !== \"flag\";\n return (\n <HoverMenu\n aria-label={labels.language}\n trigger={({ toggle }) => (\n <Tooltip label={active?.label ?? labels.language} portal>\n <button\n type=\"button\"\n onClick={toggle}\n aria-label={triggerName}\n className={showsCode ? LANGUAGE_TRIGGER_WIDE_CLASS : TOPBAR_TRIGGER_CLASS}\n >\n {active ? (\n <>\n {trigger !== \"code\" && <span aria-hidden className={`fi fi-${active.country} ${FLAG_CLASS}`} />}\n {showsCode && (\n <span aria-hidden className=\"text-xs font-semibold tabular-nums tracking-wide\">\n {languageCodeLabel(active.code)}\n </span>\n )}\n </>\n ) : (\n <Globe className=\"size-5\" />\n )}\n </button>\n </Tooltip>\n )}\n >\n {(close) => (\n <ul className=\"py-1\">\n {heading && <MenuHeading>{heading}</MenuHeading>}\n {options.map((lang) => {\n const isActive = lang.code === current;\n return (\n <li key={lang.code}>\n <button\n type=\"button\"\n onClick={() => {\n onChange(lang.code);\n close();\n }}\n className={TOPBAR_MENU_ITEM_CLASS}\n >\n <span className=\"flex items-center gap-2\">\n <span aria-hidden className={`fi fi-${lang.country} ${FLAG_CLASS}`} />\n {lang.label}\n </span>\n {isActive && <Check className=\"size-4 text-secondary\" />}\n </button>\n </li>\n );\n })}\n </ul>\n )}\n </HoverMenu>\n );\n}\n"],"mappings":"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","names":[]}
@@ -1,5 +1,5 @@
1
1
  /** Every custom property tokens.css declares, `@theme inline` names and `--app-nav-h`
2
- * left out (100 names). */
2
+ * left out (102 names). */
3
3
  export declare const KIT_NAMES: string;
4
4
  /** The theme variables of Tailwind 4.3.3's `theme.css` (413 names). */
5
5
  export declare const TAILWIND_THEME_NAMES: string;
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- const KIT_NAMES = "--bg-active --bg-hover --bg-inverse --bg-page --bg-surface --bg-surface-2 --border --border-strong --brand --brand-bg --brand-bg-hover --brand-contrast --brand-hover --brand-muted --breakpoint-3xl --chart-1 --chart-2 --chart-3 --chart-4 --chart-5 --chart-6 --chart-7 --chart-8 --chart-9 --danger --danger-bg --danger-border --danger-border-strong --danger-contrast --danger-hover --flyout-from --focus-ring-width --hue-blue --hue-blue-bg --hue-blue-border --hue-blue-contrast --hue-indigo --hue-indigo-bg --hue-indigo-border --hue-indigo-contrast --hue-orange --hue-orange-bg --hue-orange-border --hue-orange-contrast --hue-purple --hue-purple-bg --hue-purple-border --hue-purple-contrast --hue-teal --hue-teal-bg --hue-teal-border --hue-teal-contrast --info --info-bg --info-border --info-contrast --media-ink --media-scrim --media-scrim-hover --money-expense --money-expense-contrast --money-income --money-income-contrast --money-net --money-net-contrast --money-neutral --money-neutral-contrast --neutral --neutral-contrast --status-edited --status-error --status-pending --status-synced --success --success-bg --success-border --success-contrast --text-caption --text-inverse --text-micro --text-muted --text-placeholder --text-primary --text-secondary --toast-action-bg --toast-action-fg --toast-danger-bg --toast-danger-border --toast-info-bg --toast-info-border --toast-success-bg --toast-success-border --toast-warning-bg --toast-warning-border --warning --warning-bg --warning-border --warning-border-strong --warning-contrast --z-toast";
2
+ const KIT_NAMES = "--bg-active --bg-hover --bg-inverse --bg-page --bg-surface --bg-surface-2 --border --border-strong --brand --brand-bg --brand-bg-hover --brand-contrast --brand-hover --brand-muted --breakpoint-3xl --chart-1 --chart-2 --chart-3 --chart-4 --chart-5 --chart-6 --chart-7 --chart-8 --chart-9 --chart-ink --chart-muted --danger --danger-bg --danger-border --danger-border-strong --danger-contrast --danger-hover --flyout-from --focus-ring-width --hue-blue --hue-blue-bg --hue-blue-border --hue-blue-contrast --hue-indigo --hue-indigo-bg --hue-indigo-border --hue-indigo-contrast --hue-orange --hue-orange-bg --hue-orange-border --hue-orange-contrast --hue-purple --hue-purple-bg --hue-purple-border --hue-purple-contrast --hue-teal --hue-teal-bg --hue-teal-border --hue-teal-contrast --info --info-bg --info-border --info-contrast --media-ink --media-scrim --media-scrim-hover --money-expense --money-expense-contrast --money-income --money-income-contrast --money-net --money-net-contrast --money-neutral --money-neutral-contrast --neutral --neutral-contrast --status-edited --status-error --status-pending --status-synced --success --success-bg --success-border --success-contrast --text-caption --text-inverse --text-micro --text-muted --text-placeholder --text-primary --text-secondary --toast-action-bg --toast-action-fg --toast-danger-bg --toast-danger-border --toast-info-bg --toast-info-border --toast-success-bg --toast-success-border --toast-warning-bg --toast-warning-border --warning --warning-bg --warning-border --warning-border-strong --warning-contrast --z-toast";
3
3
  const TAILWIND_THEME_NAMES = "--animate-bounce --animate-ping --animate-pulse --animate-spin --aspect-video --blur-2xl --blur-3xl --blur-lg --blur-md --blur-sm --blur-xl --blur-xs --breakpoint-2xl --breakpoint-lg --breakpoint-md --breakpoint-sm --breakpoint-xl --color-amber-100 --color-amber-200 --color-amber-300 --color-amber-400 --color-amber-50 --color-amber-500 --color-amber-600 --color-amber-700 --color-amber-800 --color-amber-900 --color-amber-950 --color-black --color-blue-100 --color-blue-200 --color-blue-300 --color-blue-400 --color-blue-50 --color-blue-500 --color-blue-600 --color-blue-700 --color-blue-800 --color-blue-900 --color-blue-950 --color-cyan-100 --color-cyan-200 --color-cyan-300 --color-cyan-400 --color-cyan-50 --color-cyan-500 --color-cyan-600 --color-cyan-700 --color-cyan-800 --color-cyan-900 --color-cyan-950 --color-emerald-100 --color-emerald-200 --color-emerald-300 --color-emerald-400 --color-emerald-50 --color-emerald-500 --color-emerald-600 --color-emerald-700 --color-emerald-800 --color-emerald-900 --color-emerald-950 --color-fuchsia-100 --color-fuchsia-200 --color-fuchsia-300 --color-fuchsia-400 --color-fuchsia-50 --color-fuchsia-500 --color-fuchsia-600 --color-fuchsia-700 --color-fuchsia-800 --color-fuchsia-900 --color-fuchsia-950 --color-gray-100 --color-gray-200 --color-gray-300 --color-gray-400 --color-gray-50 --color-gray-500 --color-gray-600 --color-gray-700 --color-gray-800 --color-gray-900 --color-gray-950 --color-green-100 --color-green-200 --color-green-300 --color-green-400 --color-green-50 --color-green-500 --color-green-600 --color-green-700 --color-green-800 --color-green-900 --color-green-950 --color-indigo-100 --color-indigo-200 --color-indigo-300 --color-indigo-400 --color-indigo-50 --color-indigo-500 --color-indigo-600 --color-indigo-700 --color-indigo-800 --color-indigo-900 --color-indigo-950 --color-lime-100 --color-lime-200 --color-lime-300 --color-lime-400 --color-lime-50 --color-lime-500 --color-lime-600 --color-lime-700 --color-lime-800 --color-lime-900 --color-lime-950 --color-mauve-100 --color-mauve-200 --color-mauve-300 --color-mauve-400 --color-mauve-50 --color-mauve-500 --color-mauve-600 --color-mauve-700 --color-mauve-800 --color-mauve-900 --color-mauve-950 --color-mist-100 --color-mist-200 --color-mist-300 --color-mist-400 --color-mist-50 --color-mist-500 --color-mist-600 --color-mist-700 --color-mist-800 --color-mist-900 --color-mist-950 --color-neutral-100 --color-neutral-200 --color-neutral-300 --color-neutral-400 --color-neutral-50 --color-neutral-500 --color-neutral-600 --color-neutral-700 --color-neutral-800 --color-neutral-900 --color-neutral-950 --color-olive-100 --color-olive-200 --color-olive-300 --color-olive-400 --color-olive-50 --color-olive-500 --color-olive-600 --color-olive-700 --color-olive-800 --color-olive-900 --color-olive-950 --color-orange-100 --color-orange-200 --color-orange-300 --color-orange-400 --color-orange-50 --color-orange-500 --color-orange-600 --color-orange-700 --color-orange-800 --color-orange-900 --color-orange-950 --color-pink-100 --color-pink-200 --color-pink-300 --color-pink-400 --color-pink-50 --color-pink-500 --color-pink-600 --color-pink-700 --color-pink-800 --color-pink-900 --color-pink-950 --color-purple-100 --color-purple-200 --color-purple-300 --color-purple-400 --color-purple-50 --color-purple-500 --color-purple-600 --color-purple-700 --color-purple-800 --color-purple-900 --color-purple-950 --color-red-100 --color-red-200 --color-red-300 --color-red-400 --color-red-50 --color-red-500 --color-red-600 --color-red-700 --color-red-800 --color-red-900 --color-red-950 --color-rose-100 --color-rose-200 --color-rose-300 --color-rose-400 --color-rose-50 --color-rose-500 --color-rose-600 --color-rose-700 --color-rose-800 --color-rose-900 --color-rose-950 --color-sky-100 --color-sky-200 --color-sky-300 --color-sky-400 --color-sky-50 --color-sky-500 --color-sky-600 --color-sky-700 --color-sky-800 --color-sky-900 --color-sky-950 --color-slate-100 --color-slate-200 --color-slate-300 --color-slate-400 --color-slate-50 --color-slate-500 --color-slate-600 --color-slate-700 --color-slate-800 --color-slate-900 --color-slate-950 --color-stone-100 --color-stone-200 --color-stone-300 --color-stone-400 --color-stone-50 --color-stone-500 --color-stone-600 --color-stone-700 --color-stone-800 --color-stone-900 --color-stone-950 --color-taupe-100 --color-taupe-200 --color-taupe-300 --color-taupe-400 --color-taupe-50 --color-taupe-500 --color-taupe-600 --color-taupe-700 --color-taupe-800 --color-taupe-900 --color-taupe-950 --color-teal-100 --color-teal-200 --color-teal-300 --color-teal-400 --color-teal-50 --color-teal-500 --color-teal-600 --color-teal-700 --color-teal-800 --color-teal-900 --color-teal-950 --color-violet-100 --color-violet-200 --color-violet-300 --color-violet-400 --color-violet-50 --color-violet-500 --color-violet-600 --color-violet-700 --color-violet-800 --color-violet-900 --color-violet-950 --color-white --color-yellow-100 --color-yellow-200 --color-yellow-300 --color-yellow-400 --color-yellow-50 --color-yellow-500 --color-yellow-600 --color-yellow-700 --color-yellow-800 --color-yellow-900 --color-yellow-950 --color-zinc-100 --color-zinc-200 --color-zinc-300 --color-zinc-400 --color-zinc-50 --color-zinc-500 --color-zinc-600 --color-zinc-700 --color-zinc-800 --color-zinc-900 --color-zinc-950 --container-2xl --container-2xs --container-3xl --container-3xs --container-4xl --container-5xl --container-6xl --container-7xl --container-lg --container-md --container-sm --container-xl --container-xs --default-font-family --default-font-feature-settings --default-font-variation-settings --default-mono-font-family --default-mono-font-feature-settings --default-mono-font-variation-settings --default-transition-duration --default-transition-timing-function --drop-shadow-2xl --drop-shadow-lg --drop-shadow-md --drop-shadow-sm --drop-shadow-xl --drop-shadow-xs --ease-in --ease-in-out --ease-out --font-mono --font-sans --font-serif --font-weight-black --font-weight-bold --font-weight-extrabold --font-weight-extralight --font-weight-light --font-weight-medium --font-weight-normal --font-weight-semibold --font-weight-thin --inset-shadow-2xs --inset-shadow-sm --inset-shadow-xs --leading-loose --leading-normal --leading-relaxed --leading-snug --leading-tight --perspective-distant --perspective-dramatic --perspective-midrange --perspective-near --perspective-normal --radius-2xl --radius-3xl --radius-4xl --radius-lg --radius-md --radius-sm --radius-xl --radius-xs --shadow-2xl --shadow-2xs --shadow-lg --shadow-md --shadow-sm --shadow-xl --shadow-xs --spacing --text-2xl --text-2xl--line-height --text-3xl --text-3xl--line-height --text-4xl --text-4xl--line-height --text-5xl --text-5xl--line-height --text-6xl --text-6xl--line-height --text-7xl --text-7xl--line-height --text-8xl --text-8xl--line-height --text-9xl --text-9xl--line-height --text-base --text-base--line-height --text-lg --text-lg--line-height --text-shadow-2xs --text-shadow-lg --text-shadow-md --text-shadow-sm --text-shadow-xs --text-sm --text-sm--line-height --text-xl --text-xl--line-height --text-xs --text-xs--line-height --tracking-normal --tracking-tight --tracking-tighter --tracking-wide --tracking-wider --tracking-widest";
4
4
  export {
5
5
  KIT_NAMES,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/testing/css-variables.generated.ts"],"sourcesContent":["// GENERATED by scripts/gen-kit-css-variables.mjs — do not edit by hand. Re-run it after\n// changing tokens.css or upgrading Tailwind; src/__tests__/token-vars-declared.test.ts\n// fails while this is stale.\n\n/** Every custom property tokens.css declares, `@theme inline` names and `--app-nav-h`\n * left out (100 names). */\nexport const KIT_NAMES =\n \"--bg-active --bg-hover --bg-inverse --bg-page --bg-surface --bg-surface-2 --border \" +\n \"--border-strong --brand --brand-bg --brand-bg-hover --brand-contrast --brand-hover --brand-muted \" +\n \"--breakpoint-3xl --chart-1 --chart-2 --chart-3 --chart-4 --chart-5 --chart-6 --chart-7 --chart-8 \" +\n \"--chart-9 --danger --danger-bg --danger-border --danger-border-strong --danger-contrast \" +\n \"--danger-hover --flyout-from --focus-ring-width --hue-blue --hue-blue-bg --hue-blue-border \" +\n \"--hue-blue-contrast --hue-indigo --hue-indigo-bg --hue-indigo-border --hue-indigo-contrast \" +\n \"--hue-orange --hue-orange-bg --hue-orange-border --hue-orange-contrast --hue-purple \" +\n \"--hue-purple-bg --hue-purple-border --hue-purple-contrast --hue-teal --hue-teal-bg \" +\n \"--hue-teal-border --hue-teal-contrast --info --info-bg --info-border --info-contrast --media-ink \" +\n \"--media-scrim --media-scrim-hover --money-expense --money-expense-contrast --money-income \" +\n \"--money-income-contrast --money-net --money-net-contrast --money-neutral \" +\n \"--money-neutral-contrast --neutral --neutral-contrast --status-edited --status-error \" +\n \"--status-pending --status-synced --success --success-bg --success-border --success-contrast \" +\n \"--text-caption --text-inverse --text-micro --text-muted --text-placeholder --text-primary \" +\n \"--text-secondary --toast-action-bg --toast-action-fg --toast-danger-bg --toast-danger-border \" +\n \"--toast-info-bg --toast-info-border --toast-success-bg --toast-success-border --toast-warning-bg \" +\n \"--toast-warning-border --warning --warning-bg --warning-border --warning-border-strong \" +\n \"--warning-contrast --z-toast\";\n\n/** The theme variables of Tailwind 4.3.3's `theme.css` (413 names). */\nexport const TAILWIND_THEME_NAMES =\n \"--animate-bounce --animate-ping --animate-pulse --animate-spin --aspect-video --blur-2xl \" +\n \"--blur-3xl --blur-lg --blur-md --blur-sm --blur-xl --blur-xs --breakpoint-2xl --breakpoint-lg \" +\n \"--breakpoint-md --breakpoint-sm --breakpoint-xl --color-amber-100 --color-amber-200 \" +\n \"--color-amber-300 --color-amber-400 --color-amber-50 --color-amber-500 --color-amber-600 \" +\n \"--color-amber-700 --color-amber-800 --color-amber-900 --color-amber-950 --color-black \" +\n \"--color-blue-100 --color-blue-200 --color-blue-300 --color-blue-400 --color-blue-50 \" +\n \"--color-blue-500 --color-blue-600 --color-blue-700 --color-blue-800 --color-blue-900 \" +\n \"--color-blue-950 --color-cyan-100 --color-cyan-200 --color-cyan-300 --color-cyan-400 \" +\n \"--color-cyan-50 --color-cyan-500 --color-cyan-600 --color-cyan-700 --color-cyan-800 \" +\n \"--color-cyan-900 --color-cyan-950 --color-emerald-100 --color-emerald-200 --color-emerald-300 \" +\n \"--color-emerald-400 --color-emerald-50 --color-emerald-500 --color-emerald-600 \" +\n \"--color-emerald-700 --color-emerald-800 --color-emerald-900 --color-emerald-950 \" +\n \"--color-fuchsia-100 --color-fuchsia-200 --color-fuchsia-300 --color-fuchsia-400 \" +\n \"--color-fuchsia-50 --color-fuchsia-500 --color-fuchsia-600 --color-fuchsia-700 \" +\n \"--color-fuchsia-800 --color-fuchsia-900 --color-fuchsia-950 --color-gray-100 --color-gray-200 \" +\n \"--color-gray-300 --color-gray-400 --color-gray-50 --color-gray-500 --color-gray-600 \" +\n \"--color-gray-700 --color-gray-800 --color-gray-900 --color-gray-950 --color-green-100 \" +\n \"--color-green-200 --color-green-300 --color-green-400 --color-green-50 --color-green-500 \" +\n \"--color-green-600 --color-green-700 --color-green-800 --color-green-900 --color-green-950 \" +\n \"--color-indigo-100 --color-indigo-200 --color-indigo-300 --color-indigo-400 --color-indigo-50 \" +\n \"--color-indigo-500 --color-indigo-600 --color-indigo-700 --color-indigo-800 --color-indigo-900 \" +\n \"--color-indigo-950 --color-lime-100 --color-lime-200 --color-lime-300 --color-lime-400 \" +\n \"--color-lime-50 --color-lime-500 --color-lime-600 --color-lime-700 --color-lime-800 \" +\n \"--color-lime-900 --color-lime-950 --color-mauve-100 --color-mauve-200 --color-mauve-300 \" +\n \"--color-mauve-400 --color-mauve-50 --color-mauve-500 --color-mauve-600 --color-mauve-700 \" +\n \"--color-mauve-800 --color-mauve-900 --color-mauve-950 --color-mist-100 --color-mist-200 \" +\n \"--color-mist-300 --color-mist-400 --color-mist-50 --color-mist-500 --color-mist-600 \" +\n \"--color-mist-700 --color-mist-800 --color-mist-900 --color-mist-950 --color-neutral-100 \" +\n \"--color-neutral-200 --color-neutral-300 --color-neutral-400 --color-neutral-50 \" +\n \"--color-neutral-500 --color-neutral-600 --color-neutral-700 --color-neutral-800 \" +\n \"--color-neutral-900 --color-neutral-950 --color-olive-100 --color-olive-200 --color-olive-300 \" +\n \"--color-olive-400 --color-olive-50 --color-olive-500 --color-olive-600 --color-olive-700 \" +\n \"--color-olive-800 --color-olive-900 --color-olive-950 --color-orange-100 --color-orange-200 \" +\n \"--color-orange-300 --color-orange-400 --color-orange-50 --color-orange-500 --color-orange-600 \" +\n \"--color-orange-700 --color-orange-800 --color-orange-900 --color-orange-950 --color-pink-100 \" +\n \"--color-pink-200 --color-pink-300 --color-pink-400 --color-pink-50 --color-pink-500 \" +\n \"--color-pink-600 --color-pink-700 --color-pink-800 --color-pink-900 --color-pink-950 \" +\n \"--color-purple-100 --color-purple-200 --color-purple-300 --color-purple-400 --color-purple-50 \" +\n \"--color-purple-500 --color-purple-600 --color-purple-700 --color-purple-800 --color-purple-900 \" +\n \"--color-purple-950 --color-red-100 --color-red-200 --color-red-300 --color-red-400 \" +\n \"--color-red-50 --color-red-500 --color-red-600 --color-red-700 --color-red-800 --color-red-900 \" +\n \"--color-red-950 --color-rose-100 --color-rose-200 --color-rose-300 --color-rose-400 \" +\n \"--color-rose-50 --color-rose-500 --color-rose-600 --color-rose-700 --color-rose-800 \" +\n \"--color-rose-900 --color-rose-950 --color-sky-100 --color-sky-200 --color-sky-300 \" +\n \"--color-sky-400 --color-sky-50 --color-sky-500 --color-sky-600 --color-sky-700 --color-sky-800 \" +\n \"--color-sky-900 --color-sky-950 --color-slate-100 --color-slate-200 --color-slate-300 \" +\n \"--color-slate-400 --color-slate-50 --color-slate-500 --color-slate-600 --color-slate-700 \" +\n \"--color-slate-800 --color-slate-900 --color-slate-950 --color-stone-100 --color-stone-200 \" +\n \"--color-stone-300 --color-stone-400 --color-stone-50 --color-stone-500 --color-stone-600 \" +\n \"--color-stone-700 --color-stone-800 --color-stone-900 --color-stone-950 --color-taupe-100 \" +\n \"--color-taupe-200 --color-taupe-300 --color-taupe-400 --color-taupe-50 --color-taupe-500 \" +\n \"--color-taupe-600 --color-taupe-700 --color-taupe-800 --color-taupe-900 --color-taupe-950 \" +\n \"--color-teal-100 --color-teal-200 --color-teal-300 --color-teal-400 --color-teal-50 \" +\n \"--color-teal-500 --color-teal-600 --color-teal-700 --color-teal-800 --color-teal-900 \" +\n \"--color-teal-950 --color-violet-100 --color-violet-200 --color-violet-300 --color-violet-400 \" +\n \"--color-violet-50 --color-violet-500 --color-violet-600 --color-violet-700 --color-violet-800 \" +\n \"--color-violet-900 --color-violet-950 --color-white --color-yellow-100 --color-yellow-200 \" +\n \"--color-yellow-300 --color-yellow-400 --color-yellow-50 --color-yellow-500 --color-yellow-600 \" +\n \"--color-yellow-700 --color-yellow-800 --color-yellow-900 --color-yellow-950 --color-zinc-100 \" +\n \"--color-zinc-200 --color-zinc-300 --color-zinc-400 --color-zinc-50 --color-zinc-500 \" +\n \"--color-zinc-600 --color-zinc-700 --color-zinc-800 --color-zinc-900 --color-zinc-950 \" +\n \"--container-2xl --container-2xs --container-3xl --container-3xs --container-4xl --container-5xl \" +\n \"--container-6xl --container-7xl --container-lg --container-md --container-sm --container-xl \" +\n \"--container-xs --default-font-family --default-font-feature-settings \" +\n \"--default-font-variation-settings --default-mono-font-family \" +\n \"--default-mono-font-feature-settings --default-mono-font-variation-settings \" +\n \"--default-transition-duration --default-transition-timing-function --drop-shadow-2xl \" +\n \"--drop-shadow-lg --drop-shadow-md --drop-shadow-sm --drop-shadow-xl --drop-shadow-xs --ease-in \" +\n \"--ease-in-out --ease-out --font-mono --font-sans --font-serif --font-weight-black \" +\n \"--font-weight-bold --font-weight-extrabold --font-weight-extralight --font-weight-light \" +\n \"--font-weight-medium --font-weight-normal --font-weight-semibold --font-weight-thin \" +\n \"--inset-shadow-2xs --inset-shadow-sm --inset-shadow-xs --leading-loose --leading-normal \" +\n \"--leading-relaxed --leading-snug --leading-tight --perspective-distant --perspective-dramatic \" +\n \"--perspective-midrange --perspective-near --perspective-normal --radius-2xl --radius-3xl \" +\n \"--radius-4xl --radius-lg --radius-md --radius-sm --radius-xl --radius-xs --shadow-2xl \" +\n \"--shadow-2xs --shadow-lg --shadow-md --shadow-sm --shadow-xl --shadow-xs --spacing --text-2xl \" +\n \"--text-2xl--line-height --text-3xl --text-3xl--line-height --text-4xl --text-4xl--line-height \" +\n \"--text-5xl --text-5xl--line-height --text-6xl --text-6xl--line-height --text-7xl \" +\n \"--text-7xl--line-height --text-8xl --text-8xl--line-height --text-9xl --text-9xl--line-height \" +\n \"--text-base --text-base--line-height --text-lg --text-lg--line-height --text-shadow-2xs \" +\n \"--text-shadow-lg --text-shadow-md --text-shadow-sm --text-shadow-xs --text-sm \" +\n \"--text-sm--line-height --text-xl --text-xl--line-height --text-xs --text-xs--line-height \" +\n \"--tracking-normal --tracking-tight --tracking-tighter --tracking-wide --tracking-wider \" +\n \"--tracking-widest\";\n"],"mappings":";AAMO,MAAM,YACX;AAoBK,MAAM,uBACX;","names":[]}
1
+ {"version":3,"sources":["../../src/testing/css-variables.generated.ts"],"sourcesContent":["// GENERATED by scripts/gen-kit-css-variables.mjs — do not edit by hand. Re-run it after\n// changing tokens.css or upgrading Tailwind; src/__tests__/token-vars-declared.test.ts\n// fails while this is stale.\n\n/** Every custom property tokens.css declares, `@theme inline` names and `--app-nav-h`\n * left out (102 names). */\nexport const KIT_NAMES =\n \"--bg-active --bg-hover --bg-inverse --bg-page --bg-surface --bg-surface-2 --border \" +\n \"--border-strong --brand --brand-bg --brand-bg-hover --brand-contrast --brand-hover --brand-muted \" +\n \"--breakpoint-3xl --chart-1 --chart-2 --chart-3 --chart-4 --chart-5 --chart-6 --chart-7 --chart-8 \" +\n \"--chart-9 --chart-ink --chart-muted --danger --danger-bg --danger-border --danger-border-strong \" +\n \"--danger-contrast --danger-hover --flyout-from --focus-ring-width --hue-blue --hue-blue-bg \" +\n \"--hue-blue-border --hue-blue-contrast --hue-indigo --hue-indigo-bg --hue-indigo-border \" +\n \"--hue-indigo-contrast --hue-orange --hue-orange-bg --hue-orange-border --hue-orange-contrast \" +\n \"--hue-purple --hue-purple-bg --hue-purple-border --hue-purple-contrast --hue-teal --hue-teal-bg \" +\n \"--hue-teal-border --hue-teal-contrast --info --info-bg --info-border --info-contrast --media-ink \" +\n \"--media-scrim --media-scrim-hover --money-expense --money-expense-contrast --money-income \" +\n \"--money-income-contrast --money-net --money-net-contrast --money-neutral \" +\n \"--money-neutral-contrast --neutral --neutral-contrast --status-edited --status-error \" +\n \"--status-pending --status-synced --success --success-bg --success-border --success-contrast \" +\n \"--text-caption --text-inverse --text-micro --text-muted --text-placeholder --text-primary \" +\n \"--text-secondary --toast-action-bg --toast-action-fg --toast-danger-bg --toast-danger-border \" +\n \"--toast-info-bg --toast-info-border --toast-success-bg --toast-success-border --toast-warning-bg \" +\n \"--toast-warning-border --warning --warning-bg --warning-border --warning-border-strong \" +\n \"--warning-contrast --z-toast\";\n\n/** The theme variables of Tailwind 4.3.3's `theme.css` (413 names). */\nexport const TAILWIND_THEME_NAMES =\n \"--animate-bounce --animate-ping --animate-pulse --animate-spin --aspect-video --blur-2xl \" +\n \"--blur-3xl --blur-lg --blur-md --blur-sm --blur-xl --blur-xs --breakpoint-2xl --breakpoint-lg \" +\n \"--breakpoint-md --breakpoint-sm --breakpoint-xl --color-amber-100 --color-amber-200 \" +\n \"--color-amber-300 --color-amber-400 --color-amber-50 --color-amber-500 --color-amber-600 \" +\n \"--color-amber-700 --color-amber-800 --color-amber-900 --color-amber-950 --color-black \" +\n \"--color-blue-100 --color-blue-200 --color-blue-300 --color-blue-400 --color-blue-50 \" +\n \"--color-blue-500 --color-blue-600 --color-blue-700 --color-blue-800 --color-blue-900 \" +\n \"--color-blue-950 --color-cyan-100 --color-cyan-200 --color-cyan-300 --color-cyan-400 \" +\n \"--color-cyan-50 --color-cyan-500 --color-cyan-600 --color-cyan-700 --color-cyan-800 \" +\n \"--color-cyan-900 --color-cyan-950 --color-emerald-100 --color-emerald-200 --color-emerald-300 \" +\n \"--color-emerald-400 --color-emerald-50 --color-emerald-500 --color-emerald-600 \" +\n \"--color-emerald-700 --color-emerald-800 --color-emerald-900 --color-emerald-950 \" +\n \"--color-fuchsia-100 --color-fuchsia-200 --color-fuchsia-300 --color-fuchsia-400 \" +\n \"--color-fuchsia-50 --color-fuchsia-500 --color-fuchsia-600 --color-fuchsia-700 \" +\n \"--color-fuchsia-800 --color-fuchsia-900 --color-fuchsia-950 --color-gray-100 --color-gray-200 \" +\n \"--color-gray-300 --color-gray-400 --color-gray-50 --color-gray-500 --color-gray-600 \" +\n \"--color-gray-700 --color-gray-800 --color-gray-900 --color-gray-950 --color-green-100 \" +\n \"--color-green-200 --color-green-300 --color-green-400 --color-green-50 --color-green-500 \" +\n \"--color-green-600 --color-green-700 --color-green-800 --color-green-900 --color-green-950 \" +\n \"--color-indigo-100 --color-indigo-200 --color-indigo-300 --color-indigo-400 --color-indigo-50 \" +\n \"--color-indigo-500 --color-indigo-600 --color-indigo-700 --color-indigo-800 --color-indigo-900 \" +\n \"--color-indigo-950 --color-lime-100 --color-lime-200 --color-lime-300 --color-lime-400 \" +\n \"--color-lime-50 --color-lime-500 --color-lime-600 --color-lime-700 --color-lime-800 \" +\n \"--color-lime-900 --color-lime-950 --color-mauve-100 --color-mauve-200 --color-mauve-300 \" +\n \"--color-mauve-400 --color-mauve-50 --color-mauve-500 --color-mauve-600 --color-mauve-700 \" +\n \"--color-mauve-800 --color-mauve-900 --color-mauve-950 --color-mist-100 --color-mist-200 \" +\n \"--color-mist-300 --color-mist-400 --color-mist-50 --color-mist-500 --color-mist-600 \" +\n \"--color-mist-700 --color-mist-800 --color-mist-900 --color-mist-950 --color-neutral-100 \" +\n \"--color-neutral-200 --color-neutral-300 --color-neutral-400 --color-neutral-50 \" +\n \"--color-neutral-500 --color-neutral-600 --color-neutral-700 --color-neutral-800 \" +\n \"--color-neutral-900 --color-neutral-950 --color-olive-100 --color-olive-200 --color-olive-300 \" +\n \"--color-olive-400 --color-olive-50 --color-olive-500 --color-olive-600 --color-olive-700 \" +\n \"--color-olive-800 --color-olive-900 --color-olive-950 --color-orange-100 --color-orange-200 \" +\n \"--color-orange-300 --color-orange-400 --color-orange-50 --color-orange-500 --color-orange-600 \" +\n \"--color-orange-700 --color-orange-800 --color-orange-900 --color-orange-950 --color-pink-100 \" +\n \"--color-pink-200 --color-pink-300 --color-pink-400 --color-pink-50 --color-pink-500 \" +\n \"--color-pink-600 --color-pink-700 --color-pink-800 --color-pink-900 --color-pink-950 \" +\n \"--color-purple-100 --color-purple-200 --color-purple-300 --color-purple-400 --color-purple-50 \" +\n \"--color-purple-500 --color-purple-600 --color-purple-700 --color-purple-800 --color-purple-900 \" +\n \"--color-purple-950 --color-red-100 --color-red-200 --color-red-300 --color-red-400 \" +\n \"--color-red-50 --color-red-500 --color-red-600 --color-red-700 --color-red-800 --color-red-900 \" +\n \"--color-red-950 --color-rose-100 --color-rose-200 --color-rose-300 --color-rose-400 \" +\n \"--color-rose-50 --color-rose-500 --color-rose-600 --color-rose-700 --color-rose-800 \" +\n \"--color-rose-900 --color-rose-950 --color-sky-100 --color-sky-200 --color-sky-300 \" +\n \"--color-sky-400 --color-sky-50 --color-sky-500 --color-sky-600 --color-sky-700 --color-sky-800 \" +\n \"--color-sky-900 --color-sky-950 --color-slate-100 --color-slate-200 --color-slate-300 \" +\n \"--color-slate-400 --color-slate-50 --color-slate-500 --color-slate-600 --color-slate-700 \" +\n \"--color-slate-800 --color-slate-900 --color-slate-950 --color-stone-100 --color-stone-200 \" +\n \"--color-stone-300 --color-stone-400 --color-stone-50 --color-stone-500 --color-stone-600 \" +\n \"--color-stone-700 --color-stone-800 --color-stone-900 --color-stone-950 --color-taupe-100 \" +\n \"--color-taupe-200 --color-taupe-300 --color-taupe-400 --color-taupe-50 --color-taupe-500 \" +\n \"--color-taupe-600 --color-taupe-700 --color-taupe-800 --color-taupe-900 --color-taupe-950 \" +\n \"--color-teal-100 --color-teal-200 --color-teal-300 --color-teal-400 --color-teal-50 \" +\n \"--color-teal-500 --color-teal-600 --color-teal-700 --color-teal-800 --color-teal-900 \" +\n \"--color-teal-950 --color-violet-100 --color-violet-200 --color-violet-300 --color-violet-400 \" +\n \"--color-violet-50 --color-violet-500 --color-violet-600 --color-violet-700 --color-violet-800 \" +\n \"--color-violet-900 --color-violet-950 --color-white --color-yellow-100 --color-yellow-200 \" +\n \"--color-yellow-300 --color-yellow-400 --color-yellow-50 --color-yellow-500 --color-yellow-600 \" +\n \"--color-yellow-700 --color-yellow-800 --color-yellow-900 --color-yellow-950 --color-zinc-100 \" +\n \"--color-zinc-200 --color-zinc-300 --color-zinc-400 --color-zinc-50 --color-zinc-500 \" +\n \"--color-zinc-600 --color-zinc-700 --color-zinc-800 --color-zinc-900 --color-zinc-950 \" +\n \"--container-2xl --container-2xs --container-3xl --container-3xs --container-4xl --container-5xl \" +\n \"--container-6xl --container-7xl --container-lg --container-md --container-sm --container-xl \" +\n \"--container-xs --default-font-family --default-font-feature-settings \" +\n \"--default-font-variation-settings --default-mono-font-family \" +\n \"--default-mono-font-feature-settings --default-mono-font-variation-settings \" +\n \"--default-transition-duration --default-transition-timing-function --drop-shadow-2xl \" +\n \"--drop-shadow-lg --drop-shadow-md --drop-shadow-sm --drop-shadow-xl --drop-shadow-xs --ease-in \" +\n \"--ease-in-out --ease-out --font-mono --font-sans --font-serif --font-weight-black \" +\n \"--font-weight-bold --font-weight-extrabold --font-weight-extralight --font-weight-light \" +\n \"--font-weight-medium --font-weight-normal --font-weight-semibold --font-weight-thin \" +\n \"--inset-shadow-2xs --inset-shadow-sm --inset-shadow-xs --leading-loose --leading-normal \" +\n \"--leading-relaxed --leading-snug --leading-tight --perspective-distant --perspective-dramatic \" +\n \"--perspective-midrange --perspective-near --perspective-normal --radius-2xl --radius-3xl \" +\n \"--radius-4xl --radius-lg --radius-md --radius-sm --radius-xl --radius-xs --shadow-2xl \" +\n \"--shadow-2xs --shadow-lg --shadow-md --shadow-sm --shadow-xl --shadow-xs --spacing --text-2xl \" +\n \"--text-2xl--line-height --text-3xl --text-3xl--line-height --text-4xl --text-4xl--line-height \" +\n \"--text-5xl --text-5xl--line-height --text-6xl --text-6xl--line-height --text-7xl \" +\n \"--text-7xl--line-height --text-8xl --text-8xl--line-height --text-9xl --text-9xl--line-height \" +\n \"--text-base --text-base--line-height --text-lg --text-lg--line-height --text-shadow-2xs \" +\n \"--text-shadow-lg --text-shadow-md --text-shadow-sm --text-shadow-xs --text-sm \" +\n \"--text-sm--line-height --text-xl --text-xl--line-height --text-xs --text-xs--line-height \" +\n \"--tracking-normal --tracking-tight --tracking-tighter --tracking-wide --tracking-wider \" +\n \"--tracking-widest\";\n"],"mappings":";AAMO,MAAM,YACX;AAoBK,MAAM,uBACX;","names":[]}
@@ -1,23 +1,88 @@
1
+ /** How many categorical slots `paletteFor` cycles through: anyplot's eight hues. Amber,
2
+ * anyplot's ninth colour, is a semantic warning anchor and not a series slot. A preset
3
+ * that carries more hues (Paul Tol's nine) still writes them as `--chart-N`, but the
4
+ * by-index ramp wraps after eight, so the same series keeps the same colour whichever
5
+ * preset is active. */
6
+ export declare const PALETTE_SIZE = 8;
7
+ /** anyplot's colour-vision-deficiency-optimal order over the eight slots — the order
8
+ * that keeps the FIRST n series furthest apart under protan/deutan/tritan simulation.
9
+ * An option (`paletteFor(i, { order: "cvd" })`), not the default: the default order is
10
+ * anyplot's own slot order, so series 1 is always the green. */
11
+ export declare const CVD_ORDER: readonly [0, 1, 7, 6, 4, 5, 2, 3];
12
+ export interface PaletteForOptions {
13
+ /** `"slot"` (default): anyplot's slot order. `"cvd"`: {@link CVD_ORDER}. */
14
+ order?: "slot" | "cvd";
15
+ }
1
16
  /** CVD-safe categorical colour for series `index`, as a theme-aware CSS var.
17
+ *
18
+ * The first series is always slot 0 (`--chart-1`, anyplot's green), and the ramp
19
+ * wraps after {@link PALETTE_SIZE}.
2
20
  *
3
21
  * Wraps in both directions, the way `strokeDash` does: JS `%` keeps the sign,
4
- * so `-1 % 9` is `-1` and the plain formula named `--chart-0`, a token that does not
22
+ * so `-1 % 8` is `-1` and the plain formula named `--chart-0`, a token that does not
5
23
  * exist — a series with no stroke at all. A fractional index is truncated, and one
6
24
  * that is not a number (NaN, ±Infinity — an `indexOf` miss divided by something)
7
25
  * takes the first colour rather than `var(--chart-NaN)`. */
8
- export declare function paletteFor(index: number): string;
26
+ export declare function paletteFor(index: number, options?: PaletteForOptions): string;
9
27
  /** Semantic money colours, theme-aware via tokens. Pair with +/- and arrow
10
28
  cues at the call site so colour is never the only signal (CVD). */
11
29
  export declare const CHART_COLORS: {
12
30
  readonly income: "var(--money-income)";
13
31
  readonly expense: "var(--money-expense)";
14
32
  readonly net: "var(--money-net)";
33
+ readonly neutral: "var(--money-neutral)";
15
34
  readonly assigned: "var(--brand)";
16
35
  readonly activity: "var(--money-expense)";
36
+ /** A generic total line — not money-coloured, the chart's neutral ink. */
37
+ readonly total: "var(--chart-ink)";
38
+ };
39
+ /**
40
+ * What a chart draws that is NOT a series (anyplot's roles, 0.49). Two inks, which are
41
+ * the only chart colours that change between light and dark — the eight hues do not:
42
+ *
43
+ * - `other` / `rest` / `disabled` and confidence-`band` fills → the MUTED ink. A
44
+ * treemap's "rest" bucket or a pie's "Other" slice takes this instead of wrapping
45
+ * round to a hue the reader will match to a real series.
46
+ * - `total` / `baseline` / `outline` → the neutral INK.
47
+ */
48
+ export declare const CHART_ROLES: {
49
+ readonly other: "var(--chart-muted)";
50
+ readonly rest: "var(--chart-muted)";
51
+ readonly disabled: "var(--chart-muted)";
52
+ readonly band: "var(--chart-muted)";
53
+ readonly total: "var(--chart-ink)";
54
+ readonly baseline: "var(--chart-ink)";
55
+ readonly outline: "var(--chart-ink)";
17
56
  };
57
+ /** `--chart-ink` per theme, as hex (tokens.css holds the same two). */
58
+ export declare const CHART_INK_HEX: {
59
+ readonly light: "#1a1a17";
60
+ readonly dark: "#f0efe8";
61
+ };
62
+ /** `--chart-muted` per theme, as hex (tokens.css holds the same two). */
63
+ export declare const CHART_MUTED_HEX: {
64
+ readonly light: "#6b6a63";
65
+ readonly dark: "#a8a79f";
66
+ };
67
+ /** The 3:1 WCAG 1.4.11 asks of a filled graphical object against its surface. */
68
+ export declare const EDGE_MIN_CONTRAST = 3;
69
+ /**
70
+ * The 1px edge a FILLED mark (a bar, an area, a treemap tile) needs, or `undefined`.
71
+ *
72
+ * anyplot's answer to hues that sit under 3:1 on the ground (cyan, lime, lavender, ochre
73
+ * and green on cream; matte red and rose on the dark ground): outline the shape in ink,
74
+ * so its boundary is legible while the fill keeps the hue that identifies the series.
75
+ * Only those — a hue that clears 3:1 on every surface given stays unstroked, because an
76
+ * outline on every bar is noise.
77
+ *
78
+ * `fill` must be a measurable `#rgb`/`#rrggbb`; `surfaces` are the grounds the mark can
79
+ * sit on (the card and the page), and the WORST of them decides. Anything it cannot
80
+ * measure gets no edge rather than a guessed one.
81
+ */
82
+ export declare function chartEdge(fill: string, surfaces: readonly string[], ink?: string): string | undefined;
18
83
  export declare const PALETTE_HEX: {
19
- readonly light: readonly ["#332288", "#88ccee", "#44aa99", "#117733", "#999933", "#ddcc77", "#cc6677", "#882255", "#aa4499"];
20
- readonly dark: readonly ["#7e72d6", "#9fd8f2", "#5fc4b0", "#3fa45f", "#bfbf5e", "#e8dda0", "#e08c9a", "#c25a78", "#cc78be"];
84
+ readonly light: readonly ["#009e73", "#c475fd", "#4467a3", "#bd8233", "#ae3030", "#2abccd", "#954477", "#99b314"];
85
+ readonly dark: readonly ["#009e73", "#c475fd", "#4467a3", "#bd8233", "#ae3030", "#2abccd", "#954477", "#99b314"];
21
86
  };
22
87
  /** A heatmap's interpolation stops for one theme. Open string fields so any
23
88
  palette preset (not just the built-in default) can supply its own stops. */
@@ -30,22 +95,24 @@ export interface HeatStops {
30
95
  empty: string;
31
96
  }
32
97
  /** Diverging (variance: under←0→over) and sequential (spending intensity)
33
- heatmap stops, per theme. Teal = under/good, amber = over/spend = bad. */
98
+ heatmap stops, per theme — anyplot's scales (0.49). Sequential runs anyplot green →
99
+ blue; diverging runs blue (under) ↔ paper/ink (0, the midpoint) ↔ matte red (over).
100
+ The hues are the same in both themes; only the midpoint flips with the ground. */
34
101
  export declare const HEATMAP_HEX: {
35
102
  readonly light: {
36
- readonly neutral: "#f1e7d2";
37
- readonly under: "#0b6650";
38
- readonly over: "#9c6418";
39
- readonly seqLow: "#f6ecd0";
40
- readonly seqHigh: "#9c6418";
103
+ readonly neutral: "#faf8f1";
104
+ readonly under: "#4467a3";
105
+ readonly over: "#ae3030";
106
+ readonly seqLow: "#009e73";
107
+ readonly seqHigh: "#4467a3";
41
108
  readonly empty: "rgba(148,163,184,0.14)";
42
109
  };
43
110
  readonly dark: {
44
- readonly neutral: "#363a5a";
45
- readonly under: "#22c3b6";
46
- readonly over: "#fbbf24";
47
- readonly seqLow: "#3a2c14";
48
- readonly seqHigh: "#fbbf24";
111
+ readonly neutral: "#1a1a17";
112
+ readonly under: "#4467a3";
113
+ readonly over: "#ae3030";
114
+ readonly seqLow: "#009e73";
115
+ readonly seqHigh: "#4467a3";
49
116
  readonly empty: "rgba(148,163,184,0.10)";
50
117
  };
51
118
  };
@@ -1,44 +1,64 @@
1
1
  "use client";
2
2
  import { contrast, parseHex as parseRgbHex } from "./color.js";
3
- const PALETTE_SIZE = 9;
4
- function paletteFor(index) {
3
+ const PALETTE_SIZE = 8;
4
+ const CVD_ORDER = [0, 1, 7, 6, 4, 5, 2, 3];
5
+ function paletteFor(index, options) {
5
6
  const i = Number.isFinite(index) ? Math.trunc(index) : 0;
6
- return `var(--chart-${(i % PALETTE_SIZE + PALETTE_SIZE) % PALETTE_SIZE + 1})`;
7
+ const position = (i % PALETTE_SIZE + PALETTE_SIZE) % PALETTE_SIZE;
8
+ const slot = options?.order === "cvd" ? CVD_ORDER[position] : position;
9
+ return `var(--chart-${slot + 1})`;
7
10
  }
8
11
  const CHART_COLORS = {
9
12
  income: "var(--money-income)",
10
13
  expense: "var(--money-expense)",
11
14
  net: "var(--money-net)",
15
+ neutral: "var(--money-neutral)",
12
16
  assigned: "var(--brand)",
13
- activity: "var(--money-expense)"
17
+ activity: "var(--money-expense)",
18
+ /** A generic total line — not money-coloured, the chart's neutral ink. */
19
+ total: "var(--chart-ink)"
14
20
  };
21
+ const CHART_ROLES = {
22
+ other: "var(--chart-muted)",
23
+ rest: "var(--chart-muted)",
24
+ disabled: "var(--chart-muted)",
25
+ band: "var(--chart-muted)",
26
+ total: "var(--chart-ink)",
27
+ baseline: "var(--chart-ink)",
28
+ outline: "var(--chart-ink)"
29
+ };
30
+ const CHART_INK_HEX = { light: "#1a1a17", dark: "#f0efe8" };
31
+ const CHART_MUTED_HEX = { light: "#6b6a63", dark: "#a8a79f" };
32
+ const EDGE_MIN_CONTRAST = 3;
33
+ function chartEdge(fill, surfaces, ink = CHART_ROLES.outline) {
34
+ if (!parseRgbHex(fill)) return void 0;
35
+ const measurable = surfaces.filter((s) => parseRgbHex(s));
36
+ if (measurable.length === 0) return void 0;
37
+ const worst = Math.min(...measurable.map((s) => contrast(fill, s)));
38
+ return worst < EDGE_MIN_CONTRAST ? ink : void 0;
39
+ }
40
+ const IMPRINT_HEX = ["#009e73", "#c475fd", "#4467a3", "#bd8233", "#ae3030", "#2abccd", "#954477", "#99b314"];
15
41
  const PALETTE_HEX = {
16
- light: ["#332288", "#88ccee", "#44aa99", "#117733", "#999933", "#ddcc77", "#cc6677", "#882255", "#aa4499"],
17
- dark: ["#7e72d6", "#9fd8f2", "#5fc4b0", "#3fa45f", "#bfbf5e", "#e8dda0", "#e08c9a", "#c25a78", "#cc78be"]
42
+ light: IMPRINT_HEX,
43
+ dark: IMPRINT_HEX
18
44
  };
19
45
  const HEATMAP_HEX = {
20
46
  light: {
21
- neutral: "#f1e7d2",
22
- // the cream surface itself, not a cool grey
23
- under: "#0b6650",
24
- // DEFAULT_PRESET.light.moneyIncome
25
- over: "#9c6418",
26
- // DEFAULT_PRESET.light.moneyExpense, warmed to the page
27
- seqLow: "#f6ecd0",
28
- seqHigh: "#9c6418",
47
+ neutral: "#faf8f1",
48
+ // anyplot's light paper — the diverging scale's 0
49
+ under: "#4467a3",
50
+ over: "#ae3030",
51
+ seqLow: "#009e73",
52
+ seqHigh: "#4467a3",
29
53
  empty: "rgba(148,163,184,0.14)"
30
54
  },
31
55
  dark: {
32
- neutral: "#363a5a",
33
- // one step above --border on the indigo-noir surface
34
- under: "#22c3b6",
35
- // DEFAULT_PRESET.dark.moneyIncome
36
- // Brighter than dark.moneyExpense (#e4b035) on purpose: a gradient endpoint is
37
- // meant to be the loudest thing in its scale, and it is a fill, not a figure.
38
- over: "#fbbf24",
39
- seqLow: "#3a2c14",
40
- // dim amber, sits just above the page
41
- seqHigh: "#fbbf24",
56
+ neutral: "#1a1a17",
57
+ // anyplot's ink — the diverging scale's 0 on the dark ground
58
+ under: "#4467a3",
59
+ over: "#ae3030",
60
+ seqLow: "#009e73",
61
+ seqHigh: "#4467a3",
42
62
  empty: "rgba(148,163,184,0.10)"
43
63
  }
44
64
  };
@@ -65,8 +85,15 @@ function clamp01(t) {
65
85
  }
66
86
  export {
67
87
  CHART_COLORS,
88
+ CHART_INK_HEX,
89
+ CHART_MUTED_HEX,
90
+ CHART_ROLES,
91
+ CVD_ORDER,
92
+ EDGE_MIN_CONTRAST,
68
93
  HEATMAP_HEX,
69
94
  PALETTE_HEX,
95
+ PALETTE_SIZE,
96
+ chartEdge,
70
97
  lerpHex,
71
98
  paletteFor,
72
99
  textOn
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/theme/chart-palette.ts"],"sourcesContent":["// Chart colour system. Single source of truth lives in the design tokens\n// (:root / .dark in tokens.css): brand = indigo, money = teal/amber/purple,\n// chart-1..9 = Paul Tol \"Muted\" (CVD-safe across protan/deutan/tritan).\n// Returning CSS vars here makes every Recharts series theme-aware for free — the\n// SVG fill/stroke resolves --chart-N against the current light/dark token set.\n\nimport { contrast, parseHex as parseRgbHex } from \"./color\";\n\nconst PALETTE_SIZE = 9;\n\n/** CVD-safe categorical colour for series `index`, as a theme-aware CSS var.\n *\n * Wraps in both directions, the way `strokeDash` does: JS `%` keeps the sign,\n * so `-1 % 9` is `-1` and the plain formula named `--chart-0`, a token that does not\n * exist — a series with no stroke at all. A fractional index is truncated, and one\n * that is not a number (NaN, ±Infinity — an `indexOf` miss divided by something)\n * takes the first colour rather than `var(--chart-NaN)`. */\nexport function paletteFor(index: number): string {\n const i = Number.isFinite(index) ? Math.trunc(index) : 0;\n return `var(--chart-${(((i % PALETTE_SIZE) + PALETTE_SIZE) % PALETTE_SIZE) + 1})`;\n}\n\n/** Semantic money colours, theme-aware via tokens. Pair with +/- and arrow\n cues at the call site so colour is never the only signal (CVD). */\nexport const CHART_COLORS = {\n income: \"var(--money-income)\",\n expense: \"var(--money-expense)\",\n net: \"var(--money-net)\",\n assigned: \"var(--brand)\",\n activity: \"var(--money-expense)\",\n} as const;\n\n// ── Raw hex stops, by theme ────────────────────────────────────────────────\n// The heatmaps interpolate colours in JS (CSS vars can't be lerped), so they\n// need concrete hex per theme — i.e. the same colours DEFAULT_PRESET already\n// holds, written down a second time.\n//\n// They are duplicated rather than imported because these two objects are the whole\n// of the `@eifi1/ui-kit/chart` subpath's colour layer, and importing the preset bank\n// would pull every preset's full token set into that chunk to read two of them. What\n// keeps the copies together is `chart-palette.test.ts` — \"has not drifted from\n// DEFAULT_PRESET\" — which is not optional: they HAD drifted, five of the six light\n// heat stops, and the light `neutral` was still Tailwind's slate-100, a cool grey on\n// the warm cream page this theme has used since #328.\nexport const PALETTE_HEX = {\n light: [\"#332288\", \"#88ccee\", \"#44aa99\", \"#117733\", \"#999933\", \"#ddcc77\", \"#cc6677\", \"#882255\", \"#aa4499\"],\n dark: [\"#7e72d6\", \"#9fd8f2\", \"#5fc4b0\", \"#3fa45f\", \"#bfbf5e\", \"#e8dda0\", \"#e08c9a\", \"#c25a78\", \"#cc78be\"],\n} as const;\n\n/** A heatmap's interpolation stops for one theme. Open string fields so any\n palette preset (not just the built-in default) can supply its own stops. */\nexport interface HeatStops {\n neutral: string;\n under: string;\n over: string;\n seqLow: string;\n seqHigh: string;\n empty: string;\n}\n\n/** Diverging (variance: under←0→over) and sequential (spending intensity)\n heatmap stops, per theme. Teal = under/good, amber = over/spend = bad. */\nexport const HEATMAP_HEX = {\n light: {\n neutral: \"#f1e7d2\", // the cream surface itself, not a cool grey\n under: \"#0b6650\", // DEFAULT_PRESET.light.moneyIncome\n over: \"#9c6418\", // DEFAULT_PRESET.light.moneyExpense, warmed to the page\n seqLow: \"#f6ecd0\",\n seqHigh: \"#9c6418\",\n empty: \"rgba(148,163,184,0.14)\",\n },\n dark: {\n neutral: \"#363a5a\", // one step above --border on the indigo-noir surface\n under: \"#22c3b6\", // DEFAULT_PRESET.dark.moneyIncome\n // Brighter than dark.moneyExpense (#e4b035) on purpose: a gradient endpoint is\n // meant to be the loudest thing in its scale, and it is a fill, not a figure.\n over: \"#fbbf24\",\n seqLow: \"#3a2c14\", // dim amber, sits just above the page\n seqHigh: \"#fbbf24\",\n empty: \"rgba(148,163,184,0.10)\",\n },\n} as const;\n\n/** Linear interpolate two #rrggbb hexes; `t` in [0,1]. Returns #rrggbb. */\nexport function lerpHex(a: string, b: string, t: number): string {\n const pa = parseHex(a) ?? UNREADABLE;\n const pb = parseHex(b) ?? UNREADABLE;\n const c = (i: number) => Math.round(pa[i] + (pb[i] - pa[i]) * clamp01(t));\n return `#${[c(0), c(1), c(2)].map((v) => v.toString(16).padStart(2, \"0\")).join(\"\")}`;\n}\n\n/** The two inks a cell/treemap label can be set in. Near-black and near-white rather\n than #000/#fff: at full black-on-full-white the label stops looking like part of the\n same type system as the rest of the page. */\nconst INK_DARK = \"#1a1a1a\";\nconst INK_LIGHT = \"#f8fafc\";\n\n/** Pick a legible text colour (near-black / near-white) for a solid hex fill, by\n MEASURING both against it and keeping the one that wins.\n\n This used to flip on Rec.601 luma > 0.6, which is a brightness heuristic and not a\n contrast measure — the two disagree on six of the eighteen default chart hues, a\n third of the ramp, and on each of those the label got the worse of the two inks.\n `#44aa99` is the plainest case: luma calls it dark, so it took white, which measures\n 2.69:1 on it; near-black measures 6.18:1. keksdose measured this too and forked the\n function locally for its treemap, leaving its other two call sites on this one.\n\n A fill this cannot read gets `currentColor` — inherit, i.e. whatever the\n surrounding text already uses. That is the only answer that is right on both\n themes, because an unreadable fill is usually a translucent one (`HEATMAP_HEX.*\n .empty` is an `rgba()` string, and the variance heatmap does pass it here for any\n category with zero assigned and positive activity — a refund). Picking a constant\n instead is what produced the earlier bug: the unparsed fill fell through to the\n light ink, near-white on a near-white cell, and the figure was invisible. */\nexport function textOn(hex: string): string {\n const rgb = parseRgbHex(hex);\n if (!rgb) return \"currentColor\";\n // Ties go to the dark ink: at equal measured contrast it is the one that keeps a\n // small label looking like type rather than like a glow.\n return contrast(rgb, INK_DARK) >= contrast(rgb, INK_LIGHT) ? INK_DARK : INK_LIGHT;\n}\n\n/** Mid-grey, for a stop {@link lerpHex} has to interpolate but cannot read. It has\n * to return SOME valid colour, and a grey is the one that misleads least. */\nconst UNREADABLE: [number, number, number] = [0x80, 0x80, 0x80];\n\n/**\n * `#rgb` or `#rrggbb` to components, or **null** when it is neither.\n *\n * A tuple-shaped shim over `color.ts`'s parser, which is the same function and is\n * already the one `contrast` validates with — so {@link lerpHex} and {@link textOn}\n * cannot disagree about what counts as a colour.\n *\n * The **null** is the whole point of it. This used to `parseInt` whatever it was given\n * and hand the NaNs on, and both callers propagated them silently: {@link lerpHex}\n * produced the literal string `\"#nannannan\"`, and {@link textOn} fell through to the\n * LIGHT label colour.\n *\n * That is not hypothetical — a non-hex value ships in this very module.\n * `HEATMAP_HEX.*.empty` is an `rgba(…)` string, and the variance heatmap paints a\n * figure on an `empty` cell whenever a category has zero assigned and positive\n * activity (a refund). On the light theme the cell is near-white and the label came\n * back near-white: the number was there, and unreadable.\n */\nfunction parseHex(hex: string): [number, number, number] | null {\n const rgb = parseRgbHex(hex);\n return rgb ? [rgb.r, rgb.g, rgb.b] : null;\n}\n\nfunction clamp01(t: number): number {\n return t < 0 ? 0 : t > 1 ? 1 : t;\n}\n"],"mappings":";AAMA,SAAS,UAAU,YAAY,mBAAmB;AAElD,MAAM,eAAe;AASd,SAAS,WAAW,OAAuB;AAChD,QAAM,IAAI,OAAO,SAAS,KAAK,IAAI,KAAK,MAAM,KAAK,IAAI;AACvD,SAAO,gBAAkB,IAAI,eAAgB,gBAAgB,eAAgB,CAAC;AAChF;AAIO,MAAM,eAAe;AAAA,EAC1B,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,KAAK;AAAA,EACL,UAAU;AAAA,EACV,UAAU;AACZ;AAcO,MAAM,cAAc;AAAA,EACzB,OAAO,CAAC,WAAW,WAAW,WAAW,WAAW,WAAW,WAAW,WAAW,WAAW,SAAS;AAAA,EACzG,MAAM,CAAC,WAAW,WAAW,WAAW,WAAW,WAAW,WAAW,WAAW,WAAW,SAAS;AAC1G;AAeO,MAAM,cAAc;AAAA,EACzB,OAAO;AAAA,IACL,SAAS;AAAA;AAAA,IACT,OAAO;AAAA;AAAA,IACP,MAAM;AAAA;AAAA,IACN,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,OAAO;AAAA,EACT;AAAA,EACA,MAAM;AAAA,IACJ,SAAS;AAAA;AAAA,IACT,OAAO;AAAA;AAAA;AAAA;AAAA,IAGP,MAAM;AAAA,IACN,QAAQ;AAAA;AAAA,IACR,SAAS;AAAA,IACT,OAAO;AAAA,EACT;AACF;AAGO,SAAS,QAAQ,GAAW,GAAW,GAAmB;AAC/D,QAAM,KAAK,SAAS,CAAC,KAAK;AAC1B,QAAM,KAAK,SAAS,CAAC,KAAK;AAC1B,QAAM,IAAI,CAAC,MAAc,KAAK,MAAM,GAAG,CAAC,KAAK,GAAG,CAAC,IAAI,GAAG,CAAC,KAAK,QAAQ,CAAC,CAAC;AACxE,SAAO,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,EAAE,KAAK,EAAE,CAAC;AACpF;AAKA,MAAM,WAAW;AACjB,MAAM,YAAY;AAmBX,SAAS,OAAO,KAAqB;AAC1C,QAAM,MAAM,YAAY,GAAG;AAC3B,MAAI,CAAC,IAAK,QAAO;AAGjB,SAAO,SAAS,KAAK,QAAQ,KAAK,SAAS,KAAK,SAAS,IAAI,WAAW;AAC1E;AAIA,MAAM,aAAuC,CAAC,KAAM,KAAM,GAAI;AAoB9D,SAAS,SAAS,KAA8C;AAC9D,QAAM,MAAM,YAAY,GAAG;AAC3B,SAAO,MAAM,CAAC,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC,IAAI;AACvC;AAEA,SAAS,QAAQ,GAAmB;AAClC,SAAO,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI;AACjC;","names":[]}
1
+ {"version":3,"sources":["../../src/theme/chart-palette.ts"],"sourcesContent":["// Chart colour system. Single source of truth lives in the design tokens\n// (:root / .dark in tokens.css): brand = indigo, money = teal/amber/purple,\n// chart-1..8 = anyplot's \"Imprint\" hues (https://anyplot.ai/palette, 0.49 — Paul Tol\n// \"Muted\" before, still selectable as the `paul-tol` preset).\n// Returning CSS vars here makes every Recharts series theme-aware for free — the\n// SVG fill/stroke resolves --chart-N against the current light/dark token set.\n\nimport { contrast, parseHex as parseRgbHex } from \"./color\";\n\n/** How many categorical slots `paletteFor` cycles through: anyplot's eight hues. Amber,\n * anyplot's ninth colour, is a semantic warning anchor and not a series slot. A preset\n * that carries more hues (Paul Tol's nine) still writes them as `--chart-N`, but the\n * by-index ramp wraps after eight, so the same series keeps the same colour whichever\n * preset is active. */\nexport const PALETTE_SIZE = 8;\n\n/** anyplot's colour-vision-deficiency-optimal order over the eight slots — the order\n * that keeps the FIRST n series furthest apart under protan/deutan/tritan simulation.\n * An option (`paletteFor(i, { order: \"cvd\" })`), not the default: the default order is\n * anyplot's own slot order, so series 1 is always the green. */\nexport const CVD_ORDER = [0, 1, 7, 6, 4, 5, 2, 3] as const;\n\nexport interface PaletteForOptions {\n /** `\"slot\"` (default): anyplot's slot order. `\"cvd\"`: {@link CVD_ORDER}. */\n order?: \"slot\" | \"cvd\";\n}\n\n/** CVD-safe categorical colour for series `index`, as a theme-aware CSS var.\n *\n * The first series is always slot 0 (`--chart-1`, anyplot's green), and the ramp\n * wraps after {@link PALETTE_SIZE}.\n *\n * Wraps in both directions, the way `strokeDash` does: JS `%` keeps the sign,\n * so `-1 % 8` is `-1` and the plain formula named `--chart-0`, a token that does not\n * exist — a series with no stroke at all. A fractional index is truncated, and one\n * that is not a number (NaN, ±Infinity — an `indexOf` miss divided by something)\n * takes the first colour rather than `var(--chart-NaN)`. */\nexport function paletteFor(index: number, options?: PaletteForOptions): string {\n const i = Number.isFinite(index) ? Math.trunc(index) : 0;\n const position = ((i % PALETTE_SIZE) + PALETTE_SIZE) % PALETTE_SIZE;\n const slot = options?.order === \"cvd\" ? CVD_ORDER[position] : position;\n return `var(--chart-${slot + 1})`;\n}\n\n/** Semantic money colours, theme-aware via tokens. Pair with +/- and arrow\n cues at the call site so colour is never the only signal (CVD). */\nexport const CHART_COLORS = {\n income: \"var(--money-income)\",\n expense: \"var(--money-expense)\",\n net: \"var(--money-net)\",\n neutral: \"var(--money-neutral)\",\n assigned: \"var(--brand)\",\n activity: \"var(--money-expense)\",\n /** A generic total line — not money-coloured, the chart's neutral ink. */\n total: \"var(--chart-ink)\",\n} as const;\n\n/**\n * What a chart draws that is NOT a series (anyplot's roles, 0.49). Two inks, which are\n * the only chart colours that change between light and dark — the eight hues do not:\n *\n * - `other` / `rest` / `disabled` and confidence-`band` fills → the MUTED ink. A\n * treemap's \"rest\" bucket or a pie's \"Other\" slice takes this instead of wrapping\n * round to a hue the reader will match to a real series.\n * - `total` / `baseline` / `outline` → the neutral INK.\n */\nexport const CHART_ROLES = {\n other: \"var(--chart-muted)\",\n rest: \"var(--chart-muted)\",\n disabled: \"var(--chart-muted)\",\n band: \"var(--chart-muted)\",\n total: \"var(--chart-ink)\",\n baseline: \"var(--chart-ink)\",\n outline: \"var(--chart-ink)\",\n} as const;\n\n/** `--chart-ink` per theme, as hex (tokens.css holds the same two). */\nexport const CHART_INK_HEX = { light: \"#1a1a17\", dark: \"#f0efe8\" } as const;\n/** `--chart-muted` per theme, as hex (tokens.css holds the same two). */\nexport const CHART_MUTED_HEX = { light: \"#6b6a63\", dark: \"#a8a79f\" } as const;\n\n/** The 3:1 WCAG 1.4.11 asks of a filled graphical object against its surface. */\nexport const EDGE_MIN_CONTRAST = 3;\n\n/**\n * The 1px edge a FILLED mark (a bar, an area, a treemap tile) needs, or `undefined`.\n *\n * anyplot's answer to hues that sit under 3:1 on the ground (cyan, lime, lavender, ochre\n * and green on cream; matte red and rose on the dark ground): outline the shape in ink,\n * so its boundary is legible while the fill keeps the hue that identifies the series.\n * Only those — a hue that clears 3:1 on every surface given stays unstroked, because an\n * outline on every bar is noise.\n *\n * `fill` must be a measurable `#rgb`/`#rrggbb`; `surfaces` are the grounds the mark can\n * sit on (the card and the page), and the WORST of them decides. Anything it cannot\n * measure gets no edge rather than a guessed one.\n */\nexport function chartEdge(\n fill: string,\n surfaces: readonly string[],\n ink: string = CHART_ROLES.outline,\n): string | undefined {\n if (!parseRgbHex(fill)) return undefined;\n const measurable = surfaces.filter((s) => parseRgbHex(s));\n if (measurable.length === 0) return undefined;\n const worst = Math.min(...measurable.map((s) => contrast(fill, s)));\n return worst < EDGE_MIN_CONTRAST ? ink : undefined;\n}\n\n// ── Raw hex stops, by theme ────────────────────────────────────────────────\n// The heatmaps interpolate colours in JS (CSS vars can't be lerped), so they\n// need concrete hex per theme — i.e. the same colours DEFAULT_PRESET already\n// holds, written down a second time.\n//\n// They are duplicated rather than imported because these two objects are the whole\n// of the `@eifi1/ui-kit/chart` subpath's colour layer, and importing the preset bank\n// would pull every preset's full token set into that chunk to read two of them. What\n// keeps the copies together is `chart-palette.test.ts` — \"has not drifted from\n// DEFAULT_PRESET\" — which is not optional: they HAD drifted, five of the six light\n// heat stops, and the light `neutral` was still Tailwind's slate-100, a cool grey on\n// the warm cream page this theme has used since #328.\n//\n// 0.49: the same eight hues in both themes — anyplot does not re-tune a hue for the dark\n// ground, and neither does the kit any more; `chartEdge` outlines the two that fall\n// under 3:1 there (matte red, rose) instead of lifting them into different colours.\nconst IMPRINT_HEX = [\"#009e73\", \"#c475fd\", \"#4467a3\", \"#bd8233\", \"#ae3030\", \"#2abccd\", \"#954477\", \"#99b314\"] as const;\nexport const PALETTE_HEX = {\n light: IMPRINT_HEX,\n dark: IMPRINT_HEX,\n} as const;\n\n/** A heatmap's interpolation stops for one theme. Open string fields so any\n palette preset (not just the built-in default) can supply its own stops. */\nexport interface HeatStops {\n neutral: string;\n under: string;\n over: string;\n seqLow: string;\n seqHigh: string;\n empty: string;\n}\n\n/** Diverging (variance: under←0→over) and sequential (spending intensity)\n heatmap stops, per theme — anyplot's scales (0.49). Sequential runs anyplot green →\n blue; diverging runs blue (under) ↔ paper/ink (0, the midpoint) ↔ matte red (over).\n The hues are the same in both themes; only the midpoint flips with the ground. */\nexport const HEATMAP_HEX = {\n light: {\n neutral: \"#faf8f1\", // anyplot's light paper — the diverging scale's 0\n under: \"#4467a3\",\n over: \"#ae3030\",\n seqLow: \"#009e73\",\n seqHigh: \"#4467a3\",\n empty: \"rgba(148,163,184,0.14)\",\n },\n dark: {\n neutral: \"#1a1a17\", // anyplot's ink — the diverging scale's 0 on the dark ground\n under: \"#4467a3\",\n over: \"#ae3030\",\n seqLow: \"#009e73\",\n seqHigh: \"#4467a3\",\n empty: \"rgba(148,163,184,0.10)\",\n },\n} as const;\n\n/** Linear interpolate two #rrggbb hexes; `t` in [0,1]. Returns #rrggbb. */\nexport function lerpHex(a: string, b: string, t: number): string {\n const pa = parseHex(a) ?? UNREADABLE;\n const pb = parseHex(b) ?? UNREADABLE;\n const c = (i: number) => Math.round(pa[i] + (pb[i] - pa[i]) * clamp01(t));\n return `#${[c(0), c(1), c(2)].map((v) => v.toString(16).padStart(2, \"0\")).join(\"\")}`;\n}\n\n/** The two inks a cell/treemap label can be set in. Near-black and near-white rather\n than #000/#fff: at full black-on-full-white the label stops looking like part of the\n same type system as the rest of the page. */\nconst INK_DARK = \"#1a1a1a\";\nconst INK_LIGHT = \"#f8fafc\";\n\n/** Pick a legible text colour (near-black / near-white) for a solid hex fill, by\n MEASURING both against it and keeping the one that wins.\n\n This used to flip on Rec.601 luma > 0.6, which is a brightness heuristic and not a\n contrast measure — the two disagree on six of the eighteen default chart hues, a\n third of the ramp, and on each of those the label got the worse of the two inks.\n `#44aa99` is the plainest case: luma calls it dark, so it took white, which measures\n 2.69:1 on it; near-black measures 6.18:1. keksdose measured this too and forked the\n function locally for its treemap, leaving its other two call sites on this one.\n\n A fill this cannot read gets `currentColor` — inherit, i.e. whatever the\n surrounding text already uses. That is the only answer that is right on both\n themes, because an unreadable fill is usually a translucent one (`HEATMAP_HEX.*\n .empty` is an `rgba()` string, and the variance heatmap does pass it here for any\n category with zero assigned and positive activity — a refund). Picking a constant\n instead is what produced the earlier bug: the unparsed fill fell through to the\n light ink, near-white on a near-white cell, and the figure was invisible. */\nexport function textOn(hex: string): string {\n const rgb = parseRgbHex(hex);\n if (!rgb) return \"currentColor\";\n // Ties go to the dark ink: at equal measured contrast it is the one that keeps a\n // small label looking like type rather than like a glow.\n return contrast(rgb, INK_DARK) >= contrast(rgb, INK_LIGHT) ? INK_DARK : INK_LIGHT;\n}\n\n/** Mid-grey, for a stop {@link lerpHex} has to interpolate but cannot read. It has\n * to return SOME valid colour, and a grey is the one that misleads least. */\nconst UNREADABLE: [number, number, number] = [0x80, 0x80, 0x80];\n\n/**\n * `#rgb` or `#rrggbb` to components, or **null** when it is neither.\n *\n * A tuple-shaped shim over `color.ts`'s parser, which is the same function and is\n * already the one `contrast` validates with — so {@link lerpHex} and {@link textOn}\n * cannot disagree about what counts as a colour.\n *\n * The **null** is the whole point of it. This used to `parseInt` whatever it was given\n * and hand the NaNs on, and both callers propagated them silently: {@link lerpHex}\n * produced the literal string `\"#nannannan\"`, and {@link textOn} fell through to the\n * LIGHT label colour.\n *\n * That is not hypothetical — a non-hex value ships in this very module.\n * `HEATMAP_HEX.*.empty` is an `rgba(…)` string, and the variance heatmap paints a\n * figure on an `empty` cell whenever a category has zero assigned and positive\n * activity (a refund). On the light theme the cell is near-white and the label came\n * back near-white: the number was there, and unreadable.\n */\nfunction parseHex(hex: string): [number, number, number] | null {\n const rgb = parseRgbHex(hex);\n return rgb ? [rgb.r, rgb.g, rgb.b] : null;\n}\n\nfunction clamp01(t: number): number {\n return t < 0 ? 0 : t > 1 ? 1 : t;\n}\n"],"mappings":"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","names":[]}
@@ -25,6 +25,15 @@ export interface PalettePreset {
25
25
  dark: TokenSet;
26
26
  }
27
27
  export declare const DEFAULT_PRESET: PalettePreset;
28
+ /**
29
+ * The 0.48 default: the same surfaces, brand and money, with Paul Tol's "Muted" nine
30
+ * as the chart ramp and the teal/amber heat stops that went with it. Selectable, no
31
+ * longer the default (0.49). `paletteFor` cycles eight, so its ninth hue (#aa4499) is
32
+ * reachable only as `var(--chart-9)`.
33
+ */
34
+ export declare const PAUL_TOL_PRESET: PalettePreset;
35
+ /** @deprecated 0.49 — use `DEFAULT_PRESET` (Imprint IS the default now); removed in 0.51.
36
+ * A persisted `"imprint"` id resolves to it through `presetById`'s fallback. */
28
37
  export declare const IMPRINT_PRESET: PalettePreset;
29
38
  /**
30
39
  * Presets DERIVED rather than picked, by `derivePalette` from one brand colour each.