zabi-components 8.1.0 → 9.0.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 (273) hide show
  1. package/CHANGELOG.md +1011 -0
  2. package/README.md +1 -1
  3. package/THEME.md +18 -5
  4. package/THEMING.md +308 -74
  5. package/dist/atoms/ActionPanel.svelte +1 -1
  6. package/dist/atoms/Avatar.svelte +19 -3
  7. package/dist/atoms/Avatar.svelte.d.ts +12 -0
  8. package/dist/atoms/Badge.svelte +37 -8
  9. package/dist/atoms/Badge.svelte.d.ts +6 -0
  10. package/dist/atoms/Block.svelte +74 -0
  11. package/dist/atoms/Block.svelte.d.ts +29 -0
  12. package/dist/atoms/Button.svelte +52 -15
  13. package/dist/atoms/Button.svelte.d.ts +3 -2
  14. package/dist/atoms/Card.svelte +181 -25
  15. package/dist/atoms/Card.svelte.d.ts +55 -3
  16. package/dist/atoms/Checkbox.svelte +33 -0
  17. package/dist/atoms/Checkbox.svelte.d.ts +27 -0
  18. package/dist/atoms/Chip.svelte +351 -0
  19. package/dist/atoms/Chip.svelte.d.ts +57 -0
  20. package/dist/atoms/CodeField.svelte +385 -0
  21. package/dist/atoms/CodeField.svelte.d.ts +61 -0
  22. package/dist/atoms/ColorPicker.svelte +1 -0
  23. package/dist/atoms/DateField.svelte +41 -3
  24. package/dist/atoms/DateField.svelte.d.ts +31 -3
  25. package/dist/atoms/FloatingActionButton.svelte +5 -3
  26. package/dist/atoms/FloatingActionButton.svelte.d.ts +1 -1
  27. package/dist/atoms/Heading.svelte +27 -1
  28. package/dist/atoms/Heading.svelte.d.ts +7 -0
  29. package/dist/atoms/IconButton.svelte +81 -9
  30. package/dist/atoms/IconButton.svelte.d.ts +13 -0
  31. package/dist/atoms/Input.svelte +10 -5
  32. package/dist/atoms/Input.svelte.d.ts +3 -2
  33. package/dist/atoms/List.svelte +3 -2
  34. package/dist/atoms/ListItem.svelte +31 -7
  35. package/dist/atoms/ListItem.svelte.d.ts +6 -0
  36. package/dist/atoms/PickerField.svelte +239 -0
  37. package/dist/atoms/PickerField.svelte.d.ts +67 -0
  38. package/dist/atoms/Progress.svelte +78 -17
  39. package/dist/atoms/Progress.svelte.d.ts +11 -1
  40. package/dist/atoms/Radio.svelte +32 -0
  41. package/dist/atoms/Radio.svelte.d.ts +26 -0
  42. package/dist/atoms/Rating.svelte +132 -6
  43. package/dist/atoms/Rating.svelte.d.ts +24 -3
  44. package/dist/atoms/Select.svelte +24 -26
  45. package/dist/atoms/Select.svelte.d.ts +12 -0
  46. package/dist/atoms/SelectionControl.svelte +99 -6
  47. package/dist/atoms/SelectionControl.svelte.d.ts +25 -0
  48. package/dist/atoms/Skeleton.svelte +114 -18
  49. package/dist/atoms/Skeleton.svelte.d.ts +30 -3
  50. package/dist/atoms/SkeletonGroup.svelte +43 -0
  51. package/dist/atoms/SkeletonGroup.svelte.d.ts +20 -0
  52. package/dist/atoms/Slider.svelte +35 -0
  53. package/dist/atoms/Sparkline.svelte +109 -0
  54. package/dist/atoms/Sparkline.svelte.d.ts +27 -0
  55. package/dist/atoms/SpeechBubble.svelte +187 -0
  56. package/dist/atoms/SpeechBubble.svelte.d.ts +33 -0
  57. package/dist/atoms/Stat.svelte +72 -0
  58. package/dist/atoms/Stat.svelte.d.ts +19 -0
  59. package/dist/atoms/TemporalField.svelte +342 -10
  60. package/dist/atoms/TemporalField.svelte.d.ts +20 -4
  61. package/dist/atoms/Textarea.svelte +100 -5
  62. package/dist/atoms/Textarea.svelte.d.ts +21 -0
  63. package/dist/atoms/TimeColumn.svelte +150 -0
  64. package/dist/atoms/TimeColumn.svelte.d.ts +25 -0
  65. package/dist/atoms/TimeField.svelte +46 -3
  66. package/dist/atoms/TimeField.svelte.d.ts +34 -3
  67. package/dist/atoms/Toast.svelte +6 -5
  68. package/dist/atoms/Toggle.svelte +61 -10
  69. package/dist/atoms/Toggle.svelte.d.ts +15 -0
  70. package/dist/atoms/Tooltip.svelte +20 -1
  71. package/dist/atoms/Tooltip.svelte.d.ts +7 -0
  72. package/dist/atoms/index.d.ts +21 -0
  73. package/dist/atoms/index.js +17 -0
  74. package/dist/atoms/selection-control.styles.d.ts +15 -4
  75. package/dist/atoms/selection-control.styles.js +16 -5
  76. package/dist/components/atoms/index.d.ts +21 -0
  77. package/dist/components/atoms/index.d.ts.map +1 -1
  78. package/dist/components/atoms/selection-control.styles.d.ts +15 -4
  79. package/dist/components/atoms/selection-control.styles.d.ts.map +1 -1
  80. package/dist/components/index.d.ts +27 -3
  81. package/dist/components/index.d.ts.map +1 -1
  82. package/dist/components/map.d.ts +11 -0
  83. package/dist/components/map.d.ts.map +1 -0
  84. package/dist/components/molecules/index.d.ts +7 -2
  85. package/dist/components/molecules/index.d.ts.map +1 -1
  86. package/dist/components/molecules/toast-store.d.ts +30 -0
  87. package/dist/components/molecules/toast-store.d.ts.map +1 -1
  88. package/dist/components/organisms/index.d.ts +6 -1
  89. package/dist/components/organisms/index.d.ts.map +1 -1
  90. package/dist/components/types/variants.d.ts +12 -3
  91. package/dist/components/types/variants.d.ts.map +1 -1
  92. package/dist/components/util/app-shell.d.ts +55 -4
  93. package/dist/components/util/app-shell.d.ts.map +1 -1
  94. package/dist/components/util/avatar.d.ts +3 -0
  95. package/dist/components/util/avatar.d.ts.map +1 -1
  96. package/dist/components/util/bottom-tab-bar.d.ts +6 -0
  97. package/dist/components/util/bottom-tab-bar.d.ts.map +1 -1
  98. package/dist/components/util/chart.d.ts +140 -0
  99. package/dist/components/util/chart.d.ts.map +1 -0
  100. package/dist/components/util/chip.d.ts +66 -0
  101. package/dist/components/util/chip.d.ts.map +1 -0
  102. package/dist/components/util/cn.d.ts.map +1 -1
  103. package/dist/components/util/code-field.d.ts +63 -0
  104. package/dist/components/util/code-field.d.ts.map +1 -0
  105. package/dist/components/util/collapsible.d.ts +6 -0
  106. package/dist/components/util/collapsible.d.ts.map +1 -1
  107. package/dist/components/util/display-voice.d.ts +10 -0
  108. package/dist/components/util/display-voice.d.ts.map +1 -0
  109. package/dist/components/util/field-control.d.ts +30 -0
  110. package/dist/components/util/field-control.d.ts.map +1 -0
  111. package/dist/components/util/field.d.ts +6 -0
  112. package/dist/components/util/field.d.ts.map +1 -1
  113. package/dist/components/util/focus-utils.d.ts +2 -0
  114. package/dist/components/util/focus-utils.d.ts.map +1 -1
  115. package/dist/components/util/map-view.d.ts +144 -0
  116. package/dist/components/util/map-view.d.ts.map +1 -0
  117. package/dist/components/util/photo.d.ts +19 -0
  118. package/dist/components/util/photo.d.ts.map +1 -1
  119. package/dist/components/util/progress.d.ts +19 -0
  120. package/dist/components/util/progress.d.ts.map +1 -0
  121. package/dist/components/util/scroll-edge.d.ts +33 -0
  122. package/dist/components/util/scroll-edge.d.ts.map +1 -0
  123. package/dist/components/util/sheet-drag.d.ts +25 -1
  124. package/dist/components/util/sheet-drag.d.ts.map +1 -1
  125. package/dist/components/util/skeleton.d.ts +15 -0
  126. package/dist/components/util/skeleton.d.ts.map +1 -0
  127. package/dist/components/util/sliding-indicator.d.ts +25 -0
  128. package/dist/components/util/sliding-indicator.d.ts.map +1 -0
  129. package/dist/components/util/temporal-field.d.ts +89 -0
  130. package/dist/components/util/temporal-field.d.ts.map +1 -0
  131. package/dist/components/util/toaster.d.ts +40 -0
  132. package/dist/components/util/toaster.d.ts.map +1 -1
  133. package/dist/components/util/tone-surface.d.ts +34 -0
  134. package/dist/components/util/tone-surface.d.ts.map +1 -0
  135. package/dist/components/util/top-navbar.d.ts +26 -2
  136. package/dist/components/util/top-navbar.d.ts.map +1 -1
  137. package/dist/components/util/zabi-strings.d.ts +9 -0
  138. package/dist/components/util/zabi-strings.d.ts.map +1 -1
  139. package/dist/create-theme/cli.js +6 -2
  140. package/dist/create-theme/index.d.ts +17 -3
  141. package/dist/create-theme/index.js +252 -35
  142. package/dist/create-theme/lib/resolve.js +97 -0
  143. package/dist/create-theme/theme-data.js +1618 -90
  144. package/dist/index.d.ts +27 -3
  145. package/dist/index.js +18 -0
  146. package/dist/map.d.ts +10 -0
  147. package/dist/map.js +9 -0
  148. package/dist/molecules/Alert.svelte +31 -12
  149. package/dist/molecules/Alert.svelte.d.ts +12 -1
  150. package/dist/molecules/AppBar.svelte +287 -28
  151. package/dist/molecules/AppBar.svelte.d.ts +62 -0
  152. package/dist/molecules/AvatarGroup.svelte +1 -1
  153. package/dist/molecules/BarChart.svelte +381 -0
  154. package/dist/molecules/BarChart.svelte.d.ts +45 -0
  155. package/dist/molecules/BottomSheet.svelte +88 -20
  156. package/dist/molecules/BottomSheet.svelte.d.ts +17 -1
  157. package/dist/molecules/BottomTabBar.svelte +134 -28
  158. package/dist/molecules/BottomTabBar.svelte.d.ts +36 -2
  159. package/dist/molecules/Calendar.svelte.d.ts +1 -1
  160. package/dist/molecules/ChipGroup.svelte +325 -0
  161. package/dist/molecules/ChipGroup.svelte.d.ts +41 -0
  162. package/dist/molecules/Collapsible.svelte +158 -54
  163. package/dist/molecules/Collapsible.svelte.d.ts +23 -6
  164. package/dist/molecules/CollapsibleGroup.svelte +16 -6
  165. package/dist/molecules/ConfirmDialog.svelte +5 -1
  166. package/dist/molecules/ConfirmDialog.svelte.d.ts +3 -0
  167. package/dist/molecules/Drawer.svelte +33 -12
  168. package/dist/molecules/Dropdown.svelte +9 -1
  169. package/dist/molecules/Dropdown.svelte.d.ts +2 -0
  170. package/dist/molecules/DropdownItem.svelte +1 -1
  171. package/dist/molecules/ImageUpload.svelte +230 -120
  172. package/dist/molecules/ImageUpload.svelte.d.ts +38 -3
  173. package/dist/molecules/LineChart.svelte +459 -0
  174. package/dist/molecules/LineChart.svelte.d.ts +53 -0
  175. package/dist/molecules/MapView.svelte +565 -0
  176. package/dist/molecules/MapView.svelte.d.ts +88 -0
  177. package/dist/molecules/Modal.svelte +11 -6
  178. package/dist/molecules/PhotoGrid.svelte +100 -10
  179. package/dist/molecules/PhotoGrid.svelte.d.ts +11 -0
  180. package/dist/molecules/PhotoViewer.svelte +121 -24
  181. package/dist/molecules/PhotoViewer.svelte.d.ts +18 -0
  182. package/dist/molecules/PieChart.svelte +421 -0
  183. package/dist/molecules/PieChart.svelte.d.ts +51 -0
  184. package/dist/molecules/RadioGroup.svelte +20 -4
  185. package/dist/molecules/RadioGroup.svelte.d.ts +5 -0
  186. package/dist/molecules/SegmentedControl.svelte +136 -3
  187. package/dist/molecules/SegmentedControl.svelte.d.ts +6 -0
  188. package/dist/molecules/SidebarFooter.svelte +1 -1
  189. package/dist/molecules/SidebarNavSection.svelte +1 -1
  190. package/dist/molecules/SortableList.svelte +1 -0
  191. package/dist/molecules/Stepper.svelte +21 -7
  192. package/dist/molecules/StickyActionBar.svelte +67 -8
  193. package/dist/molecules/StickyActionBar.svelte.d.ts +14 -1
  194. package/dist/molecules/Tabs.svelte +79 -2
  195. package/dist/molecules/Toaster.svelte +171 -7
  196. package/dist/molecules/Toaster.svelte.d.ts +60 -2
  197. package/dist/molecules/ToasterToast.svelte +292 -60
  198. package/dist/molecules/ToasterToast.svelte.d.ts +6 -0
  199. package/dist/molecules/UnsavedChangesBar.svelte +13 -2
  200. package/dist/molecules/index.d.ts +7 -2
  201. package/dist/molecules/index.js +4 -0
  202. package/dist/molecules/toast-store.d.ts +30 -0
  203. package/dist/molecules/toast-store.js +4 -1
  204. package/dist/organisms/AppNavigation.svelte +440 -0
  205. package/dist/organisms/AppNavigation.svelte.d.ts +72 -0
  206. package/dist/organisms/AppShell.svelte +539 -39
  207. package/dist/organisms/AppShell.svelte.d.ts +102 -6
  208. package/dist/organisms/AppShellHeaderPart.svelte +163 -0
  209. package/dist/organisms/AppShellHeaderPart.svelte.d.ts +57 -0
  210. package/dist/organisms/SidebarNavigation.svelte +23 -36
  211. package/dist/organisms/SidebarPanel.svelte +10 -14
  212. package/dist/organisms/SidebarPanel.svelte.d.ts +1 -0
  213. package/dist/organisms/SidebarShell.svelte +2 -3
  214. package/dist/organisms/TopNavbar.svelte +117 -28
  215. package/dist/organisms/TopNavbar.svelte.d.ts +22 -1
  216. package/dist/organisms/TopNavbarSheetContent.svelte +145 -0
  217. package/dist/organisms/TopNavbarSheetContent.svelte.d.ts +24 -0
  218. package/dist/organisms/index.d.ts +6 -1
  219. package/dist/organisms/index.js +4 -0
  220. package/dist/types/components.d.ts +17 -1
  221. package/dist/types/components.d.ts.map +1 -1
  222. package/dist/types/index.d.ts +497 -10
  223. package/dist/types/index.d.ts.map +1 -1
  224. package/dist/types/variants.ts +14 -3
  225. package/dist/util/app-shell.d.ts +55 -4
  226. package/dist/util/app-shell.js +32 -7
  227. package/dist/util/avatar.d.ts +3 -0
  228. package/dist/util/bottom-tab-bar.d.ts +6 -0
  229. package/dist/util/chart.d.ts +139 -0
  230. package/dist/util/chart.js +305 -0
  231. package/dist/util/chip.d.ts +65 -0
  232. package/dist/util/chip.js +58 -0
  233. package/dist/util/cn.js +28 -2
  234. package/dist/util/code-field.d.ts +62 -0
  235. package/dist/util/code-field.js +112 -0
  236. package/dist/util/collapsible.d.ts +6 -0
  237. package/dist/util/display-voice.d.ts +9 -0
  238. package/dist/util/display-voice.js +9 -0
  239. package/dist/util/field-control.d.ts +29 -0
  240. package/dist/util/field-control.js +32 -0
  241. package/dist/util/field.d.ts +6 -0
  242. package/dist/util/focus-utils.d.ts +2 -0
  243. package/dist/util/focus-utils.js +4 -0
  244. package/dist/util/map-view.d.ts +143 -0
  245. package/dist/util/map-view.js +121 -0
  246. package/dist/util/photo.d.ts +19 -0
  247. package/dist/util/photo.js +3 -0
  248. package/dist/util/progress.d.ts +18 -0
  249. package/dist/util/progress.js +17 -0
  250. package/dist/util/scroll-edge.d.ts +32 -0
  251. package/dist/util/scroll-edge.js +87 -0
  252. package/dist/util/sheet-drag.d.ts +25 -1
  253. package/dist/util/sheet-drag.js +71 -1
  254. package/dist/util/skeleton.d.ts +14 -0
  255. package/dist/util/skeleton.js +22 -0
  256. package/dist/util/sliding-indicator.d.ts +24 -0
  257. package/dist/util/sliding-indicator.js +44 -0
  258. package/dist/util/temporal-field.d.ts +88 -0
  259. package/dist/util/temporal-field.js +136 -0
  260. package/dist/util/toaster.d.ts +40 -0
  261. package/dist/util/toaster.js +55 -0
  262. package/dist/util/tone-surface.d.ts +33 -0
  263. package/dist/util/tone-surface.js +35 -0
  264. package/dist/util/top-navbar.d.ts +26 -2
  265. package/dist/util/top-navbar.js +23 -0
  266. package/dist/util/zabi-strings.d.ts +9 -0
  267. package/dist/zabi-components-colors.css +9 -3
  268. package/dist/zabi-components-theme-dark-only.css +474 -164
  269. package/dist/zabi-components-theme-dark.css +474 -164
  270. package/dist/zabi-components-theme-only.css +933 -99
  271. package/dist/zabi-components-theme.css +933 -99
  272. package/dist/zabi-components.css +2275 -321
  273. package/package.json +11 -1
@@ -124,49 +124,70 @@
124
124
  --color-base-950: var(--zabi-base-50);
125
125
  /* Background Colors - No dark mode overrides needed, base colors are already inverted */
126
126
  /* Page Colors - No dark mode overrides needed, base colors are already inverted */
127
- /* Surface elevation — shadows don't read on dark, so each level steps up +6 OKLCH lightness
128
- * from the page background (L 21 → 27 → 33 → 39).
127
+ /* Surface elevation — shadows don't read on dark, so the surfaces carry it by tone, in two
128
+ * steps: page L 21.0, card L 24.1, elevated and overlay L 27.4 (D133/D134).
129
129
  *
130
130
  * GENERATED by scripts/generate-surfaces.js from tokens/surface-ladder.js — do not hand-edit.
131
- * The page is --zabi-base-900. Each level above it is --zabi-base-50 (the same light as the
132
- * dark hover tint) washed over the page at 6.4% / 13.1% / 19.7%, so every level is the one
133
- * below it with more light on it, and a --zabi-base-* override carries the whole ladder.
134
- * Mixing two opaque colours stays opaque, so a modal never lets the page through. With the
135
- * default greys these resolve to #18181b / #262629 / #363638 / #454547.
136
- * Enforced by scripts/check-surface-elevation.js (strictly increasing, 5–8 points). */
131
+ * Every role is a plain step of the neutral ramp (--zabi-base-N), so hue and chroma are the
132
+ * ramp's own, and a --zabi-base-* override or a tinted neutral carries all of them. An overlay
133
+ * (sheet, menu, modal) is the same tone as elevated: it is told apart by its edge
134
+ * (--color-material-rim) and its shadow, not by being greyer. The hover on an overlay is one
135
+ * ramp step lighter; the inset is the page step, told apart by its 3:1 edge.
136
+ * With the default greys: #18181b / #1f1f22 / #27272a / #27272a, hover #333338, inset #18181b.
137
+ * Enforced by scripts/check-surface-elevation.js (ramp steps, never lighter downwards, at most
138
+ * two tone steps of 2–5 points, the overlay edge and the card edge). */
137
139
  --color-surface-base: var(--zabi-base-900);
138
- --color-surface-raised: color-mix(in srgb, var(--zabi-base-50) 6.4%, var(--zabi-base-900));
139
- --color-surface-elevated: color-mix(in srgb, var(--zabi-base-50) 13.1%, var(--zabi-base-900));
140
- --color-surface-overlay: color-mix(in srgb, var(--zabi-base-50) 19.7%, var(--zabi-base-900));
141
- /* base-400 mirrors to --zabi-base-600 (#52525b), one step lighter than the overlay. */
142
- --color-surface-overlay-hover: var(--color-base-400);
143
- /* Inset — the step between the page and the raised surface (L 24). The input
144
- * field is the library's own inset; see --color-input below. */
145
- --color-surface-inset: var(--color-base-150);
140
+ --color-surface-raised: var(--zabi-base-850);
141
+ --color-surface-elevated: var(--zabi-base-800);
142
+ --color-surface-overlay: var(--zabi-base-800);
143
+ --color-surface-overlay-hover: var(--zabi-base-750);
144
+ /* Inset — the page step, 3 points below the card (L 24.1). A field on the page is told apart
145
+ * by its 3:1 edge, as a white field on the near-white light page is. */
146
+ --color-surface-inset: var(--zabi-base-900);
147
+ /* Chrome and the bar tokens — restated so a light-only override of the page
148
+ * or the raised surface does not reach dark. */
149
+ --color-surface-chrome: var(--color-surface-base);
150
+ --color-bar: var(--color-surface-chrome);
151
+ --color-bar-border: color-mix(in srgb, var(--color-headline) 10%, transparent);
152
+ --color-bar-brand: var(--color-action-primary);
153
+ --color-tabbar-active: var(--color-nav-menu-active);
146
154
  /* Distinct from base, raised AND elevated — a neutral chip has no hue to
147
- * separate it, so it has to separate on lightness wherever it lands. */
155
+ * separate it, so it has to separate on lightness wherever it lands:
156
+ * base-300 is 1.70:1 on the page, 1.57:1 on the card, 1.43:1 on elevated. */
148
157
  --color-neutral-subtle: var(--color-base-300);
149
158
  --color-neutral-border: var(--color-base-200);
150
159
  --color-border-overlay: var(--color-base-450);
151
160
  /* Background Colors */
152
161
  --color-background: var(--color-surface-base);
153
162
  --color-background-tertiary: var(--color-base-100);
154
- /* Page Colors — pinned: light moved the page to base-150, dark stays on base-100 */
155
- --color-page: var(--color-base-100);
163
+ /* Page Colors — the dark page (base-100 here is --zabi-base-900, the same value as before) */
164
+ --color-page: var(--color-surface-base);
156
165
  /* Card Colors — aliases of the surface levels */
157
166
  --color-card: var(--color-surface-raised);
158
167
  --color-card-hover: var(--color-surface-elevated);
159
- --color-card-active: var(--color-surface-overlay);
168
+ --color-card-active: var(--color-surface-overlay-hover);
160
169
  --color-card-elevated: var(--color-surface-elevated);
170
+ /* Card tint, dark: the brand at 8% over the card (as the status tints of D136,
171
+ * which are 16%: here the field edge sets the ceiling, base-500 is 3.07:1 on
172
+ * this and 2.90:1 at 12%). 8% is #252733: 1.11:1 on the card and 1.20:1 on the
173
+ * page; headline 13.49, body 11.68, label and description 10.03, caption 7.68,
174
+ * link 8.81, ring 6.59:1. */
175
+ --color-card-tint: color-mix(in srgb, var(--zabi-brand-500) 8%, var(--color-surface-raised));
176
+ /* Card variant="flat", dark: a translucent step of the neutral ramp (base-50 at 7%), so a flat card
177
+ * is lighter than whatever it sits on, and a flat card inside a flat card is lighter again. Shadows
178
+ * do not carry layers on a dark page; alpha does. Hover and pressed are the same step at 11% and 15%.
179
+ * Restore the opaque fill: overrides: { dark: { '--color-card-flat': 'var(--color-card)' } }. */
180
+ --color-card-flat: color-mix(in srgb, var(--zabi-base-50) 7%, transparent);
181
+ --color-card-flat-hover: color-mix(in srgb, var(--zabi-base-50) 11%, transparent);
182
+ --color-card-flat-active: color-mix(in srgb, var(--zabi-base-50) 15%, transparent);
161
183
  /* Surfaces — numbered aliases of the surface levels */
162
184
  --color-surface-1: var(--color-surface-raised);
163
185
  --color-surface-2: var(--color-surface-elevated);
164
186
  --color-surface-3: var(--color-surface-overlay);
165
- --color-surface-4: var(--color-base-400);
187
+ --color-surface-4: var(--color-surface-overlay-hover);
166
188
  /*
167
- * Secondary text keeps ≥ 4.5:1 on every surface level, including overlays (L39).
168
- * Only base-650 and lighter pass on the overlay, so caption takes base-650 and description
169
- * moves one step up to base-700 to stay visibly stronger than caption.
189
+ * Secondary text keeps ≥ 4.5:1 on every surface level, including overlays (L27).
190
+ * Caption takes base-650 and description base-700 to stay visibly stronger than it.
170
191
  */
171
192
  --color-description: var(--color-base-700);
172
193
  --color-caption: var(--color-base-650);
@@ -178,28 +199,87 @@
178
199
  /* Overlay Colors */
179
200
  --color-overlay: rgba(0, 0, 0, 0.7);
180
201
  /* Input Colors
181
- * Dark fields are INSET: one step below the raised surface they sit on,
182
- * which is the dark-mode equivalent of a white field on a grey page. */
183
- --color-input: var(--color-surface-inset);
202
+ * Dark fields are a translucent step of the neutral ramp (base-50 at 7%), the
203
+ * same idea as the flat card: a well one step LIGHTER than whatever it sits on,
204
+ * without a rule per surface. It was the page step (--color-surface-inset), so
205
+ * a dark form was told from the page by its 3:1 edge alone. 7% is 1.20:1 on
206
+ * the page (#28282b), 1.22:1 on a card (#2e2e31) and 1.23:1 on the overlay
207
+ * (#363639); hover 10% and pressed 15% (1.30:1 against the resting field on
208
+ * all three). The edge below stays 3:1 against the surface outside it.
209
+ * Restore the opaque field: overrides: { dark: {
210
+ * '--color-input': 'var(--color-surface-inset)', '--color-input-hover': 'var(--color-base-200)',
211
+ * '--color-input-focus': 'var(--color-base-150)',
212
+ * '--color-input-active': 'color-mix(in srgb, var(--zabi-base-50) 10%, var(--color-surface-inset))' } }. */
213
+ --color-input: color-mix(in srgb, var(--zabi-base-50) 7%, transparent);
214
+ /* What a field is inside a colour block (.on-brand, .on-accent, .on-fill): the translucent
215
+ * well would sit on the block's fill, which the theme's text is not set for, so the field
216
+ * takes these opaque steps there. */
217
+ --color-input-solid: var(--color-surface-inset);
218
+ --color-input-solid-hover: var(--color-base-200);
219
+ --color-input-solid-focus: var(--color-base-150);
220
+ --color-input-solid-active: color-mix(in srgb, var(--zabi-base-50) 10%, var(--color-surface-inset));
184
221
  /* Restated here: the root value of a var() alias is resolved at :root. */
185
222
  --color-progress-fill: var(--color-action-primary);
186
- --color-progress-track: var(--color-input);
187
- /* base-500 (the mirror's fixed point): 3.40:1 on the dark field, 3.67:1 on
188
- * the page and 3.12:1 on the card. base-300 was 1.57, 1.70 and 1.44. */
223
+ /* The field step is the page now, so the track takes its own: base-75
224
+ * (#101013), a well just below the page: 1.06:1 on the page, 1.14:1 on the
225
+ * card, 1.45:1 on the elevated surface. It was base-250 (#333338, 1.41:1 on
226
+ * the page). The fill is a mid step now (brand-600), and 3:1 on its rail
227
+ * needs a track this dark: 3.91:1 here with the default brand (2.59:1 on
228
+ * base-250, 2.94:1 for an amber brand on base-200). */
229
+ --color-progress-track: var(--color-base-75);
230
+ /* Restated: the root value of a var() alias is resolved at :root. The field
231
+ * edge, 3.67:1 on the page and 3.40:1 on the card. */
232
+ --color-progress-track-border: var(--color-input-border);
233
+ /* base-500 (the mirror's fixed point): 3.67:1 on the dark field and the page,
234
+ * 3.40:1 on the card, 3.08:1 on the elevated surface and the overlay.
235
+ * base-300 was 1.70, 1.57 and 1.43. */
189
236
  --color-input-border: var(--color-base-500);
190
237
  --color-input-border-hover: var(--color-input-border);
191
- --color-input-hover: var(--color-base-200);
192
- /* One step past hover, and one short of the border (base-300), so the
193
- * pressed field keeps its edge: 1.31:1 against the resting field. */
194
- --color-input-active: var(--color-base-250);
195
- --color-input-focus: var(--color-base-150);
196
- --color-input-disabled: var(--color-base-100);
238
+ /* The decorative hairline (Card's default variant, list dividers). Quiet, like
239
+ * light's: base-250 is #333338, 1.41:1 on the page and 1.31:1 on the card
240
+ * (base-300 was 1.70 and 1.57, base-200 only 1.10 on the card). Restated: the
241
+ * root value of a var() alias is resolved at :root. -medium (2.29:1) and
242
+ * -strong (3.67:1) keep their order above it. */
243
+ --color-border: var(--color-base-250);
244
+ --color-border-weak: var(--color-base-250);
245
+ /* Toggle track, off. The thumb is the card (#1f1f22), so the track cannot
246
+ * be as dark as it. base-300 (#3f3f46) is 1.57:1 against the thumb, and the
247
+ * field edge gives the 3:1: 3.67:1 on the page, 3.40:1 on the card. */
248
+ --color-toggle-track: var(--color-base-300);
249
+ --color-toggle-track-hover: var(--color-base-350);
250
+ --color-toggle-track-active: var(--color-base-400);
251
+ --color-toggle-track-border: var(--color-input-border);
252
+ /* Toggle knob in dark: off, a light knob on the dark track (base-700, #d4d4d8:
253
+ * 7.07:1 resting, 5.23:1 pressed); on, as in light (the same 4.86:1 and up). */
254
+ --color-toggle-knob: var(--color-base-700);
255
+ --color-toggle-knob-checked: var(--color-action-primary-text);
256
+ /* Hover 10%: 1.10:1 above the resting well on the page, the card and the overlay, body 8.65:1
257
+ * or more and placeholder 4.68:1 or more on it. Was base-200. */
258
+ --color-input-hover: color-mix(in srgb, var(--zabi-base-50) 10%, transparent);
259
+ /* Pressed 15% of the light end of the ramp (unmirrored, --zabi-base-50): 1.30:1 against the
260
+ * resting well on the page (#3a3a3c), the card (#404042) and the overlay (#474749), 1.18:1
261
+ * past hover, body 7.34:1 or more, placeholder 4.54:1 on a card and 4.08:1 on the overlay,
262
+ * short of the border (2.35:1 or less). 14% is 1.254:1 on the default and under 1.25 on a tinted ramp. Was 10% into the page step (#2f2f31). */
263
+ --color-input-active: color-mix(in srgb, var(--zabi-base-50) 15%, transparent);
264
+ /* Focus is the resting well: the ring shows focus, and hover still reads on a focused field.
265
+ * Was base-150. */
266
+ --color-input-focus: color-mix(in srgb, var(--zabi-base-50) 7%, transparent);
267
+ /* The field is the page step now, so the disabled field goes one step below
268
+ * the page (base-75, #101013): 1.07:1 on the page, 1.16:1 on the card. */
269
+ --color-input-disabled: var(--color-base-75);
197
270
  /* base-500 is the mirror's fixed point, the same grey as it was in light,
198
271
  * and a dark field is a far lighter ground for it than a white one is dark:
199
272
  * 3.40:1 at rest, 3.08:1 on hover, 2.60:1 pressed. base-600 mirrors to
200
273
  * --zabi-base-400 (#a1a1aa): 6.41, 5.81 and 4.90. base-550 would be 4.74
201
- * at rest but under 4.5 on hover. The value text is 2.0:1 stronger. */
202
- --color-input-placeholder: var(--color-base-600);
274
+ * at rest but under 4.5 on hover. The value text is 2.0:1 stronger.
275
+ * The well is lighter now (a translucent step over the surface), and the
276
+ * pressed one over a card held base-400 to 4.04:1: so 40% of the next step
277
+ * toward the value (base-350, #babac1) is mixed in, #ababb3: 6.46, 5.89 and
278
+ * 4.98:1 at rest, hover and pressed on the page, 5.91, 5.37 and 4.54:1 on a
279
+ * card, 5.30, 4.82 and 4.08:1 on the overlay (pressed there is held to 4:1,
280
+ * see contrast-pairs.js), and 1.80:1 under the value (1.75 is the floor; 50%
281
+ * would be 1.75). Was base-600 (#a1a1aa). */
282
+ --color-input-placeholder: color-mix(in srgb, var(--zabi-base-350) 40%, var(--zabi-base-400));
203
283
  /* Text Colors - No dark mode overrides needed, base colors are already inverted */
204
284
  /* Border Colors - the decorative edges need no override, base colors are
205
285
  * already inverted. The control boundary does: base-500 is the mirror's
@@ -208,6 +288,14 @@
208
288
  * mirrors to --zabi-base-400 (#a1a1aa): 3.73:1 on the overlay, the lightest
209
289
  * surface, and more on every level below it. base-550 is 2.76:1 there. */
210
290
  --color-control-border: var(--color-base-600);
291
+ /* Segmented control thumb — restated: the aliases resolve at :root, so without
292
+ * these dark would keep the light card colour. The track is a light tint over
293
+ * the page or a card (#2c2c2f / #333335), which the overlay tone now sits
294
+ * below, so the thumb takes base-400 (#52525b): 1.80:1 over the page track,
295
+ * 1.63:1 over the card track, headline text 7.03:1. */
296
+ --color-segment-thumb: var(--color-base-400);
297
+ --color-segment-thumb-text: var(--color-headline);
298
+ --color-segment-thumb-border: var(--color-control-border);
211
299
  /* Focus Colors — the same steps as light, through the mirror: brand-600 is
212
300
  * --zabi-brand-400 here, which is also the dark primary fill, so on a
213
301
  * primary button the ring is separated by its offset gap exactly as in light.
@@ -225,25 +313,28 @@
225
313
  --color-focus-ring-muted: var(--color-base-600);
226
314
  /* Primary Colors - No dark mode overrides needed, brand colors are already inverted */
227
315
  /* Secondary Colors - No dark mode overrides needed, base colors are already inverted */
228
- /* Tinted fills and edges — pinned at steps 100 and 200.
229
- * The solid, weak, medium, strong and text steps need no override: they
230
- * resolve through the mirrored ramps. The tints do, because light moved them
231
- * to steps 200 and 300 to read on a white card, and the mirror of those is
232
- * too heavy on a dark surface. */
233
- --color-success-subtle: var(--color-pine-100);
234
- --color-success-border: var(--color-pine-200);
235
- --color-warning-subtle: var(--color-warning-100);
236
- --color-warning-border: var(--color-warning-200);
237
- --color-error-subtle: var(--color-error-100);
238
- --color-error-border: var(--color-error-200);
239
- --color-energetic-subtle: var(--color-citron-100);
240
- --color-energetic-border: var(--color-citron-200);
241
- --color-accent-subtle: var(--color-accent-100);
242
- --color-accent-border: var(--color-accent-200);
316
+ /* Tinted fills and edges — the hue thinned into the card surface, not the
317
+ * mirrored step 100 / 200 (physical 900 / 800: deep, saturated, the loudest
318
+ * area on a dark screen). A tint here is a low-chroma tint of the card, and
319
+ * the hue is carried by the icon and the text. The mid step (500) is where
320
+ * the ramp's chroma peaks and the mirror's fixed point. The base is the card
321
+ * surface, so a badge or alert on a card is never darker than the card, and
322
+ * an app's override of the card surface carries through. The edge is the
323
+ * same mix, stronger. */
324
+ --color-success-subtle: color-mix(in srgb, var(--zabi-pine-500) 16%, var(--color-surface-raised));
325
+ --color-success-border: color-mix(in srgb, var(--zabi-pine-500) 36%, var(--color-surface-raised));
326
+ --color-warning-subtle: color-mix(in srgb, var(--zabi-warning-500) 16%, var(--color-surface-raised));
327
+ --color-warning-border: color-mix(in srgb, var(--zabi-warning-500) 36%, var(--color-surface-raised));
328
+ --color-error-subtle: color-mix(in srgb, var(--zabi-error-500) 16%, var(--color-surface-raised));
329
+ --color-error-border: color-mix(in srgb, var(--zabi-error-500) 36%, var(--color-surface-raised));
330
+ --color-energetic-subtle: color-mix(in srgb, var(--zabi-citron-500) 16%, var(--color-surface-raised));
331
+ --color-energetic-border: color-mix(in srgb, var(--zabi-citron-500) 36%, var(--color-surface-raised));
332
+ --color-accent-subtle: color-mix(in srgb, var(--zabi-accent-500) 16%, var(--color-surface-raised));
333
+ --color-accent-border: color-mix(in srgb, var(--zabi-accent-500) 36%, var(--color-surface-raised));
243
334
  /* Label on a solid accent fill: the dark end of the ramp on its light end. */
244
335
  --color-on-accent: var(--zabi-on-accent-dark);
245
- --color-info-subtle: var(--color-iris-100);
246
- --color-info-border: var(--color-iris-200);
336
+ --color-info-subtle: color-mix(in srgb, var(--zabi-iris-500) 16%, var(--color-surface-raised));
337
+ --color-info-border: color-mix(in srgb, var(--zabi-iris-500) 36%, var(--color-surface-raised));
247
338
  /* Neutral Colors — base-550 mirrors to L* 57, which is only 4.35:1 on a
248
339
  * raised surface. One step lighter clears AA on every dark surface level. */
249
340
  --color-neutral: var(--color-base-600);
@@ -251,48 +342,112 @@
251
342
  /* ========================================
252
343
  ACTION COLORS - Dark Mode Overrides
253
344
  ======================================== */
254
- /* Primary Actions - Background adapts automatically via brand color inversion */
255
- /* 🎯 TO CHANGE bg-action-primary IN DARK MODE: Edit the --zabi-brand-* physical ramp in @theme (semantic --color-brand-* follows the mirror) */
256
- /* "On brand" in dark: --zabi-on-brand-dark, by default the physical dark end
257
- * of the brand ramp on the physical light end used as the dark-mode primary
258
- * fill (7.21:1). --color-action-primary-text is restated, as it always
259
- * was here, so that an app's light-only override of it does not carry a
260
- * white label onto the light dark-mode fill. */
345
+ /* Primary Actions - the brand hue in dark too: the same physical steps as
346
+ * light (600 / 700 / 800), not the mirrored ones, which would be the pale
347
+ * 400 / 300 / 200. White on step 600 is 4.76 to 4.86:1 (every ramp is on one
348
+ * lightness curve); with the default brand the fill is 3.65:1 on the page,
349
+ * 3.39:1 on the card and 3.07:1 on the overlay (3:1 holds on all three, WCAG
350
+ * 1.4.11). Hover and pressed go darker, as in light.
351
+ * createTheme({ darkPrimary: 'mirror' }) writes the mirrored fills instead.
352
+ * 🎯 TO CHANGE bg-action-primary IN DARK MODE: override --color-action-primary under `dark`. */
353
+ --color-action-primary: var(--zabi-brand-600);
354
+ --color-action-primary-hover: var(--zabi-brand-700);
355
+ --color-action-primary-active: var(--zabi-brand-800);
356
+ /* "On brand" in dark: --zabi-on-brand-dark, which follows the light label (white). If an app
357
+ * moves the dark primary to a pale step, it sets the knob to a dark one.
358
+ * --color-action-primary-text is restated, as it always was here, so that an
359
+ * app's light-only override of it does not carry a different label onto the
360
+ * dark fill. */
261
361
  --color-on-brand: var(--zabi-on-brand-dark);
262
362
  --color-action-primary-text: var(--color-on-brand);
263
363
  /* Pinned like the other tints: light moved these a step darker. */
264
364
  --color-action-primary-subtle: var(--color-brand-100);
265
365
  --color-action-primary-subtle-hover: var(--color-brand-200);
366
+ --color-action-primary-border: var(--color-brand-200);
266
367
  /* Half a step past the dark hover fill, as in light. */
267
368
  --color-action-primary-subtle-active: color-mix(in srgb, var(--color-brand-300) 50%, var(--color-brand-200));
268
369
  /* Secondary Actions - light alpha tint instead of the light-mode dark tint */
269
370
  --color-action-secondary: rgba(250, 250, 250, 0.09);
270
371
  --color-action-secondary-hover: rgba(250, 250, 250, 0.15);
271
372
  --color-action-secondary-active: rgba(250, 250, 250, 0.21);
272
- /* Disabled Actions - base-200 in dark is #27272a, still the quietest fill.
373
+ /* Tonal Actions - the deep brand tint (brand-100 in dark is the deep end of
374
+ * the mirrored ramp) with a light brand label. Restated: the root values are
375
+ * var() aliases of roles that move in dark. Brand-800 is the light end of the
376
+ * mirrored ramp here: 9.48, 6.94 and 5.86:1 on the three fills. */
377
+ --color-action-tonal: var(--color-action-primary-subtle);
378
+ --color-action-tonal-hover: var(--color-action-primary-subtle-hover);
379
+ --color-action-tonal-active: color-mix(in srgb, var(--color-brand-300) 50%, var(--color-brand-200));
380
+ --color-action-tonal-text: var(--color-brand-800);
381
+ /* Chip, dark - its own steps, not the mirror of light. The ladder rises from
382
+ * the page (base-900, card base-850): rest base-750 is 1.41:1 on the page and
383
+ * 1.31:1 on the card, hover base-700 and pressed base-650 lighter again (1.70
384
+ * and 1.96 on the page). Headline is 11.43, 9.50 and 8.24:1 on the three.
385
+ * Restated, with the selected chip, because the root values are var() aliases. */
386
+ --color-chip: var(--zabi-base-750);
387
+ --color-chip-hover: var(--zabi-base-700);
388
+ --color-chip-active: var(--zabi-base-650);
389
+ --color-chip-text: var(--color-headline);
390
+ --color-chip-selected: var(--color-action-tonal);
391
+ --color-chip-selected-hover: var(--color-action-tonal-hover);
392
+ --color-chip-selected-active: var(--color-action-tonal-active);
393
+ --color-chip-selected-text: var(--color-action-tonal-text);
394
+ /* Outline Actions - the field edge, restated for the same reason. */
395
+ --color-action-outline-border: var(--color-input-border);
396
+ --color-action-outline-border-hover: var(--color-input-border-hover);
397
+ /* Disabled Actions - base-200 in dark is #27272a (the elevated tone), still
398
+ * the quietest fill, and the one the disabled label (base-500) reads on at 3.08:1.
273
399
  * Pinned: light moved a step darker to stay visible on its darker page. */
274
400
  --color-action-disabled: var(--color-base-200);
275
401
  --color-action-disabled-border: var(--color-base-250);
276
- /* Danger Actions — restated as aliases, not re-pointed.
277
- * These resolve through the mirrored --color-error-* scale, so dark danger
278
- * is the same step of the same ramp as light danger. They are repeated here
279
- * only because `zabi-components/theme-dark-only` has to stand alone as a
280
- * complete import (validate-theme.js enforces that). The raw hex this
281
- * replaced pinned dark danger brighter than light, which is why the
282
- * destructive action was the loudest element on a dark page. */
283
- --color-action-danger: var(--color-error-600);
284
- --color-action-danger-hover: var(--color-error-700);
285
- --color-action-danger-active: var(--color-error-800);
286
- --color-action-danger-subtle: var(--color-error-100);
287
- --color-action-danger-subtle-hover: var(--color-error-200);
288
- --color-action-danger-text: var(--color-error-50);
289
- /* Shadow Colors — black, not the light theme's ink tint */
402
+ /* Danger Actions — the same physical steps as light danger (600 / 700 / 800
403
+ * of --zabi-error-*) with the same label (step 50), so a destructive button
404
+ * is one red in both modes and shares the primary's veil. Physical, not
405
+ * through the mirror, which would be the pale 400 / 300 / 200. Restated
406
+ * because `zabi-components/theme-dark-only` has to stand alone as a complete
407
+ * import (validate-theme.js enforces that). The subtle tints are the thin mix below. */
408
+ --color-action-danger: var(--zabi-error-600);
409
+ --color-action-danger-hover: var(--zabi-error-700);
410
+ --color-action-danger-active: var(--zabi-error-800);
411
+ --color-action-danger-subtle: var(--color-error-subtle);
412
+ --color-action-danger-subtle-hover: color-mix(in srgb, var(--zabi-error-500) 20%, var(--color-surface-raised));
413
+ --color-action-danger-subtle-active: color-mix(in srgb, var(--zabi-error-500) 32%, var(--color-surface-raised));
414
+ --color-action-danger-text: var(--zabi-error-50);
415
+ /* Shadow Colors — black, not the light theme's ink tint. Much stronger than
416
+ * light (0.1 / 0.06): an overlay is the same tone as the elevated surface
417
+ * now, so a sheet (L 27.4) floats over a card (L 24.1) by its shadow and its
418
+ * edge, and the shadow has to carry it. */
290
419
  --shadow-color: 0 0 0;
291
- --shadow-opacity: 0.3;
292
- --shadow-sm: 0 1px 3px 0 rgb(var(--shadow-color) / var(--shadow-opacity)), 0 1px 2px -1px rgb(var(--shadow-color) / var(--shadow-opacity));
293
- --shadow-md: 0 4px 6px -1px rgb(var(--shadow-color) / var(--shadow-opacity)), 0 2px 4px -2px rgb(var(--shadow-color) / var(--shadow-opacity));
294
- --shadow-lg: 0 10px 15px -3px rgb(var(--shadow-color) / var(--shadow-opacity)), 0 4px 6px -4px rgb(var(--shadow-color) / var(--shadow-opacity));
295
- --shadow-xl: 0 20px 25px -5px rgb(var(--shadow-color) / var(--shadow-opacity)), 0 8px 10px -6px rgb(var(--shadow-color) / var(--shadow-opacity));
420
+ --shadow-opacity: 0.5;
421
+ --shadow-opacity-contact: 0.4;
422
+ --shadow-sm: 0 1px 2px 0 rgb(var(--shadow-color) / var(--shadow-opacity-contact)), 0 2px 6px -1px rgb(var(--shadow-color) / var(--shadow-opacity));
423
+ --shadow-md: 0 1px 3px 0 rgb(var(--shadow-color) / var(--shadow-opacity-contact)), 0 6px 14px -2px rgb(var(--shadow-color) / var(--shadow-opacity));
424
+ --shadow-lg: 0 1px 2px 0 rgb(var(--shadow-color) / var(--shadow-opacity-contact)), 0 10px 30px 0 rgb(var(--shadow-color) / var(--shadow-opacity));
425
+ --shadow-xl: 0 2px 4px 0 rgb(var(--shadow-color) / var(--shadow-opacity-contact)), 0 20px 50px -8px rgb(var(--shadow-color) / var(--shadow-opacity));
426
+ --shadow-material: 0 1px 2px 0 rgb(var(--shadow-color) / var(--shadow-opacity-contact)), 0 10px 30px 0 rgb(var(--shadow-color) / var(--shadow-opacity));
427
+ /* Materials — the alphas and the backdrop brightness differ (see @theme).
428
+ * The fills are not restated: they follow the surface tokens above. The
429
+ * filters are restated, because they hold a var() that resolves where it is
430
+ * declared; the fallbacks below are one class more specific than this rule
431
+ * and set them to none, so they still win in dark. */
432
+ --material-backdrop-brightness: 0.6;
433
+ --material-alpha-thin: 60%;
434
+ --material-alpha-regular: 68%;
435
+ --material-alpha-thick: 72%;
436
+ --material-filter-thin: blur(var(--material-blur-thin)) saturate(var(--material-saturate)) brightness(var(--material-backdrop-brightness));
437
+ --material-filter-regular: blur(var(--material-blur-regular)) saturate(var(--material-saturate)) brightness(var(--material-backdrop-brightness));
438
+ --material-filter-thick: blur(var(--material-blur-thick)) saturate(var(--material-saturate)) brightness(var(--material-backdrop-brightness));
439
+ --color-material-highlight: color-mix(in srgb, var(--zabi-base-50) 14%, transparent);
440
+ --color-material-rim: color-mix(in srgb, var(--zabi-base-50) 14%, transparent);
441
+ /* Gradients: the wash is quieter in dark (see @theme). The stops follow. */
442
+ --gradient-canvas-strength: 25%;
443
+ --gradient-control-top-strength: 0%;
444
+ --gradient-control-bottom-strength: 14%;
445
+ /* The accent's own strengths, no longer the shared ones: in dark its fill is
446
+ * the light step with a dark label, which a darkening veil would fail, so it
447
+ * keeps the lighter top and slight dark bottom it had (22 / 6). */
448
+ --gradient-control-accent-top-strength: 22%;
449
+ --gradient-control-accent-bottom-strength: 6%;
450
+ --shadow-control-highlight: inset 0 1px 0 color-mix(in srgb, var(--zabi-base-50) 25%, transparent);
296
451
  /* Not a token: tells the browser the page is dark, so what it draws itself
297
452
  * (date and time pickers, scrollbars, autofill, the caret of a native
298
453
  * select) is dark too. It is the last declaration on purpose:
@@ -414,49 +569,70 @@ color-scheme: dark;
414
569
  --color-base-950: var(--zabi-base-50);
415
570
  /* Background Colors - No dark mode overrides needed, base colors are already inverted */
416
571
  /* Page Colors - No dark mode overrides needed, base colors are already inverted */
417
- /* Surface elevation — shadows don't read on dark, so each level steps up +6 OKLCH lightness
418
- * from the page background (L 21 → 27 → 33 → 39).
572
+ /* Surface elevation — shadows don't read on dark, so the surfaces carry it by tone, in two
573
+ * steps: page L 21.0, card L 24.1, elevated and overlay L 27.4 (D133/D134).
419
574
  *
420
575
  * GENERATED by scripts/generate-surfaces.js from tokens/surface-ladder.js — do not hand-edit.
421
- * The page is --zabi-base-900. Each level above it is --zabi-base-50 (the same light as the
422
- * dark hover tint) washed over the page at 6.4% / 13.1% / 19.7%, so every level is the one
423
- * below it with more light on it, and a --zabi-base-* override carries the whole ladder.
424
- * Mixing two opaque colours stays opaque, so a modal never lets the page through. With the
425
- * default greys these resolve to #18181b / #262629 / #363638 / #454547.
426
- * Enforced by scripts/check-surface-elevation.js (strictly increasing, 5–8 points). */
576
+ * Every role is a plain step of the neutral ramp (--zabi-base-N), so hue and chroma are the
577
+ * ramp's own, and a --zabi-base-* override or a tinted neutral carries all of them. An overlay
578
+ * (sheet, menu, modal) is the same tone as elevated: it is told apart by its edge
579
+ * (--color-material-rim) and its shadow, not by being greyer. The hover on an overlay is one
580
+ * ramp step lighter; the inset is the page step, told apart by its 3:1 edge.
581
+ * With the default greys: #18181b / #1f1f22 / #27272a / #27272a, hover #333338, inset #18181b.
582
+ * Enforced by scripts/check-surface-elevation.js (ramp steps, never lighter downwards, at most
583
+ * two tone steps of 2–5 points, the overlay edge and the card edge). */
427
584
  --color-surface-base: var(--zabi-base-900);
428
- --color-surface-raised: color-mix(in srgb, var(--zabi-base-50) 6.4%, var(--zabi-base-900));
429
- --color-surface-elevated: color-mix(in srgb, var(--zabi-base-50) 13.1%, var(--zabi-base-900));
430
- --color-surface-overlay: color-mix(in srgb, var(--zabi-base-50) 19.7%, var(--zabi-base-900));
431
- /* base-400 mirrors to --zabi-base-600 (#52525b), one step lighter than the overlay. */
432
- --color-surface-overlay-hover: var(--color-base-400);
433
- /* Inset — the step between the page and the raised surface (L 24). The input
434
- * field is the library's own inset; see --color-input below. */
435
- --color-surface-inset: var(--color-base-150);
585
+ --color-surface-raised: var(--zabi-base-850);
586
+ --color-surface-elevated: var(--zabi-base-800);
587
+ --color-surface-overlay: var(--zabi-base-800);
588
+ --color-surface-overlay-hover: var(--zabi-base-750);
589
+ /* Inset — the page step, 3 points below the card (L 24.1). A field on the page is told apart
590
+ * by its 3:1 edge, as a white field on the near-white light page is. */
591
+ --color-surface-inset: var(--zabi-base-900);
592
+ /* Chrome and the bar tokens — restated so a light-only override of the page
593
+ * or the raised surface does not reach dark. */
594
+ --color-surface-chrome: var(--color-surface-base);
595
+ --color-bar: var(--color-surface-chrome);
596
+ --color-bar-border: color-mix(in srgb, var(--color-headline) 10%, transparent);
597
+ --color-bar-brand: var(--color-action-primary);
598
+ --color-tabbar-active: var(--color-nav-menu-active);
436
599
  /* Distinct from base, raised AND elevated — a neutral chip has no hue to
437
- * separate it, so it has to separate on lightness wherever it lands. */
600
+ * separate it, so it has to separate on lightness wherever it lands:
601
+ * base-300 is 1.70:1 on the page, 1.57:1 on the card, 1.43:1 on elevated. */
438
602
  --color-neutral-subtle: var(--color-base-300);
439
603
  --color-neutral-border: var(--color-base-200);
440
604
  --color-border-overlay: var(--color-base-450);
441
605
  /* Background Colors */
442
606
  --color-background: var(--color-surface-base);
443
607
  --color-background-tertiary: var(--color-base-100);
444
- /* Page Colors — pinned: light moved the page to base-150, dark stays on base-100 */
445
- --color-page: var(--color-base-100);
608
+ /* Page Colors — the dark page (base-100 here is --zabi-base-900, the same value as before) */
609
+ --color-page: var(--color-surface-base);
446
610
  /* Card Colors — aliases of the surface levels */
447
611
  --color-card: var(--color-surface-raised);
448
612
  --color-card-hover: var(--color-surface-elevated);
449
- --color-card-active: var(--color-surface-overlay);
613
+ --color-card-active: var(--color-surface-overlay-hover);
450
614
  --color-card-elevated: var(--color-surface-elevated);
615
+ /* Card tint, dark: the brand at 8% over the card (as the status tints of D136,
616
+ * which are 16%: here the field edge sets the ceiling, base-500 is 3.07:1 on
617
+ * this and 2.90:1 at 12%). 8% is #252733: 1.11:1 on the card and 1.20:1 on the
618
+ * page; headline 13.49, body 11.68, label and description 10.03, caption 7.68,
619
+ * link 8.81, ring 6.59:1. */
620
+ --color-card-tint: color-mix(in srgb, var(--zabi-brand-500) 8%, var(--color-surface-raised));
621
+ /* Card variant="flat", dark: a translucent step of the neutral ramp (base-50 at 7%), so a flat card
622
+ * is lighter than whatever it sits on, and a flat card inside a flat card is lighter again. Shadows
623
+ * do not carry layers on a dark page; alpha does. Hover and pressed are the same step at 11% and 15%.
624
+ * Restore the opaque fill: overrides: { dark: { '--color-card-flat': 'var(--color-card)' } }. */
625
+ --color-card-flat: color-mix(in srgb, var(--zabi-base-50) 7%, transparent);
626
+ --color-card-flat-hover: color-mix(in srgb, var(--zabi-base-50) 11%, transparent);
627
+ --color-card-flat-active: color-mix(in srgb, var(--zabi-base-50) 15%, transparent);
451
628
  /* Surfaces — numbered aliases of the surface levels */
452
629
  --color-surface-1: var(--color-surface-raised);
453
630
  --color-surface-2: var(--color-surface-elevated);
454
631
  --color-surface-3: var(--color-surface-overlay);
455
- --color-surface-4: var(--color-base-400);
632
+ --color-surface-4: var(--color-surface-overlay-hover);
456
633
  /*
457
- * Secondary text keeps ≥ 4.5:1 on every surface level, including overlays (L39).
458
- * Only base-650 and lighter pass on the overlay, so caption takes base-650 and description
459
- * moves one step up to base-700 to stay visibly stronger than caption.
634
+ * Secondary text keeps ≥ 4.5:1 on every surface level, including overlays (L27).
635
+ * Caption takes base-650 and description base-700 to stay visibly stronger than it.
460
636
  */
461
637
  --color-description: var(--color-base-700);
462
638
  --color-caption: var(--color-base-650);
@@ -468,28 +644,87 @@ color-scheme: dark;
468
644
  /* Overlay Colors */
469
645
  --color-overlay: rgba(0, 0, 0, 0.7);
470
646
  /* Input Colors
471
- * Dark fields are INSET: one step below the raised surface they sit on,
472
- * which is the dark-mode equivalent of a white field on a grey page. */
473
- --color-input: var(--color-surface-inset);
647
+ * Dark fields are a translucent step of the neutral ramp (base-50 at 7%), the
648
+ * same idea as the flat card: a well one step LIGHTER than whatever it sits on,
649
+ * without a rule per surface. It was the page step (--color-surface-inset), so
650
+ * a dark form was told from the page by its 3:1 edge alone. 7% is 1.20:1 on
651
+ * the page (#28282b), 1.22:1 on a card (#2e2e31) and 1.23:1 on the overlay
652
+ * (#363639); hover 10% and pressed 15% (1.30:1 against the resting field on
653
+ * all three). The edge below stays 3:1 against the surface outside it.
654
+ * Restore the opaque field: overrides: { dark: {
655
+ * '--color-input': 'var(--color-surface-inset)', '--color-input-hover': 'var(--color-base-200)',
656
+ * '--color-input-focus': 'var(--color-base-150)',
657
+ * '--color-input-active': 'color-mix(in srgb, var(--zabi-base-50) 10%, var(--color-surface-inset))' } }. */
658
+ --color-input: color-mix(in srgb, var(--zabi-base-50) 7%, transparent);
659
+ /* What a field is inside a colour block (.on-brand, .on-accent, .on-fill): the translucent
660
+ * well would sit on the block's fill, which the theme's text is not set for, so the field
661
+ * takes these opaque steps there. */
662
+ --color-input-solid: var(--color-surface-inset);
663
+ --color-input-solid-hover: var(--color-base-200);
664
+ --color-input-solid-focus: var(--color-base-150);
665
+ --color-input-solid-active: color-mix(in srgb, var(--zabi-base-50) 10%, var(--color-surface-inset));
474
666
  /* Restated here: the root value of a var() alias is resolved at :root. */
475
667
  --color-progress-fill: var(--color-action-primary);
476
- --color-progress-track: var(--color-input);
477
- /* base-500 (the mirror's fixed point): 3.40:1 on the dark field, 3.67:1 on
478
- * the page and 3.12:1 on the card. base-300 was 1.57, 1.70 and 1.44. */
668
+ /* The field step is the page now, so the track takes its own: base-75
669
+ * (#101013), a well just below the page: 1.06:1 on the page, 1.14:1 on the
670
+ * card, 1.45:1 on the elevated surface. It was base-250 (#333338, 1.41:1 on
671
+ * the page). The fill is a mid step now (brand-600), and 3:1 on its rail
672
+ * needs a track this dark: 3.91:1 here with the default brand (2.59:1 on
673
+ * base-250, 2.94:1 for an amber brand on base-200). */
674
+ --color-progress-track: var(--color-base-75);
675
+ /* Restated: the root value of a var() alias is resolved at :root. The field
676
+ * edge, 3.67:1 on the page and 3.40:1 on the card. */
677
+ --color-progress-track-border: var(--color-input-border);
678
+ /* base-500 (the mirror's fixed point): 3.67:1 on the dark field and the page,
679
+ * 3.40:1 on the card, 3.08:1 on the elevated surface and the overlay.
680
+ * base-300 was 1.70, 1.57 and 1.43. */
479
681
  --color-input-border: var(--color-base-500);
480
682
  --color-input-border-hover: var(--color-input-border);
481
- --color-input-hover: var(--color-base-200);
482
- /* One step past hover, and one short of the border (base-300), so the
483
- * pressed field keeps its edge: 1.31:1 against the resting field. */
484
- --color-input-active: var(--color-base-250);
485
- --color-input-focus: var(--color-base-150);
486
- --color-input-disabled: var(--color-base-100);
683
+ /* The decorative hairline (Card's default variant, list dividers). Quiet, like
684
+ * light's: base-250 is #333338, 1.41:1 on the page and 1.31:1 on the card
685
+ * (base-300 was 1.70 and 1.57, base-200 only 1.10 on the card). Restated: the
686
+ * root value of a var() alias is resolved at :root. -medium (2.29:1) and
687
+ * -strong (3.67:1) keep their order above it. */
688
+ --color-border: var(--color-base-250);
689
+ --color-border-weak: var(--color-base-250);
690
+ /* Toggle track, off. The thumb is the card (#1f1f22), so the track cannot
691
+ * be as dark as it. base-300 (#3f3f46) is 1.57:1 against the thumb, and the
692
+ * field edge gives the 3:1: 3.67:1 on the page, 3.40:1 on the card. */
693
+ --color-toggle-track: var(--color-base-300);
694
+ --color-toggle-track-hover: var(--color-base-350);
695
+ --color-toggle-track-active: var(--color-base-400);
696
+ --color-toggle-track-border: var(--color-input-border);
697
+ /* Toggle knob in dark: off, a light knob on the dark track (base-700, #d4d4d8:
698
+ * 7.07:1 resting, 5.23:1 pressed); on, as in light (the same 4.86:1 and up). */
699
+ --color-toggle-knob: var(--color-base-700);
700
+ --color-toggle-knob-checked: var(--color-action-primary-text);
701
+ /* Hover 10%: 1.10:1 above the resting well on the page, the card and the overlay, body 8.65:1
702
+ * or more and placeholder 4.68:1 or more on it. Was base-200. */
703
+ --color-input-hover: color-mix(in srgb, var(--zabi-base-50) 10%, transparent);
704
+ /* Pressed 15% of the light end of the ramp (unmirrored, --zabi-base-50): 1.30:1 against the
705
+ * resting well on the page (#3a3a3c), the card (#404042) and the overlay (#474749), 1.18:1
706
+ * past hover, body 7.34:1 or more, placeholder 4.54:1 on a card and 4.08:1 on the overlay,
707
+ * short of the border (2.35:1 or less). 14% is 1.254:1 on the default and under 1.25 on a tinted ramp. Was 10% into the page step (#2f2f31). */
708
+ --color-input-active: color-mix(in srgb, var(--zabi-base-50) 15%, transparent);
709
+ /* Focus is the resting well: the ring shows focus, and hover still reads on a focused field.
710
+ * Was base-150. */
711
+ --color-input-focus: color-mix(in srgb, var(--zabi-base-50) 7%, transparent);
712
+ /* The field is the page step now, so the disabled field goes one step below
713
+ * the page (base-75, #101013): 1.07:1 on the page, 1.16:1 on the card. */
714
+ --color-input-disabled: var(--color-base-75);
487
715
  /* base-500 is the mirror's fixed point, the same grey as it was in light,
488
716
  * and a dark field is a far lighter ground for it than a white one is dark:
489
717
  * 3.40:1 at rest, 3.08:1 on hover, 2.60:1 pressed. base-600 mirrors to
490
718
  * --zabi-base-400 (#a1a1aa): 6.41, 5.81 and 4.90. base-550 would be 4.74
491
- * at rest but under 4.5 on hover. The value text is 2.0:1 stronger. */
492
- --color-input-placeholder: var(--color-base-600);
719
+ * at rest but under 4.5 on hover. The value text is 2.0:1 stronger.
720
+ * The well is lighter now (a translucent step over the surface), and the
721
+ * pressed one over a card held base-400 to 4.04:1: so 40% of the next step
722
+ * toward the value (base-350, #babac1) is mixed in, #ababb3: 6.46, 5.89 and
723
+ * 4.98:1 at rest, hover and pressed on the page, 5.91, 5.37 and 4.54:1 on a
724
+ * card, 5.30, 4.82 and 4.08:1 on the overlay (pressed there is held to 4:1,
725
+ * see contrast-pairs.js), and 1.80:1 under the value (1.75 is the floor; 50%
726
+ * would be 1.75). Was base-600 (#a1a1aa). */
727
+ --color-input-placeholder: color-mix(in srgb, var(--zabi-base-350) 40%, var(--zabi-base-400));
493
728
  /* Text Colors - No dark mode overrides needed, base colors are already inverted */
494
729
  /* Border Colors - the decorative edges need no override, base colors are
495
730
  * already inverted. The control boundary does: base-500 is the mirror's
@@ -498,6 +733,14 @@ color-scheme: dark;
498
733
  * mirrors to --zabi-base-400 (#a1a1aa): 3.73:1 on the overlay, the lightest
499
734
  * surface, and more on every level below it. base-550 is 2.76:1 there. */
500
735
  --color-control-border: var(--color-base-600);
736
+ /* Segmented control thumb — restated: the aliases resolve at :root, so without
737
+ * these dark would keep the light card colour. The track is a light tint over
738
+ * the page or a card (#2c2c2f / #333335), which the overlay tone now sits
739
+ * below, so the thumb takes base-400 (#52525b): 1.80:1 over the page track,
740
+ * 1.63:1 over the card track, headline text 7.03:1. */
741
+ --color-segment-thumb: var(--color-base-400);
742
+ --color-segment-thumb-text: var(--color-headline);
743
+ --color-segment-thumb-border: var(--color-control-border);
501
744
  /* Focus Colors — the same steps as light, through the mirror: brand-600 is
502
745
  * --zabi-brand-400 here, which is also the dark primary fill, so on a
503
746
  * primary button the ring is separated by its offset gap exactly as in light.
@@ -515,25 +758,28 @@ color-scheme: dark;
515
758
  --color-focus-ring-muted: var(--color-base-600);
516
759
  /* Primary Colors - No dark mode overrides needed, brand colors are already inverted */
517
760
  /* Secondary Colors - No dark mode overrides needed, base colors are already inverted */
518
- /* Tinted fills and edges — pinned at steps 100 and 200.
519
- * The solid, weak, medium, strong and text steps need no override: they
520
- * resolve through the mirrored ramps. The tints do, because light moved them
521
- * to steps 200 and 300 to read on a white card, and the mirror of those is
522
- * too heavy on a dark surface. */
523
- --color-success-subtle: var(--color-pine-100);
524
- --color-success-border: var(--color-pine-200);
525
- --color-warning-subtle: var(--color-warning-100);
526
- --color-warning-border: var(--color-warning-200);
527
- --color-error-subtle: var(--color-error-100);
528
- --color-error-border: var(--color-error-200);
529
- --color-energetic-subtle: var(--color-citron-100);
530
- --color-energetic-border: var(--color-citron-200);
531
- --color-accent-subtle: var(--color-accent-100);
532
- --color-accent-border: var(--color-accent-200);
761
+ /* Tinted fills and edges — the hue thinned into the card surface, not the
762
+ * mirrored step 100 / 200 (physical 900 / 800: deep, saturated, the loudest
763
+ * area on a dark screen). A tint here is a low-chroma tint of the card, and
764
+ * the hue is carried by the icon and the text. The mid step (500) is where
765
+ * the ramp's chroma peaks and the mirror's fixed point. The base is the card
766
+ * surface, so a badge or alert on a card is never darker than the card, and
767
+ * an app's override of the card surface carries through. The edge is the
768
+ * same mix, stronger. */
769
+ --color-success-subtle: color-mix(in srgb, var(--zabi-pine-500) 16%, var(--color-surface-raised));
770
+ --color-success-border: color-mix(in srgb, var(--zabi-pine-500) 36%, var(--color-surface-raised));
771
+ --color-warning-subtle: color-mix(in srgb, var(--zabi-warning-500) 16%, var(--color-surface-raised));
772
+ --color-warning-border: color-mix(in srgb, var(--zabi-warning-500) 36%, var(--color-surface-raised));
773
+ --color-error-subtle: color-mix(in srgb, var(--zabi-error-500) 16%, var(--color-surface-raised));
774
+ --color-error-border: color-mix(in srgb, var(--zabi-error-500) 36%, var(--color-surface-raised));
775
+ --color-energetic-subtle: color-mix(in srgb, var(--zabi-citron-500) 16%, var(--color-surface-raised));
776
+ --color-energetic-border: color-mix(in srgb, var(--zabi-citron-500) 36%, var(--color-surface-raised));
777
+ --color-accent-subtle: color-mix(in srgb, var(--zabi-accent-500) 16%, var(--color-surface-raised));
778
+ --color-accent-border: color-mix(in srgb, var(--zabi-accent-500) 36%, var(--color-surface-raised));
533
779
  /* Label on a solid accent fill: the dark end of the ramp on its light end. */
534
780
  --color-on-accent: var(--zabi-on-accent-dark);
535
- --color-info-subtle: var(--color-iris-100);
536
- --color-info-border: var(--color-iris-200);
781
+ --color-info-subtle: color-mix(in srgb, var(--zabi-iris-500) 16%, var(--color-surface-raised));
782
+ --color-info-border: color-mix(in srgb, var(--zabi-iris-500) 36%, var(--color-surface-raised));
537
783
  /* Neutral Colors — base-550 mirrors to L* 57, which is only 4.35:1 on a
538
784
  * raised surface. One step lighter clears AA on every dark surface level. */
539
785
  --color-neutral: var(--color-base-600);
@@ -541,48 +787,112 @@ color-scheme: dark;
541
787
  /* ========================================
542
788
  ACTION COLORS - Dark Mode Overrides
543
789
  ======================================== */
544
- /* Primary Actions - Background adapts automatically via brand color inversion */
545
- /* 🎯 TO CHANGE bg-action-primary IN DARK MODE: Edit the --zabi-brand-* physical ramp in @theme (semantic --color-brand-* follows the mirror) */
546
- /* "On brand" in dark: --zabi-on-brand-dark, by default the physical dark end
547
- * of the brand ramp on the physical light end used as the dark-mode primary
548
- * fill (7.21:1). --color-action-primary-text is restated, as it always
549
- * was here, so that an app's light-only override of it does not carry a
550
- * white label onto the light dark-mode fill. */
790
+ /* Primary Actions - the brand hue in dark too: the same physical steps as
791
+ * light (600 / 700 / 800), not the mirrored ones, which would be the pale
792
+ * 400 / 300 / 200. White on step 600 is 4.76 to 4.86:1 (every ramp is on one
793
+ * lightness curve); with the default brand the fill is 3.65:1 on the page,
794
+ * 3.39:1 on the card and 3.07:1 on the overlay (3:1 holds on all three, WCAG
795
+ * 1.4.11). Hover and pressed go darker, as in light.
796
+ * createTheme({ darkPrimary: 'mirror' }) writes the mirrored fills instead.
797
+ * 🎯 TO CHANGE bg-action-primary IN DARK MODE: override --color-action-primary under `dark`. */
798
+ --color-action-primary: var(--zabi-brand-600);
799
+ --color-action-primary-hover: var(--zabi-brand-700);
800
+ --color-action-primary-active: var(--zabi-brand-800);
801
+ /* "On brand" in dark: --zabi-on-brand-dark, which follows the light label (white). If an app
802
+ * moves the dark primary to a pale step, it sets the knob to a dark one.
803
+ * --color-action-primary-text is restated, as it always was here, so that an
804
+ * app's light-only override of it does not carry a different label onto the
805
+ * dark fill. */
551
806
  --color-on-brand: var(--zabi-on-brand-dark);
552
807
  --color-action-primary-text: var(--color-on-brand);
553
808
  /* Pinned like the other tints: light moved these a step darker. */
554
809
  --color-action-primary-subtle: var(--color-brand-100);
555
810
  --color-action-primary-subtle-hover: var(--color-brand-200);
811
+ --color-action-primary-border: var(--color-brand-200);
556
812
  /* Half a step past the dark hover fill, as in light. */
557
813
  --color-action-primary-subtle-active: color-mix(in srgb, var(--color-brand-300) 50%, var(--color-brand-200));
558
814
  /* Secondary Actions - light alpha tint instead of the light-mode dark tint */
559
815
  --color-action-secondary: rgba(250, 250, 250, 0.09);
560
816
  --color-action-secondary-hover: rgba(250, 250, 250, 0.15);
561
817
  --color-action-secondary-active: rgba(250, 250, 250, 0.21);
562
- /* Disabled Actions - base-200 in dark is #27272a, still the quietest fill.
818
+ /* Tonal Actions - the deep brand tint (brand-100 in dark is the deep end of
819
+ * the mirrored ramp) with a light brand label. Restated: the root values are
820
+ * var() aliases of roles that move in dark. Brand-800 is the light end of the
821
+ * mirrored ramp here: 9.48, 6.94 and 5.86:1 on the three fills. */
822
+ --color-action-tonal: var(--color-action-primary-subtle);
823
+ --color-action-tonal-hover: var(--color-action-primary-subtle-hover);
824
+ --color-action-tonal-active: color-mix(in srgb, var(--color-brand-300) 50%, var(--color-brand-200));
825
+ --color-action-tonal-text: var(--color-brand-800);
826
+ /* Chip, dark - its own steps, not the mirror of light. The ladder rises from
827
+ * the page (base-900, card base-850): rest base-750 is 1.41:1 on the page and
828
+ * 1.31:1 on the card, hover base-700 and pressed base-650 lighter again (1.70
829
+ * and 1.96 on the page). Headline is 11.43, 9.50 and 8.24:1 on the three.
830
+ * Restated, with the selected chip, because the root values are var() aliases. */
831
+ --color-chip: var(--zabi-base-750);
832
+ --color-chip-hover: var(--zabi-base-700);
833
+ --color-chip-active: var(--zabi-base-650);
834
+ --color-chip-text: var(--color-headline);
835
+ --color-chip-selected: var(--color-action-tonal);
836
+ --color-chip-selected-hover: var(--color-action-tonal-hover);
837
+ --color-chip-selected-active: var(--color-action-tonal-active);
838
+ --color-chip-selected-text: var(--color-action-tonal-text);
839
+ /* Outline Actions - the field edge, restated for the same reason. */
840
+ --color-action-outline-border: var(--color-input-border);
841
+ --color-action-outline-border-hover: var(--color-input-border-hover);
842
+ /* Disabled Actions - base-200 in dark is #27272a (the elevated tone), still
843
+ * the quietest fill, and the one the disabled label (base-500) reads on at 3.08:1.
563
844
  * Pinned: light moved a step darker to stay visible on its darker page. */
564
845
  --color-action-disabled: var(--color-base-200);
565
846
  --color-action-disabled-border: var(--color-base-250);
566
- /* Danger Actions — restated as aliases, not re-pointed.
567
- * These resolve through the mirrored --color-error-* scale, so dark danger
568
- * is the same step of the same ramp as light danger. They are repeated here
569
- * only because `zabi-components/theme-dark-only` has to stand alone as a
570
- * complete import (validate-theme.js enforces that). The raw hex this
571
- * replaced pinned dark danger brighter than light, which is why the
572
- * destructive action was the loudest element on a dark page. */
573
- --color-action-danger: var(--color-error-600);
574
- --color-action-danger-hover: var(--color-error-700);
575
- --color-action-danger-active: var(--color-error-800);
576
- --color-action-danger-subtle: var(--color-error-100);
577
- --color-action-danger-subtle-hover: var(--color-error-200);
578
- --color-action-danger-text: var(--color-error-50);
579
- /* Shadow Colors — black, not the light theme's ink tint */
847
+ /* Danger Actions — the same physical steps as light danger (600 / 700 / 800
848
+ * of --zabi-error-*) with the same label (step 50), so a destructive button
849
+ * is one red in both modes and shares the primary's veil. Physical, not
850
+ * through the mirror, which would be the pale 400 / 300 / 200. Restated
851
+ * because `zabi-components/theme-dark-only` has to stand alone as a complete
852
+ * import (validate-theme.js enforces that). The subtle tints are the thin mix below. */
853
+ --color-action-danger: var(--zabi-error-600);
854
+ --color-action-danger-hover: var(--zabi-error-700);
855
+ --color-action-danger-active: var(--zabi-error-800);
856
+ --color-action-danger-subtle: var(--color-error-subtle);
857
+ --color-action-danger-subtle-hover: color-mix(in srgb, var(--zabi-error-500) 20%, var(--color-surface-raised));
858
+ --color-action-danger-subtle-active: color-mix(in srgb, var(--zabi-error-500) 32%, var(--color-surface-raised));
859
+ --color-action-danger-text: var(--zabi-error-50);
860
+ /* Shadow Colors — black, not the light theme's ink tint. Much stronger than
861
+ * light (0.1 / 0.06): an overlay is the same tone as the elevated surface
862
+ * now, so a sheet (L 27.4) floats over a card (L 24.1) by its shadow and its
863
+ * edge, and the shadow has to carry it. */
580
864
  --shadow-color: 0 0 0;
581
- --shadow-opacity: 0.3;
582
- --shadow-sm: 0 1px 3px 0 rgb(var(--shadow-color) / var(--shadow-opacity)), 0 1px 2px -1px rgb(var(--shadow-color) / var(--shadow-opacity));
583
- --shadow-md: 0 4px 6px -1px rgb(var(--shadow-color) / var(--shadow-opacity)), 0 2px 4px -2px rgb(var(--shadow-color) / var(--shadow-opacity));
584
- --shadow-lg: 0 10px 15px -3px rgb(var(--shadow-color) / var(--shadow-opacity)), 0 4px 6px -4px rgb(var(--shadow-color) / var(--shadow-opacity));
585
- --shadow-xl: 0 20px 25px -5px rgb(var(--shadow-color) / var(--shadow-opacity)), 0 8px 10px -6px rgb(var(--shadow-color) / var(--shadow-opacity));
865
+ --shadow-opacity: 0.5;
866
+ --shadow-opacity-contact: 0.4;
867
+ --shadow-sm: 0 1px 2px 0 rgb(var(--shadow-color) / var(--shadow-opacity-contact)), 0 2px 6px -1px rgb(var(--shadow-color) / var(--shadow-opacity));
868
+ --shadow-md: 0 1px 3px 0 rgb(var(--shadow-color) / var(--shadow-opacity-contact)), 0 6px 14px -2px rgb(var(--shadow-color) / var(--shadow-opacity));
869
+ --shadow-lg: 0 1px 2px 0 rgb(var(--shadow-color) / var(--shadow-opacity-contact)), 0 10px 30px 0 rgb(var(--shadow-color) / var(--shadow-opacity));
870
+ --shadow-xl: 0 2px 4px 0 rgb(var(--shadow-color) / var(--shadow-opacity-contact)), 0 20px 50px -8px rgb(var(--shadow-color) / var(--shadow-opacity));
871
+ --shadow-material: 0 1px 2px 0 rgb(var(--shadow-color) / var(--shadow-opacity-contact)), 0 10px 30px 0 rgb(var(--shadow-color) / var(--shadow-opacity));
872
+ /* Materials — the alphas and the backdrop brightness differ (see @theme).
873
+ * The fills are not restated: they follow the surface tokens above. The
874
+ * filters are restated, because they hold a var() that resolves where it is
875
+ * declared; the fallbacks below are one class more specific than this rule
876
+ * and set them to none, so they still win in dark. */
877
+ --material-backdrop-brightness: 0.6;
878
+ --material-alpha-thin: 60%;
879
+ --material-alpha-regular: 68%;
880
+ --material-alpha-thick: 72%;
881
+ --material-filter-thin: blur(var(--material-blur-thin)) saturate(var(--material-saturate)) brightness(var(--material-backdrop-brightness));
882
+ --material-filter-regular: blur(var(--material-blur-regular)) saturate(var(--material-saturate)) brightness(var(--material-backdrop-brightness));
883
+ --material-filter-thick: blur(var(--material-blur-thick)) saturate(var(--material-saturate)) brightness(var(--material-backdrop-brightness));
884
+ --color-material-highlight: color-mix(in srgb, var(--zabi-base-50) 14%, transparent);
885
+ --color-material-rim: color-mix(in srgb, var(--zabi-base-50) 14%, transparent);
886
+ /* Gradients: the wash is quieter in dark (see @theme). The stops follow. */
887
+ --gradient-canvas-strength: 25%;
888
+ --gradient-control-top-strength: 0%;
889
+ --gradient-control-bottom-strength: 14%;
890
+ /* The accent's own strengths, no longer the shared ones: in dark its fill is
891
+ * the light step with a dark label, which a darkening veil would fail, so it
892
+ * keeps the lighter top and slight dark bottom it had (22 / 6). */
893
+ --gradient-control-accent-top-strength: 22%;
894
+ --gradient-control-accent-bottom-strength: 6%;
895
+ --shadow-control-highlight: inset 0 1px 0 color-mix(in srgb, var(--zabi-base-50) 25%, transparent);
586
896
  /* Not a token: tells the browser the page is dark, so what it draws itself
587
897
  * (date and time pickers, scrollbars, autofill, the caret of a native
588
898
  * select) is dark too. It is the last declaration on purpose: