@multiplatform.one/theme 7.34.4 → 7.35.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 (280) hide show
  1. package/package.json +20 -5
  2. package/src/audit/constraintAudit.spec.ts +274 -87
  3. package/src/audit/constraintAudit.ts +135 -120
  4. package/src/audit/constraintScope.spec.ts +1 -1
  5. package/src/audit/designTokens.ts +42 -0
  6. package/src/audit/themeMatrix.spec.ts +83 -2
  7. package/src/audit/themeMatrix.ts +33 -1
  8. package/src/figma/figmaTokens.spec.ts +26 -34
  9. package/src/figma/figmaTokens.ts +26 -50
  10. package/src/fonts/createFontLoader.ts +4 -1
  11. package/src/menuRow.ts +4 -1
  12. package/src/theme/FontKnobStyles.tsx +2 -1
  13. package/src/theme/Preset.stories.tsx +0 -1
  14. package/src/theme/Preset.tsx +6 -1
  15. package/src/theme/PresetContext.ts +4 -0
  16. package/src/theme/Surface.spec.tsx +114 -67
  17. package/src/theme/Surface.stories.tsx +8 -25
  18. package/src/theme/Surface.tsx +87 -43
  19. package/src/theme/accentFloor.spec.ts +37 -11
  20. package/src/theme/activateThemeConfig.ts +22 -6
  21. package/src/theme/animations/animations.spec.ts +62 -0
  22. package/src/theme/animations/css.ts +9 -35
  23. package/src/theme/animations/reactNative.ts +98 -25
  24. package/src/theme/boundaryFloor.spec.ts +57 -0
  25. package/src/theme/chartPalette.spec.ts +56 -27
  26. package/src/theme/chartPalette.ts +33 -28
  27. package/src/theme/colorRules.spec.ts +32 -72
  28. package/src/theme/cookies.spec.ts +49 -18
  29. package/src/theme/cookies.ts +36 -7
  30. package/src/theme/cookiesPersistence.spec.ts +5 -5
  31. package/src/theme/createDefaultThemeConfig.spec.ts +182 -20
  32. package/src/theme/createDefaultThemeConfig.ts +34 -28
  33. package/src/theme/createThemes.ts +169 -218
  34. package/src/theme/defaults/accent.ts +3 -35
  35. package/src/theme/defaults/base.ts +14 -53
  36. package/src/theme/defaults/builderOptions.ts +68 -85
  37. package/src/theme/defaults/categoryFonts.ts +10 -0
  38. package/src/theme/defaults/fonts.spec.ts +84 -4
  39. package/src/theme/defaults/fonts.ts +166 -124
  40. package/src/theme/defaults/house.ts +48 -0
  41. package/src/theme/defaults/index.ts +1 -0
  42. package/src/theme/defaults/neutralRamp.spec.ts +52 -47
  43. package/src/theme/devtools/ColorLineVisualizer.tsx +6 -6
  44. package/src/theme/devtools/ThemeDevtoolsPanel.setMarkers.spec.tsx +13 -6
  45. package/src/theme/devtools/ThemeDevtoolsPanel.tsx +14 -77
  46. package/src/theme/devtools/animationOptions.spec.ts +23 -37
  47. package/src/theme/devtools/animationOptions.ts +4 -6
  48. package/src/theme/devtools/buildSnapshot.ts +0 -3
  49. package/src/theme/devtools/knobIcons.tsx +4 -24
  50. package/src/theme/focusState.spec.ts +9 -30
  51. package/src/theme/index.ts +17 -0
  52. package/src/theme/intentFill.spec.ts +42 -0
  53. package/src/theme/knobs.ts +14 -42
  54. package/src/theme/layoutTokens.ts +15 -1
  55. package/src/theme/layoutTokensHooks.spec.tsx +8 -4
  56. package/src/theme/mergedTokens.ts +20 -0
  57. package/src/theme/motion.spec.ts +84 -0
  58. package/src/theme/motion.ts +53 -0
  59. package/src/theme/motionStops.ts +17 -0
  60. package/src/theme/motionTiming.ts +59 -0
  61. package/src/theme/mpo13Proof.spec.ts +43 -46
  62. package/src/theme/mpo19Proof.spec.ts +13 -14
  63. package/src/theme/namedInkFloor.spec.ts +60 -0
  64. package/src/theme/overlaySurfaces.tsx +330 -0
  65. package/src/theme/preset.types.ts +16 -0
  66. package/src/theme/presetPreview.ts +32 -0
  67. package/src/theme/presetRoot.spec.tsx +89 -0
  68. package/src/theme/presetStore.persistence.spec.ts +141 -0
  69. package/src/theme/presetStore.spec.ts +205 -0
  70. package/src/theme/presetStore.ts +286 -0
  71. package/src/theme/presetUserSync.spec.tsx +168 -0
  72. package/src/theme/presetUserSync.ts +47 -0
  73. package/src/theme/presets.spec.ts +22 -15
  74. package/src/theme/presets.ts +22 -10
  75. package/src/theme/radiusClass.spec.ts +60 -41
  76. package/src/theme/radiusClass.ts +17 -4
  77. package/src/theme/radiusClassTable.generated.ts +3 -3
  78. package/src/theme/readableColor.spec.ts +59 -152
  79. package/src/theme/readableColor.ts +34 -174
  80. package/src/theme/recipeInputs.ts +153 -295
  81. package/src/theme/recipes.ts +58 -18
  82. package/src/theme/resolveKnobs.spec.ts +528 -588
  83. package/src/theme/resolveKnobs.ts +250 -325
  84. package/src/theme/scale/ScaleStyles.native.tsx +19 -0
  85. package/src/theme/scale/ScaleStyles.tsx +48 -0
  86. package/src/theme/scale/activeScale.ts +30 -0
  87. package/src/theme/scale/deriveRecipeInputs.ts +110 -0
  88. package/src/theme/scale/generateScale.spec.ts +260 -0
  89. package/src/theme/scale/generateScale.ts +147 -0
  90. package/src/theme/scale/index.ts +54 -0
  91. package/src/theme/scale/installScaleConfig.native.spec.ts +77 -0
  92. package/src/theme/scale/installScaleConfig.native.ts +57 -0
  93. package/src/theme/scale/installScaleConfig.ts +7 -0
  94. package/src/theme/scale/scaleAttribute.ts +21 -0
  95. package/src/theme/scale/scaleCss.probe.spec.tsx +64 -0
  96. package/src/theme/scale/scaleCss.spec.ts +132 -0
  97. package/src/theme/scale/scaleCss.ts +95 -0
  98. package/src/theme/scale/scaleFonts.ts +106 -0
  99. package/src/theme/scale/scaleInputs.ts +322 -0
  100. package/src/theme/scale/themeScales.ts +181 -0
  101. package/src/theme/scale/useActiveScale.spec.tsx +129 -0
  102. package/src/theme/scale/useActiveScale.ts +95 -0
  103. package/src/theme/schemeStore.spec.ts +81 -0
  104. package/src/theme/schemeStore.ts +67 -0
  105. package/src/theme/shared.spec.ts +7 -6
  106. package/src/theme/shared.tsx +37 -3
  107. package/src/theme/sizeLadder.spec.ts +37 -36
  108. package/src/theme/sizeRecipes.spec.ts +179 -85
  109. package/src/theme/sizeRecipes.ts +48 -25
  110. package/src/theme/subsetThemes.spec.ts +9 -3
  111. package/src/theme/switchThumb.spec.ts +69 -0
  112. package/src/theme/syntaxFloor.spec.ts +11 -8
  113. package/src/theme/theme.hydration.native.spec.tsx +51 -0
  114. package/src/theme/theme.native.tsx +59 -21
  115. package/src/theme/theme.palette.spec.tsx +76 -0
  116. package/src/theme/theme.preset.native.spec.tsx +73 -0
  117. package/src/theme/theme.provider.spec.ts +143 -1
  118. package/src/theme/theme.tsx +61 -25
  119. package/src/theme/tileSizes.ts +6 -7
  120. package/src/theme/transitionProps.native.ts +25 -2
  121. package/src/theme/transitionProps.spec.ts +34 -0
  122. package/src/theme/transitionProps.ts +26 -2
  123. package/src/theme/usePresetSwitch.native.spec.tsx +68 -0
  124. package/src/theme/usePresetSwitch.native.ts +32 -0
  125. package/src/theme/usePresetSwitch.spec.tsx +103 -0
  126. package/src/theme/usePresetSwitch.ts +54 -0
  127. package/src/theme/useResolvedKnobs.hydration.spec.tsx +2 -2
  128. package/src/theme/useResolvedKnobs.ts +23 -90
  129. package/src/theme/useResolvedKnobsBehavior.spec.tsx +25 -30
  130. package/src/theme/useSchemePreference.native.ts +33 -0
  131. package/src/theme/useSchemePreference.spec.tsx +51 -0
  132. package/src/theme/useSchemePreference.ts +32 -0
  133. package/src/theme/useSchemePreference.web.spec.tsx +32 -0
  134. package/src/theme/useTheme.native.spec.tsx +68 -0
  135. package/src/theme/useTheme.native.ts +49 -24
  136. package/src/theme/useTheme.ts +7 -4
  137. package/types/audit/constraintAudit.d.ts +6 -5
  138. package/types/audit/constraintAudit.d.ts.map +1 -1
  139. package/types/audit/designTokens.d.ts +13 -0
  140. package/types/audit/designTokens.d.ts.map +1 -0
  141. package/types/audit/themeMatrix.d.ts +10 -0
  142. package/types/audit/themeMatrix.d.ts.map +1 -1
  143. package/types/figma/figmaTokens.d.ts +11 -28
  144. package/types/figma/figmaTokens.d.ts.map +1 -1
  145. package/types/fonts/createFontLoader.d.ts.map +1 -1
  146. package/types/menuRow.d.ts +4 -1
  147. package/types/menuRow.d.ts.map +1 -1
  148. package/types/theme/FontKnobStyles.d.ts.map +1 -1
  149. package/types/theme/Preset.d.ts.map +1 -1
  150. package/types/theme/PresetContext.d.ts +4 -0
  151. package/types/theme/PresetContext.d.ts.map +1 -1
  152. package/types/theme/Surface.d.ts +57 -26
  153. package/types/theme/Surface.d.ts.map +1 -1
  154. package/types/theme/activateThemeConfig.d.ts +5 -3
  155. package/types/theme/activateThemeConfig.d.ts.map +1 -1
  156. package/types/theme/animations/css.d.ts +27 -34
  157. package/types/theme/animations/css.d.ts.map +1 -1
  158. package/types/theme/animations/reactNative.d.ts +196 -45
  159. package/types/theme/animations/reactNative.d.ts.map +1 -1
  160. package/types/theme/chartPalette.d.ts +11 -3
  161. package/types/theme/chartPalette.d.ts.map +1 -1
  162. package/types/theme/colorRules.d.ts +1 -1
  163. package/types/theme/cookies.d.ts +12 -1
  164. package/types/theme/cookies.d.ts.map +1 -1
  165. package/types/theme/createDefaultThemeConfig.d.ts +13 -13
  166. package/types/theme/createDefaultThemeConfig.d.ts.map +1 -1
  167. package/types/theme/createThemes.d.ts +58 -2
  168. package/types/theme/createThemes.d.ts.map +1 -1
  169. package/types/theme/defaults/accent.d.ts.map +1 -1
  170. package/types/theme/defaults/base.d.ts.map +1 -1
  171. package/types/theme/defaults/builderOptions.d.ts.map +1 -1
  172. package/types/theme/defaults/categoryFonts.d.ts +10 -0
  173. package/types/theme/defaults/categoryFonts.d.ts.map +1 -1
  174. package/types/theme/defaults/fonts.d.ts +330 -30
  175. package/types/theme/defaults/fonts.d.ts.map +1 -1
  176. package/types/theme/defaults/house.d.ts +8 -0
  177. package/types/theme/defaults/house.d.ts.map +1 -0
  178. package/types/theme/defaults/index.d.ts +1 -0
  179. package/types/theme/defaults/index.d.ts.map +1 -1
  180. package/types/theme/devtools/ThemeDevtoolsPanel.d.ts +3 -3
  181. package/types/theme/devtools/ThemeDevtoolsPanel.d.ts.map +1 -1
  182. package/types/theme/devtools/animationOptions.d.ts.map +1 -1
  183. package/types/theme/devtools/buildSnapshot.d.ts.map +1 -1
  184. package/types/theme/devtools/knobIcons.d.ts +0 -3
  185. package/types/theme/devtools/knobIcons.d.ts.map +1 -1
  186. package/types/theme/index.d.ts +11 -0
  187. package/types/theme/index.d.ts.map +1 -1
  188. package/types/theme/knobs.d.ts +8 -36
  189. package/types/theme/knobs.d.ts.map +1 -1
  190. package/types/theme/layoutTokens.d.ts +13 -1
  191. package/types/theme/layoutTokens.d.ts.map +1 -1
  192. package/types/theme/mergedTokens.d.ts +11 -0
  193. package/types/theme/mergedTokens.d.ts.map +1 -0
  194. package/types/theme/motion.d.ts +17 -0
  195. package/types/theme/motion.d.ts.map +1 -0
  196. package/types/theme/motionStops.d.ts +3 -0
  197. package/types/theme/motionStops.d.ts.map +1 -0
  198. package/types/theme/motionTiming.d.ts +36 -0
  199. package/types/theme/motionTiming.d.ts.map +1 -0
  200. package/types/theme/overlaySurfaces.d.ts +70 -0
  201. package/types/theme/overlaySurfaces.d.ts.map +1 -0
  202. package/types/theme/preset.types.d.ts +16 -0
  203. package/types/theme/preset.types.d.ts.map +1 -1
  204. package/types/theme/presetPreview.d.ts +14 -0
  205. package/types/theme/presetPreview.d.ts.map +1 -0
  206. package/types/theme/presetStore.d.ts +40 -0
  207. package/types/theme/presetStore.d.ts.map +1 -0
  208. package/types/theme/presetUserSync.d.ts +7 -0
  209. package/types/theme/presetUserSync.d.ts.map +1 -0
  210. package/types/theme/presets.d.ts +12 -4
  211. package/types/theme/presets.d.ts.map +1 -1
  212. package/types/theme/radiusClass.d.ts +5 -2
  213. package/types/theme/radiusClass.d.ts.map +1 -1
  214. package/types/theme/radiusClassTable.generated.d.ts +3 -3
  215. package/types/theme/readableColor.d.ts +11 -49
  216. package/types/theme/readableColor.d.ts.map +1 -1
  217. package/types/theme/recipeInputs.d.ts +39 -70
  218. package/types/theme/recipeInputs.d.ts.map +1 -1
  219. package/types/theme/recipes.d.ts +79 -18
  220. package/types/theme/recipes.d.ts.map +1 -1
  221. package/types/theme/resolveKnobs.d.ts +64 -32
  222. package/types/theme/resolveKnobs.d.ts.map +1 -1
  223. package/types/theme/scale/ScaleStyles.d.ts +18 -0
  224. package/types/theme/scale/ScaleStyles.d.ts.map +1 -0
  225. package/types/theme/scale/ScaleStyles.native.d.ts +10 -0
  226. package/types/theme/scale/ScaleStyles.native.d.ts.map +1 -0
  227. package/types/theme/scale/activeScale.d.ts +13 -0
  228. package/types/theme/scale/activeScale.d.ts.map +1 -0
  229. package/types/theme/scale/deriveRecipeInputs.d.ts +10 -0
  230. package/types/theme/scale/deriveRecipeInputs.d.ts.map +1 -0
  231. package/types/theme/scale/generateScale.d.ts +38 -0
  232. package/types/theme/scale/generateScale.d.ts.map +1 -0
  233. package/types/theme/scale/index.d.ts +17 -0
  234. package/types/theme/scale/index.d.ts.map +1 -0
  235. package/types/theme/scale/installScaleConfig.d.ts +4 -0
  236. package/types/theme/scale/installScaleConfig.d.ts.map +1 -0
  237. package/types/theme/scale/installScaleConfig.native.d.ts +13 -0
  238. package/types/theme/scale/installScaleConfig.native.d.ts.map +1 -0
  239. package/types/theme/scale/scaleAttribute.d.ts +14 -0
  240. package/types/theme/scale/scaleAttribute.d.ts.map +1 -0
  241. package/types/theme/scale/scaleCss.d.ts +25 -0
  242. package/types/theme/scale/scaleCss.d.ts.map +1 -0
  243. package/types/theme/scale/scaleFonts.d.ts +16 -0
  244. package/types/theme/scale/scaleFonts.d.ts.map +1 -0
  245. package/types/theme/scale/scaleInputs.d.ts +66 -0
  246. package/types/theme/scale/scaleInputs.d.ts.map +1 -0
  247. package/types/theme/scale/themeScales.d.ts +52 -0
  248. package/types/theme/scale/themeScales.d.ts.map +1 -0
  249. package/types/theme/scale/useActiveScale.d.ts +31 -0
  250. package/types/theme/scale/useActiveScale.d.ts.map +1 -0
  251. package/types/theme/schemeStore.d.ts +6 -0
  252. package/types/theme/schemeStore.d.ts.map +1 -0
  253. package/types/theme/shared.d.ts +18 -0
  254. package/types/theme/shared.d.ts.map +1 -1
  255. package/types/theme/sizeRecipes.d.ts +20 -15
  256. package/types/theme/sizeRecipes.d.ts.map +1 -1
  257. package/types/theme/theme.d.ts +2 -1
  258. package/types/theme/theme.d.ts.map +1 -1
  259. package/types/theme/theme.native.d.ts +10 -1
  260. package/types/theme/theme.native.d.ts.map +1 -1
  261. package/types/theme/tileSizes.d.ts +6 -7
  262. package/types/theme/tileSizes.d.ts.map +1 -1
  263. package/types/theme/transitionProps.d.ts +3 -1
  264. package/types/theme/transitionProps.d.ts.map +1 -1
  265. package/types/theme/transitionProps.native.d.ts +3 -1
  266. package/types/theme/transitionProps.native.d.ts.map +1 -1
  267. package/types/theme/usePresetSwitch.d.ts +10 -0
  268. package/types/theme/usePresetSwitch.d.ts.map +1 -0
  269. package/types/theme/usePresetSwitch.native.d.ts +10 -0
  270. package/types/theme/usePresetSwitch.native.d.ts.map +1 -0
  271. package/types/theme/useResolvedKnobs.d.ts +13 -13
  272. package/types/theme/useResolvedKnobs.d.ts.map +1 -1
  273. package/types/theme/useSchemePreference.d.ts +8 -0
  274. package/types/theme/useSchemePreference.d.ts.map +1 -0
  275. package/types/theme/useSchemePreference.native.d.ts +8 -0
  276. package/types/theme/useSchemePreference.native.d.ts.map +1 -0
  277. package/types/theme/useTheme.d.ts +4 -2
  278. package/types/theme/useTheme.d.ts.map +1 -1
  279. package/types/theme/useTheme.native.d.ts +10 -1
  280. package/types/theme/useTheme.native.d.ts.map +1 -1
@@ -7,70 +7,20 @@ import { createFont, type GenericFont } from "@tamagui/web";
7
7
  import { flooredLineHeight, fontCategoryMetrics } from "../fontCategoryMetrics";
8
8
  import { fontCategoryStacks } from "../fontCategoryStacks";
9
9
  import type { FontCategory } from "../knobs";
10
+ import { generateScale, type ScaleFonts } from "../scale/generateScale";
11
+ import { defaultScaleInputs } from "../scale/scaleInputs";
10
12
 
11
- // Default purpose fonts for components that reference $heading / $body
12
- // directly, both backed by Inter.
13
+ // $heading and $body are Inter on the generated scale's tables
14
+ // (scale/generateScale.ts reproduces @tamagui/config/v5 key for key at the
15
+ // default inputs); weights come from v5's tables. Inter tracks by size on its
16
+ // dynamic-metrics curve (rsms.me/inter/dynmetrics): a + b·e^(c·size) em.
13
17
  //
14
- // MPO-19 X8 + X9. These tables used to be @tamagui/config/v4's — headings on a
15
- // 1.4x scale (H1 $10 = 64px) at whatever weight the knob happened to default
16
- // to, body at 14px with `lineHeight = size + 10`. Against the v5 era this
17
- // package now pins (see `TAMAGUI_ERA` in createDefaultThemeConfig.ts) that is a
18
- // different typographic voice, not a variation on one: H1 was 64px/400 where
19
- // stock v5 is 40px/800, large and light against compact and heavy. And
20
- // `size + 10` is not a ratio at all — 1.91 at 11px, 1.22 at 46px — which is
21
- // why v5 replaced it with an explicit taper.
22
- //
23
- // So the tables are no longer transcribed. They are READ from
24
- // `defaultConfig.fonts`, which means the era pin is load-bearing rather than
25
- // decorative: re-point it and the type scale follows, and there is no local
26
- // copy that can drift from upstream between pins. That also retires the
27
- // hand-mirrored `interBaseSizes` table this file used to carry purely so
28
- // tracking could be computed per key — the size table it needed IS the one
29
- // upstream ships.
30
- //
31
- // Tracking-by-size (Axiom 15 OPTICS + LC-11): letter-spacing is a function
32
- // of the rendered size, not a constant — slightly loose at caption sizes,
33
- // increasingly tight at display sizes (the same optical behavior Apple's SF
34
- // implements per point size). The curve is Inter's own dynamic-metrics
35
- // model (rsms.me/inter/dynmetrics): tracking(em) = a + b·e^(c·size).
36
- //
37
- // ── Category fonts (MPO-44, re-derived against the era pin) ──────────────
38
- //
39
- // A bodyFont stop is a FAMILY choice. It must move the family and nothing
40
- // else, so every category font is built from the era's own `$body` tables
41
- // rather than a ramp of its own. Measured on the label text node before this
42
- // change, both schemes:
43
- //
44
- // sans-serif Inter 15px/23px
45
- // serif Georgia 14px/24px
46
- // mono ui-monospace 14px/24px
47
- // rounded Nunito 14px/24px
48
- //
49
- // Registering the families (the first half of MPO-44) stopped the collapse to
50
- // -apple-system 14px/normal, but the era pin landed after it and moved `$body`
51
- // to 15/23 — so ten of eleven stops were left on a second ladder, and flipping
52
- // the knob resized the label by a pixel and moved its leading. Same defect
53
- // class as the one MPO-44 was filed for: the knob was still taking type off
54
- // the ramp, just more quietly.
55
- //
56
- // Three deliberate departures from `$body`, all upward and all LC-11:
57
- //
58
- // 1. Leading is floored per category — `flooredLineHeight` raises the era's
59
- // value where the taper falls under the category's ratio, never lowers
60
- // it. Only display steps move; every text step is `$body`'s own value.
61
- // 2. Tracking is 0, not Inter's curve. That curve is Inter-specific, and an
62
- // empty table is not neutral: the class Tamagui emits for `$mono` does
63
- // not declare `--f-letterSpacing-*`, so the value fell through to the
64
- // `:root` Inter one and Georgia and Nunito rendered at Inter's −0.13px.
65
- // There is no measured optical curve for these faces, so 0 is the honest
66
- // value rather than a borrowed one.
67
- // 3. Weight is the era's (400 at every step). The previous default was 300,
68
- // which is off the reference's label weight in the other direction.
69
- //
70
- // `$body` itself is untouched: it IS upstream's table under the pin, and
71
- // MPO-19 owns it. Its ratio dips below LC-11's 1.4 body floor from $11 up;
72
- // FontKnobStyles floors that scope on web, and correcting the table would
73
- // have to be argued on MPO-19, not smuggled in here.
18
+ // A category font moves the family and nothing else: it takes its scope's
19
+ // ladder ($body, or $heading for the heading-scope copy) with the same sizes
20
+ // and weights, `true` included (without `true`, $serif and $mono collapsed to
21
+ // the system face at line-height normal, MPO-44). Two departures, both LC-11:
22
+ // leading is floored per category, and tracking is an explicit zero table,
23
+ // because an empty one lets the inherited Inter `--f-letterSpacing-*` through.
74
24
 
75
25
  const TRACKING_A = -0.0223;
76
26
  const TRACKING_B = 0.185;
@@ -113,9 +63,6 @@ function plain<T>(raw: unknown): Record<string, T> {
113
63
  return out;
114
64
  }
115
65
 
116
- /** A tamagui font table's numbers, with Variables and `$` prefixes unwrapped. */
117
- const numbers = (raw: unknown) => plain<number>(raw);
118
-
119
66
  /** Inter dynamic tracking for every key of a size table. */
120
67
  function trackingFor(sizes: Record<string, number>): Record<string, number> {
121
68
  return Object.fromEntries(Object.entries(sizes).map(([key, px]) => [key, interTrackingPx(px)]));
@@ -126,32 +73,29 @@ function neutralTracking(sizes: Record<string, number>): Record<string, number>
126
73
  return Object.fromEntries(Object.keys(sizes).map((key) => [key, 0]));
127
74
  }
128
75
 
129
- const v5Body = defaultConfig.fonts.body;
130
- const v5Heading = defaultConfig.fonts.heading;
131
- const bodySizes = numbers(v5Body.size);
132
- const bodyLineHeights = numbers(v5Body.lineHeight);
133
- const bodyWeights = plain<string>(v5Body.weight);
134
- const headingSizes = numbers(v5Heading.size);
76
+ const bodyWeights = plain<string>(defaultConfig.fonts.body.weight);
77
+ const headingWeights = plain<string>(defaultConfig.fonts.heading.weight);
78
+ // v5 aliases true to 800 although its default size is step 4.
79
+ headingWeights.true = headingWeights["4"]!;
80
+ const defaultTypeTables: ScaleFonts = generateScale(defaultScaleInputs).fonts;
135
81
 
136
82
  /**
137
- * The era's body size table. Exported under its historical name because
138
- * `createDefaultFont` defaults to it, so a consumer-registered category font
139
- * lands on the same ladder as the built-in ones instead of a private copy
140
- * that drifts at the next pin (MPO-19 X8/X9).
83
+ * The default scale's body size table. Exported under its historical name
84
+ * because `createDefaultFont` defaults to it, so a consumer-registered
85
+ * category font lands on the same ladder as the built-in ones instead of a
86
+ * private copy (MPO-19 X8/X9).
141
87
  */
142
- export const bodyFontSizes: Record<string, number> = bodySizes;
88
+ export const bodyFontSizes: Record<string, number> = { ...defaultTypeTables.bodySizes };
143
89
 
144
- /** The era's body leading, before any per-category floor. */
145
- export const bodyFontLineHeights: Record<string, number> = bodyLineHeights;
90
+ /** The default scale's body leading, before any per-category floor. */
91
+ export const bodyFontLineHeights: Record<string, number> = {
92
+ ...defaultTypeTables.bodyLineHeights,
93
+ };
146
94
 
147
- /**
148
- * Inter faces the workspace ships. v5 heading weights are sparse — 600 up to
149
- * $5, 700 at $6–$8, 800 from $9 — and A4's reading of that is the point:
150
- * everything between inherits, so bold is rare and therefore means something.
151
- * Each declared weight needs a face or the browser synthesises one.
152
- */
95
+ /** Shipped Inter faces: 400/500/600/700/800. */
153
96
  const interFaces = {
154
97
  400: { normal: "Inter" },
98
+ 500: { normal: "InterMedium" },
155
99
  600: { normal: "InterSemiBold" },
156
100
  700: { normal: "InterBold" },
157
101
  800: { normal: "InterExtraBold" },
@@ -166,58 +110,71 @@ export const fontFamilyStacks = fontCategoryStacks;
166
110
 
167
111
  export type FontFamilyCategory = FontCategory;
168
112
 
169
- /**
170
- * Leading for one category: the era's body table with the category's LC-11
171
- * floor applied. Every text step comes back as `$body`'s own value; only the
172
- * display steps, where the era's taper eases below the floor, move up.
173
- */
174
- export function categoryLineHeights(category: FontCategory): Record<string, number> {
175
- const minRatio = (fontCategoryMetrics[category] ?? fontCategoryMetrics["sans-serif"]).body;
113
+ /** Scope leading raised to the category's LC-11 floor. */
114
+ export function categoryLineHeights(
115
+ category: FontCategory,
116
+ tables: ScaleFonts = defaultTypeTables,
117
+ scope: "body" | "heading" = "body",
118
+ ): Record<string, number> {
119
+ const minRatio = (fontCategoryMetrics[category] ?? fontCategoryMetrics["sans-serif"])[scope];
120
+ const sizes = scope === "heading" ? tables.headingSizes : tables.bodySizes;
121
+ const lineHeights = scope === "heading" ? tables.headingLineHeights : tables.bodyLineHeights;
176
122
  return Object.fromEntries(
177
- Object.entries(bodySizes).map(([key, px]) => [
123
+ Object.entries(sizes).map(([key, px]) => [
178
124
  key,
179
- flooredLineHeight(px, bodyLineHeights[key] ?? bodyLineHeightPx(px), minRatio),
125
+ flooredLineHeight(px, lineHeights[key] ?? bodyLineHeightPx(px), minRatio),
180
126
  ]),
181
127
  );
182
128
  }
183
129
 
184
- /**
185
- * Category font on the `$body` ladder: same sizes (including `true`), same
186
- * weights, leading floored per LC-11, tracking neutral. A missing `true` key
187
- * is what collapsed `$serif`/`$mono`/`$rounded` to the system face at
188
- * `line-height: normal` (MPO-44).
189
- */
190
- export function createCategoryFont(category: FontCategory, family: string): GenericFont {
130
+ /** Category family on the selected scope's size and weight ladder. */
131
+ export function createCategoryFont(
132
+ category: FontCategory,
133
+ family: string,
134
+ tables: ScaleFonts = defaultTypeTables,
135
+ scope: "body" | "heading" = "body",
136
+ ): GenericFont {
137
+ const sizes = scope === "heading" ? tables.headingSizes : tables.bodySizes;
191
138
  return createFont({
192
139
  family,
193
- size: bodySizes,
194
- lineHeight: categoryLineHeights(category),
195
- weight: bodyWeights,
196
- letterSpacing: neutralTracking(bodySizes),
140
+ size: { ...sizes },
141
+ lineHeight: categoryLineHeights(category, tables, scope),
142
+ weight: scope === "heading" ? headingWeights : bodyWeights,
143
+ letterSpacing: neutralTracking(sizes),
197
144
  }) as GenericFont;
198
145
  }
199
146
 
200
- export const defaultHeadingFont = createInterFont(
201
- {
202
- face: interFaces,
203
- size: headingSizes,
204
- lineHeight: numbers(v5Heading.lineHeight),
205
- weight: plain<string>(v5Heading.weight),
206
- letterSpacing: trackingFor(headingSizes),
207
- },
208
- { sizeSize: (size) => size },
209
- );
147
+ /** Inter `$heading` on a generated scale's heading ladder. */
148
+ export function createHeadingFont(tables: ScaleFonts = defaultTypeTables) {
149
+ return createInterFont(
150
+ {
151
+ face: interFaces,
152
+ size: { ...tables.headingSizes },
153
+ lineHeight: { ...tables.headingLineHeights },
154
+ weight: headingWeights,
155
+ letterSpacing: trackingFor(tables.headingSizes),
156
+ },
157
+ { sizeSize: (size) => size },
158
+ );
159
+ }
210
160
 
211
- export const defaultBodyFont = createInterFont(
212
- {
213
- face: interFaces,
214
- size: bodySizes,
215
- lineHeight: bodyLineHeights,
216
- weight: bodyWeights,
217
- letterSpacing: trackingFor(bodySizes),
218
- },
219
- { sizeSize: (size) => size },
220
- );
161
+ /** Inter `$body` on a generated scale's body ladder. */
162
+ export function createBodyFont(tables: ScaleFonts = defaultTypeTables) {
163
+ return createInterFont(
164
+ {
165
+ face: interFaces,
166
+ size: { ...tables.bodySizes },
167
+ lineHeight: { ...tables.bodyLineHeights },
168
+ weight: bodyWeights,
169
+ letterSpacing: trackingFor(tables.bodySizes),
170
+ },
171
+ { sizeSize: (size) => size },
172
+ );
173
+ }
174
+
175
+ export const defaultHeadingFont = createHeadingFont();
176
+
177
+ export const defaultBodyFont = createBodyFont();
221
178
 
222
179
  export const defaultSerifFont = createCategoryFont("serif", fontFamilyStacks.serif);
223
180
  export const defaultMonoFont = createCategoryFont("mono", fontFamilyStacks.mono);
@@ -236,10 +193,95 @@ export const defaultBlackletterFont = createCategoryFont(
236
193
  );
237
194
  export const defaultGeometricFont = createCategoryFont("geometric", fontFamilyStacks.geometric);
238
195
 
196
+ export const headingCategoryFontKeys = {
197
+ serif: "headingSerif",
198
+ mono: "headingMono",
199
+ slab: "headingSlab",
200
+ rounded: "headingRounded",
201
+ condensed: "headingCondensed",
202
+ cursive: "headingCursive",
203
+ handwriting: "headingHandwriting",
204
+ pixel: "headingPixel",
205
+ blackletter: "headingBlackletter",
206
+ geometric: "headingGeometric",
207
+ } as const satisfies Record<Exclude<FontCategory, "sans-serif">, string>;
208
+
209
+ export const defaultHeadingSerifFont = createCategoryFont(
210
+ "serif",
211
+ fontFamilyStacks.serif,
212
+ defaultTypeTables,
213
+ "heading",
214
+ );
215
+ export const defaultHeadingMonoFont = createCategoryFont(
216
+ "mono",
217
+ fontFamilyStacks.mono,
218
+ defaultTypeTables,
219
+ "heading",
220
+ );
221
+ export const defaultHeadingSlabFont = createCategoryFont(
222
+ "slab",
223
+ fontFamilyStacks.slab,
224
+ defaultTypeTables,
225
+ "heading",
226
+ );
227
+ export const defaultHeadingRoundedFont = createCategoryFont(
228
+ "rounded",
229
+ fontFamilyStacks.rounded,
230
+ defaultTypeTables,
231
+ "heading",
232
+ );
233
+ export const defaultHeadingCondensedFont = createCategoryFont(
234
+ "condensed",
235
+ fontFamilyStacks.condensed,
236
+ defaultTypeTables,
237
+ "heading",
238
+ );
239
+ export const defaultHeadingCursiveFont = createCategoryFont(
240
+ "cursive",
241
+ fontFamilyStacks.cursive,
242
+ defaultTypeTables,
243
+ "heading",
244
+ );
245
+ export const defaultHeadingHandwritingFont = createCategoryFont(
246
+ "handwriting",
247
+ fontFamilyStacks.handwriting,
248
+ defaultTypeTables,
249
+ "heading",
250
+ );
251
+ export const defaultHeadingPixelFont = createCategoryFont(
252
+ "pixel",
253
+ fontFamilyStacks.pixel,
254
+ defaultTypeTables,
255
+ "heading",
256
+ );
257
+ export const defaultHeadingBlackletterFont = createCategoryFont(
258
+ "blackletter",
259
+ fontFamilyStacks.blackletter,
260
+ defaultTypeTables,
261
+ "heading",
262
+ );
263
+ export const defaultHeadingGeometricFont = createCategoryFont(
264
+ "geometric",
265
+ fontFamilyStacks.geometric,
266
+ defaultTypeTables,
267
+ "heading",
268
+ );
269
+
239
270
  /** All purpose + category fonts registered by `createDefaultThemeConfig`. */
240
271
  export const defaultFonts = {
241
272
  heading: defaultHeadingFont,
242
273
  body: defaultBodyFont,
274
+ headingSerif: defaultHeadingSerifFont,
275
+ headingMono: defaultHeadingMonoFont,
276
+ headingSlab: defaultHeadingSlabFont,
277
+ headingRounded: defaultHeadingRoundedFont,
278
+ headingCondensed: defaultHeadingCondensedFont,
279
+ headingCursive: defaultHeadingCursiveFont,
280
+ headingHandwriting: defaultHeadingHandwritingFont,
281
+ headingPixel: defaultHeadingPixelFont,
282
+ headingBlackletter: defaultHeadingBlackletterFont,
283
+ headingGeometric: defaultHeadingGeometricFont,
284
+
243
285
  serif: defaultSerifFont,
244
286
  mono: defaultMonoFont,
245
287
  slab: defaultSlabFont,
@@ -0,0 +1,48 @@
1
+ import type { BaseThemeBuilderDefinition, ThemeBuilderDefinition } from "../createThemes";
2
+ import { defaultBaseTheme } from "./base";
3
+
4
+ /**
5
+ * Optional: pass createDefaultThemeConfig({ base: houseBaseTheme, accent: houseAccentTheme }).
6
+ * These are not the default (Clay 2026-10-22).
7
+ */
8
+ export const houseBaseTheme: BaseThemeBuilderDefinition = {
9
+ lightPalette: [100, 97, 93, 88, 83, 76, 66, 52, 40, 30, 20, 8].map(
10
+ (lightness) => `hsl(270, 6%, ${lightness}%)`,
11
+ ),
12
+ darkPalette: [4, 8, 11, 15, 20, 27, 38, 47, 56, 66, 80, 95].map(
13
+ (lightness) => `hsl(270, 6%, ${lightness}%)`,
14
+ ),
15
+ lightShadows: defaultBaseTheme.lightShadows,
16
+ darkShadows: defaultBaseTheme.darkShadows,
17
+ };
18
+
19
+ export const houseAccentTheme: ThemeBuilderDefinition = {
20
+ darkPalette: [
21
+ "hsla(250, 50%, 35%, 1)",
22
+ "hsla(250, 50%, 38%, 1)",
23
+ "hsla(250, 50%, 41%, 1)",
24
+ "hsla(250, 50%, 43%, 1)",
25
+ "hsla(250, 50%, 46%, 1)",
26
+ "hsla(250, 50%, 49%, 1)",
27
+ "hsla(250, 50%, 52%, 1)",
28
+ "hsla(250, 50%, 54%, 1)",
29
+ "hsla(250, 50%, 57%, 1)",
30
+ "hsla(250, 50%, 60%, 1)",
31
+ "hsla(250, 50%, 90%, 1)",
32
+ "hsla(250, 50%, 95%, 1)",
33
+ ],
34
+ lightPalette: [
35
+ "hsla(250, 50%, 62%, 1)",
36
+ "hsla(250, 50%, 58%, 1)",
37
+ "hsla(250, 50%, 54%, 1)",
38
+ "hsla(250, 50%, 48%, 1)",
39
+ "hsla(250, 50%, 46%, 1)",
40
+ "hsla(250, 50%, 44%, 1)",
41
+ "hsla(250, 50%, 42%, 1)",
42
+ "hsla(250, 50%, 40%, 1)",
43
+ "hsla(250, 50%, 38%, 1)",
44
+ "hsla(250, 50%, 36%, 1)",
45
+ "hsla(250, 50%, 95%, 1)",
46
+ "hsla(250, 50%, 98%, 1)",
47
+ ],
48
+ };
@@ -3,3 +3,4 @@ export * from "./base";
3
3
  export * from "./builderOptions";
4
4
  export * from "./categoryFonts";
5
5
  export * from "./fonts";
6
+ export * from "./house";
@@ -1,37 +1,12 @@
1
- /**
2
- * MPO-95, ruled C by Clay 2026-09-26: mpo's neutral ramp is its own hue-270
3
- * ramp, signed off as a deliberate divergence from tamagui.dev's achromatic
4
- * grays. This pins the design (the stops in `base.ts`), what the builder makes
5
- * of it (it quantizes each stop to integer hsla, so 56.5% renders as 56%), and
6
- * the file the gallery boards and their verifiers read. Changing the ramp is a
7
- * new ruling: update the rulebook, the JSON and the gallery token block with it.
8
- */
9
-
10
1
  import { readFileSync } from "node:fs";
11
2
  import { join } from "node:path";
3
+ import { defaultLightPalette, defaultDarkPalette, themes as stockThemes } from "@tamagui/themes/v5";
12
4
  import { describe, expect, it } from "vitest";
13
5
  import { createThemesBuilder } from "../createThemes";
14
6
  import { defaultAccentTheme } from "./accent";
15
7
  import { defaultBaseTheme } from "./base";
16
8
  import { defaultBuilderOptions } from "./builderOptions";
17
-
18
- const signedOffLightness = {
19
- light: [99.5, 98.2, 95.2, 92.5, 90, 87, 82.5, 75, 56.5, 46, 40, 14],
20
- dark: [8.7, 10.5, 13.5, 16, 18.5, 21.5, 25.5, 32, 43, 54, 62.5, 94],
21
- } as const;
22
-
23
- /**
24
- * Read off the rendered FileUpload dropzone (Storybook, Chromium, DPR 2) when
25
- * MPO-95 was filed. These are what a user sees; everything else is derived.
26
- */
27
- const measured = [
28
- ["light", 3, "rgb(243, 242, 243)"],
29
- ["light", 6, "rgb(222, 220, 224)"],
30
- ["light", 9, "rgb(143, 136, 150)"],
31
- ["dark", 9, "rgb(110, 103, 116)"],
32
- ["light", 12, "rgb(36, 34, 38)"],
33
- ["dark", 12, "rgb(240, 239, 241)"],
34
- ] as const;
9
+ import { houseBaseTheme, houseAccentTheme } from "./house";
35
10
 
36
11
  type Scheme = "light" | "dark";
37
12
  type Themes = Record<string, Record<string, string>>;
@@ -76,32 +51,62 @@ const toHex = (value: string) =>
76
51
  const builtRamp = (scheme: Scheme) =>
77
52
  Array.from({ length: 12 }, (_, i) => built[scheme][`color${i + 1}`]);
78
53
 
79
- describe("MPO-95: the neutral ramp is mpo's hue-270 ramp", () => {
80
- it("base.ts declares the signed-off stops: hue 270, 6% saturation, twelve lightnesses", () => {
81
- expect(defaultBaseTheme.lightPalette).toEqual(
82
- signedOffLightness.light.map((l) => `hsla(270, 6%, ${l}%, 1)`),
83
- );
84
- expect(defaultBaseTheme.darkPalette).toEqual(
85
- signedOffLightness.dark.map((l) => `hsla(270, 6%, ${l}%, 1)`),
86
- );
54
+ describe("the default neutral ramp is stock Tamagui v5 (Clay 2026-10-22)", () => {
55
+ it("declares the v5 base palettes and their inversion as the default accent", () => {
56
+ expect(defaultBaseTheme.lightPalette).toEqual(defaultLightPalette);
57
+ expect(defaultBaseTheme.darkPalette).toEqual(defaultDarkPalette);
58
+ expect(defaultAccentTheme.lightPalette).toEqual(defaultDarkPalette);
59
+ expect(defaultAccentTheme.darkPalette).toEqual(defaultLightPalette);
87
60
  });
88
61
 
89
- for (const [scheme, step, rgb] of measured) {
90
- it(`${scheme} $color${step} builds to the measured ${rgb}`, () => {
91
- expect(`rgb(${hslaToRgb(built[scheme][`color${step}`]).join(", ")})`).toBe(rgb);
62
+ for (const scheme of ["light", "dark"] as const) {
63
+ it(`builds the stock v5 ${scheme} ramp step for step`, () => {
64
+ expect(builtRamp(scheme)).toEqual(
65
+ Array.from(
66
+ { length: 12 },
67
+ (_, i) => stockThemes[scheme][`color${i + 1}` as keyof typeof stockThemes.light],
68
+ ),
69
+ );
92
70
  });
93
- }
94
71
 
95
- for (const scheme of ["light", "dark"] as const) {
96
- it(`the built ${scheme} ramp is mpo-neutral-ramp.json, step for step`, () => {
72
+ it(`the built ${scheme} ramp equals the gallery JSON`, () => {
97
73
  expect(builtRamp(scheme).map(toHex)).toEqual(rampFile[scheme]);
98
74
  });
75
+
76
+ it(`every ${scheme} step is achromatic`, () => {
77
+ for (const value of builtRamp(scheme)) {
78
+ const [r, g, b] = hslaToRgb(value);
79
+ expect(r).toBe(g);
80
+ expect(g).toBe(b);
81
+ }
82
+ });
99
83
  }
84
+ });
100
85
 
101
- it("the tint survives the builder's rounding: only light $color1 lands on pure white", () => {
102
- const achromatic = (hex: string) =>
103
- new Set([hex.slice(1, 3), hex.slice(3, 5), hex.slice(5, 7)]).size === 1;
104
- expect(rampFile.light.filter(achromatic)).toEqual(["#ffffff"]);
105
- expect(rampFile.dark.filter(achromatic)).toEqual([]);
106
- });
86
+ describe("the optional house palette", () => {
87
+ const house = createThemesBuilder(
88
+ houseBaseTheme,
89
+ houseAccentTheme,
90
+ defaultBuilderOptions,
91
+ ).themes();
92
+ const lightnesses = {
93
+ light: [100, 97, 93, 88, 83, 76, 66, 52, 40, 30, 20, 8],
94
+ dark: [4, 8, 11, 15, 20, 27, 38, 47, 56, 66, 80, 95],
95
+ };
96
+ for (const scheme of ["light", "dark"] as const) {
97
+ it(`declares the optional ${scheme} integer HSL stops`, () => {
98
+ expect(houseBaseTheme[`${scheme}Palette`]).toEqual(
99
+ lightnesses[scheme].map((l) => `hsl(270, 6%, ${l}%)`),
100
+ );
101
+ });
102
+
103
+ it(`round-trips each ${scheme} stop without RGB drift`, () => {
104
+ const source = houseBaseTheme[`${scheme}Palette`];
105
+ for (const [i, declared] of source.entries()) {
106
+ expect(hslaToRgb(house[scheme][`color${i + 1}`]), `color${i + 1}`).toEqual(
107
+ hslaToRgb(declared),
108
+ );
109
+ }
110
+ });
111
+ }
107
112
  });
@@ -80,12 +80,12 @@ function fallbackGradient(): string[] {
80
80
 
81
81
  function NumberLineRow({
82
82
  colorFor,
83
- compact,
83
+ halfHeight,
84
84
  }: {
85
85
  colorFor: (step: number) => string;
86
- compact?: boolean;
86
+ halfHeight?: boolean;
87
87
  }) {
88
- const h = compact ? Math.round(cellSize / 2) : cellSize;
88
+ const h = halfHeight ? Math.round(cellSize / 2) : cellSize;
89
89
  return (
90
90
  <div style={{ display: "flex", gap: 0 }}>
91
91
  {Array.from({ length: 12 }, (_, i) => {
@@ -103,7 +103,7 @@ function NumberLineRow({
103
103
  display: "flex",
104
104
  alignItems: "center",
105
105
  justifyContent: "center",
106
- fontSize: compact ? 9 : 11,
106
+ fontSize: halfHeight ? 9 : 11,
107
107
  fontWeight: 700,
108
108
  }}
109
109
  title={`$color${step}`}
@@ -238,7 +238,7 @@ export function ColorLineVisualizer({
238
238
  <div style={styles.title as CSSProperties}>Theme</div>
239
239
  <SemanticBrackets />
240
240
  <NumberLineRow colorFor={colorFor} />
241
- {oppositeColors && <NumberLineRow colorFor={toColorFn(oppositeColors)} compact />}
241
+ {oppositeColors && <NumberLineRow colorFor={toColorFn(oppositeColors)} halfHeight />}
242
242
  {accentColors && (
243
243
  <>
244
244
  <div
@@ -252,7 +252,7 @@ export function ColorLineVisualizer({
252
252
  </div>
253
253
  <NumberLineRow colorFor={toColorFn(accentColors)} />
254
254
  {oppositeAccentColors && (
255
- <NumberLineRow colorFor={toColorFn(oppositeAccentColors)} compact />
255
+ <NumberLineRow colorFor={toColorFn(oppositeAccentColors)} halfHeight />
256
256
  )}
257
257
  </>
258
258
  )}
@@ -50,7 +50,7 @@ describe("ThemeDevtoolsPanel set markers survive a remount (MPO-463)", () => {
50
50
  render(<Shell scheme="light" />);
51
51
  expect(pressed("Fill", "outlined")).toBe("true");
52
52
  expect(pressed("Fill", "unset (inherit)")).toBe("false");
53
- expect(pressed("Size", "unset (inherit)")).toBe("true");
53
+ expect(pressed("Elevation", "unset (inherit)")).toBe("true");
54
54
  expect(pressed("Radius", "unset (inherit)")).toBe("true");
55
55
  });
56
56
 
@@ -67,16 +67,16 @@ describe("ThemeDevtoolsPanel set markers survive a remount (MPO-463)", () => {
67
67
  it("a preset's own values read as inherited, only the knobs over it as set", () => {
68
68
  registerPreset("mpo463", {
69
69
  theme: "gray" as ThemeName,
70
- knobs: { ...defaultKnobs, fillStyle: "outlined", size: "small" },
70
+ knobs: { ...defaultKnobs, fillStyle: "outlined", elevation: "none" },
71
71
  intents: {},
72
72
  tints: [],
73
73
  });
74
74
  document.cookie = `${cookiePreset}=mpo463; path=/`;
75
- persistPresetOverrides({ size: "large" });
75
+ persistPresetOverrides({ elevation: "large" });
76
76
  render(<Shell scheme="light" />);
77
77
  expect(pressed("Fill", "unset (inherit)")).toBe("true");
78
- expect(pressed("Size", "large")).toBe("true");
79
- expect(pressed("Size", "unset (inherit)")).toBe("false");
78
+ expect(pressed("Elevation", "large")).toBe("true");
79
+ expect(pressed("Elevation", "unset (inherit)")).toBe("false");
80
80
  });
81
81
 
82
82
  it("a knob never set shows unset after a remount", () => {
@@ -84,8 +84,15 @@ describe("ThemeDevtoolsPanel set markers survive a remount (MPO-463)", () => {
84
84
  fireEvent.click(within(screen.getByRole("group", { name: "Fill" })).getByTitle("outlined"));
85
85
 
86
86
  shell.rerender(<Shell scheme="dark" />);
87
- for (const label of ["Size", "Space", "Radius", "Border", "Elevation"]) {
87
+ for (const label of ["Radius", "Border", "Elevation"]) {
88
88
  expect(pressed(label, "unset (inherit)"), label).toBe("true");
89
89
  }
90
90
  });
91
+
92
+ it("has no Size, Space or Density row: those come from the preset's scale", () => {
93
+ render(<Shell scheme="light" />);
94
+ for (const label of ["Size", "Space", "Density"]) {
95
+ expect(screen.queryByRole("group", { name: label }), label).toBeNull();
96
+ }
97
+ });
91
98
  });