@multiplatform.one/theme 7.4.0 → 7.5.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 (106) hide show
  1. package/README.md +2 -1
  2. package/package.json +5 -5
  3. package/src/figma/figmaTokens.spec.ts +25 -21
  4. package/src/figma/figmaTokens.ts +2 -2
  5. package/src/font.ts +6 -19
  6. package/src/keyboardFocusRing.ts +12 -6
  7. package/src/theme/FontKnobStyles.tsx +5 -3
  8. package/src/theme/Tint.tsx +3 -2
  9. package/src/theme/animations/css.ts +14 -1
  10. package/src/theme/colorRules.spec.ts +233 -2
  11. package/src/theme/colorRules.ts +118 -0
  12. package/src/theme/cornerSmoothing.native.ts +39 -2
  13. package/src/theme/cornerSmoothing.spec.ts +213 -3
  14. package/src/theme/cornerSmoothing.ts +132 -10
  15. package/src/theme/createDefaultThemeConfig.spec.ts +107 -1
  16. package/src/theme/createDefaultThemeConfig.ts +27 -8
  17. package/src/theme/defaults/builderOptions.ts +23 -10
  18. package/src/theme/defaults/categoryFonts.ts +21 -0
  19. package/src/theme/defaults/fonts.spec.ts +93 -10
  20. package/src/theme/defaults/fonts.theme.spec.tsx +128 -0
  21. package/src/theme/defaults/fonts.ts +155 -27
  22. package/src/theme/defaults/index.ts +1 -0
  23. package/src/theme/devtools/ThemeDevtoolsPanel.tsx +0 -11
  24. package/src/theme/focusState.spec.ts +110 -1
  25. package/src/theme/focusState.ts +32 -7
  26. package/src/theme/fontCategoryStacks.ts +24 -0
  27. package/src/theme/fontKnobStyles.spec.ts +15 -2
  28. package/src/theme/glyphPaint.spec.ts +26 -0
  29. package/src/theme/glyphPaint.ts +46 -0
  30. package/src/theme/index.ts +3 -0
  31. package/src/theme/knobs.ts +18 -2
  32. package/src/theme/layoutTokens.spec.ts +3 -1
  33. package/src/theme/layoutTokens.ts +47 -4
  34. package/src/theme/mpo13Proof.spec.ts +3 -3
  35. package/src/theme/mpo19Proof.spec.ts +285 -0
  36. package/src/theme/presets.ts +1 -1
  37. package/src/theme/radiusClass.spec.ts +394 -0
  38. package/src/theme/radiusClass.ts +203 -4
  39. package/src/theme/radiusClassTable.generated.ts +121 -0
  40. package/src/theme/recipeInputs.ts +223 -36
  41. package/src/theme/recipes.ts +68 -4
  42. package/src/theme/resolveKnobs.spec.ts +139 -11
  43. package/src/theme/resolveKnobs.ts +190 -25
  44. package/src/theme/sizeLadder.spec.ts +84 -0
  45. package/src/theme/sizeRecipes.spec.ts +19 -6
  46. package/src/theme/sizeRecipes.ts +43 -13
  47. package/src/theme/theme.native.tsx +2 -0
  48. package/src/theme/theme.provider.spec.ts +9 -0
  49. package/src/theme/theme.tsx +6 -3
  50. package/src/theme/tint.spec.tsx +36 -35
  51. package/src/theme/useResolvedKnobs.ts +6 -3
  52. package/types/figma/figmaTokens.d.ts +1 -1
  53. package/types/figma/figmaTokens.d.ts.map +1 -1
  54. package/types/font.d.ts.map +1 -1
  55. package/types/keyboardFocusRing.d.ts +11 -5
  56. package/types/keyboardFocusRing.d.ts.map +1 -1
  57. package/types/theme/FontKnobStyles.d.ts +2 -1
  58. package/types/theme/FontKnobStyles.d.ts.map +1 -1
  59. package/types/theme/Tint.d.ts +2 -1
  60. package/types/theme/Tint.d.ts.map +1 -1
  61. package/types/theme/animations/css.d.ts +15 -2
  62. package/types/theme/animations/css.d.ts.map +1 -1
  63. package/types/theme/colorRules.d.ts +59 -1
  64. package/types/theme/colorRules.d.ts.map +1 -1
  65. package/types/theme/cornerSmoothing.d.ts +42 -20
  66. package/types/theme/cornerSmoothing.d.ts.map +1 -1
  67. package/types/theme/cornerSmoothing.native.d.ts +22 -2
  68. package/types/theme/cornerSmoothing.native.d.ts.map +1 -1
  69. package/types/theme/createDefaultThemeConfig.d.ts +21 -3
  70. package/types/theme/createDefaultThemeConfig.d.ts.map +1 -1
  71. package/types/theme/defaults/builderOptions.d.ts.map +1 -1
  72. package/types/theme/defaults/categoryFonts.d.ts +19 -0
  73. package/types/theme/defaults/categoryFonts.d.ts.map +1 -0
  74. package/types/theme/defaults/fonts.d.ts +135 -2
  75. package/types/theme/defaults/fonts.d.ts.map +1 -1
  76. package/types/theme/defaults/index.d.ts +1 -0
  77. package/types/theme/defaults/index.d.ts.map +1 -1
  78. package/types/theme/devtools/ThemeDevtoolsPanel.d.ts.map +1 -1
  79. package/types/theme/focusState.d.ts +32 -6
  80. package/types/theme/focusState.d.ts.map +1 -1
  81. package/types/theme/fontCategoryStacks.d.ts +11 -0
  82. package/types/theme/fontCategoryStacks.d.ts.map +1 -0
  83. package/types/theme/glyphPaint.d.ts +16 -0
  84. package/types/theme/glyphPaint.d.ts.map +1 -0
  85. package/types/theme/index.d.ts +3 -0
  86. package/types/theme/index.d.ts.map +1 -1
  87. package/types/theme/knobs.d.ts +10 -2
  88. package/types/theme/knobs.d.ts.map +1 -1
  89. package/types/theme/layoutTokens.d.ts +46 -4
  90. package/types/theme/layoutTokens.d.ts.map +1 -1
  91. package/types/theme/radiusClass.d.ts +96 -4
  92. package/types/theme/radiusClass.d.ts.map +1 -1
  93. package/types/theme/radiusClassTable.generated.d.ts +146 -0
  94. package/types/theme/radiusClassTable.generated.d.ts.map +1 -0
  95. package/types/theme/recipeInputs.d.ts +85 -14
  96. package/types/theme/recipeInputs.d.ts.map +1 -1
  97. package/types/theme/recipes.d.ts +78 -4
  98. package/types/theme/recipes.d.ts.map +1 -1
  99. package/types/theme/resolveKnobs.d.ts +12 -1
  100. package/types/theme/resolveKnobs.d.ts.map +1 -1
  101. package/types/theme/sizeRecipes.d.ts +27 -14
  102. package/types/theme/sizeRecipes.d.ts.map +1 -1
  103. package/types/theme/theme.d.ts.map +1 -1
  104. package/types/theme/theme.native.d.ts.map +1 -1
  105. package/types/theme/useResolvedKnobs.d.ts +2 -2
  106. package/types/theme/useResolvedKnobs.d.ts.map +1 -1
@@ -7,6 +7,7 @@ import {
7
7
  SIZE_RECIPE_COMPACT_RATIOS,
8
8
  SIZE_RECIPE_HEIGHTS,
9
9
  SIZE_RECIPE_HEIGHTS_TOUCH,
10
+ SIZE_RECIPE_RADII,
10
11
  SIZE_RECIPE_RATIOS,
11
12
  applyTouchFloor,
12
13
  defaultRecipeInputs,
@@ -22,6 +23,7 @@ import {
22
23
  sizeRecipeIconVariants,
23
24
  sizeRecipes,
24
25
  sizeRecipeTypeVariants,
26
+ textFieldInset,
25
27
  type SizeRecipe,
26
28
  } from "./sizeRecipes";
27
29
 
@@ -75,10 +77,9 @@ describe("size recipes", () => {
75
77
  }
76
78
  });
77
79
 
78
- it("static variants match sizeRecipeFromHeight for every token", () => {
80
+ it("static variants match the generated table for every token", () => {
79
81
  for (const token of RECIPE_KEYS) {
80
- const recipe = sizeRecipeFromHeight(SIZE_RECIPE_HEIGHTS[token]);
81
- expect(sizeRecipes[token]).toEqual(recipe);
82
+ const recipe = sizeRecipes[token];
82
83
  expect(sizeRecipeBoxVariants[token]).toEqual({
83
84
  height: recipe.height,
84
85
  paddingHorizontal: recipe.paddingHorizontal,
@@ -90,6 +91,7 @@ describe("size recipes", () => {
90
91
  height: recipe.iconSize,
91
92
  });
92
93
  }
94
+ expect(sizeRecipeFromHeight(44)).toEqual(sizeRecipes.$4);
93
95
  });
94
96
 
95
97
  it("sizeRecipeForToken falls back to $4", () => {
@@ -230,7 +232,7 @@ describe("generateSizeRecipes (LC-70)", () => {
230
232
  expect(generated.families.control.recipes.$4.height).toBe(44);
231
233
  expect(generated.families.controlCompact.recipes.$4.height).toBe(44);
232
234
  expect(generated.families.control.recipes.$4.paddingHorizontal).toBe(18);
233
- expect(generated.families.controlCompact.recipes.$4.paddingHorizontal).toBe(13);
235
+ expect(generated.families.controlCompact.recipes.$4.paddingHorizontal).toBe(16);
234
236
  expect(generated.families.controlCompact.recipes.$4.paddingHorizontal).toBeLessThan(
235
237
  generated.families.control.recipes.$4.paddingHorizontal,
236
238
  );
@@ -260,12 +262,23 @@ describe("generateSizeRecipes (LC-70)", () => {
260
262
  expect(sizeRecipeTypeVariants).toBe(recipeFamilies.control.typeVariants);
261
263
  expect(sizeRecipeIconVariants).toBe(recipeFamilies.control.iconVariants);
262
264
  expect(SIZE_RECIPE_HEIGHTS).toBe(recipeFamilies.control.heights);
265
+ expect(SIZE_RECIPE_RADII).toBe(recipeFamilies.control.radii);
263
266
  });
264
267
 
265
268
  it("sizeRecipeEscape is identity", () => {
266
269
  expect(sizeRecipeEscape(32)).toBe(32);
267
270
  expect(sizeRecipeEscape("keep")).toBe("keep");
268
271
  });
272
+
273
+ it("textFieldInset is min(padX, floor((height-fontSize)/2)) — $4 → 15 (LC-84)", () => {
274
+ expect(textFieldInset(sizeRecipes.$4)).toBe(15);
275
+ expect(textFieldInset({ height: 44, paddingHorizontal: 18, fontSize: 14 })).toBe(15);
276
+ expect(textFieldInset({ height: 32, paddingHorizontal: 12, fontSize: 14 })).toBe(9);
277
+ expect(textFieldInset({ height: 48, paddingHorizontal: 8, fontSize: 14 })).toBe(8);
278
+ expect(textFieldInset(sizeRecipes.$4)).toBeLessThanOrEqual(
279
+ Math.floor((sizeRecipes.$4.height - sizeRecipes.$4.fontSize) / 2),
280
+ );
281
+ });
269
282
  });
270
283
 
271
284
  describe("size recipe families", () => {
@@ -291,8 +304,8 @@ describe("size recipe families", () => {
291
304
  expect(compact.boxVariants.$4.paddingHorizontal).toBeLessThan(
292
305
  control.boxVariants.$4.paddingHorizontal,
293
306
  );
294
- expect(compact.boxVariants.$4.paddingHorizontal).toBe(13);
295
- expect(compact.boxVariants.$4.gap).toBe(7);
307
+ expect(compact.boxVariants.$4.paddingHorizontal).toBe(16);
308
+ expect(compact.boxVariants.$4.gap).toBe(9);
296
309
  expect(sizeRecipeForToken("$4", { family: "controlCompact" })).toEqual(compact.recipes.$4);
297
310
  });
298
311
  });
@@ -1,9 +1,11 @@
1
1
  /**
2
- * Coordinated control-size recipes (LC-65 SIZE-RECIPE, LC-70 RECIPE-INPUTS).
2
+ * Coordinated control-size recipes (LC-75 SIZE-RECIPE, LC-80 RECIPE-INPUTS).
3
3
  *
4
- * A flat size ramp cannot encode relationships: padding, font, and icon
5
- * must stay in ratio with height. Radius is excluded — it stays on the
6
- * borderRadius knob and must never scale with size.
4
+ * The default ladder is the measured Tamagui v5 table in recipeInputs
5
+ * (padX / font / icon / radius per size token). Gap equals radius at
6
+ * every size and at every borderRadius knob stop (`applyRadiusStop`).
7
+ * A linear ratio is only the `{ baseHeight }` fallback — not a
8
+ * documented divergence (MPO-46 / R6).
7
9
  *
8
10
  * Tables are GENERATED from `defaultRecipeInputs` at module eval (Tamagui
9
11
  * 2.0.0-rc.41 flattens `styled(View, { variants: { size: tables } })` when
@@ -13,25 +15,30 @@
13
15
  * reserve it for non-enumerable values only. Do NOT put recipe tables on a
14
16
  * `createStyledContext` primitive (`neverFlatten`).
15
17
  *
16
- * Reference md ($4 / $true): height 44, padX 18, font 14, icon 16, gap 9
17
- * (the measured tamagui.dev $4 control — MPO-13).
18
+ * Reference md ($4 / $true): height 44, padX 18, font 14, icon 16, radius 9,
19
+ * gap 9 (the measured tamagui.dev $4 control — MPO-13 / MPO-19 X1/X2).
18
20
  * `sizeToken` remains the recipe KEY (string "$4") so styled() variants
19
21
  * can switch; it is not a height scalar.
20
22
  *
21
- * Families: `control` uses `inputs.ratios`. `controlCompact` uses
22
- * `compactRatios` at the SAME heights (tighter pad/font/icon/gap).
23
+ * Families: `control` reads `inputs.tokenTable`. `controlCompact` reads
24
+ * `compactTokenTable` at the SAME heights (Input padX, tighter type).
23
25
  *
24
- * On touch (LC-67), `applyTouchFloor` / `sizeRecipeForToken(token, { touch: true })`
25
- * lift painted height to ≥ 44 while pad/font/icon stay in ratio. Static
26
- * `sizeRecipe*Variants` stay on the desktop table — runtime fragments
27
- * carry the floor; consumers spread `knobProps.control` after `size`.
26
+ * On touch (LC-77), `applyTouchFloor` / `sizeRecipeForToken(token, { touch: true })`
27
+ * lift painted height to ≥ 44. Static `sizeRecipe*Variants` stay on the
28
+ * desktop table — runtime fragments carry the floor; consumers spread
29
+ * `knobProps.control` after `size`.
28
30
  */
29
31
 
30
32
  export {
31
33
  DEFAULT_SIZE_RECIPE_INPUTS,
34
+ SIZE_RECIPE_RADIUS_STOPS,
32
35
  SIZE_RECIPE_TOKENS,
33
36
  SIZE_TOKEN_EXPONENTS,
37
+ TAMAGUI_COMPACT_SIZE_TABLE,
38
+ TAMAGUI_CONTROL_SIZE_TABLE,
39
+ TAMAGUI_INPUT_PADX,
34
40
  TOUCH_HEIGHT_FLOOR,
41
+ applyRadiusStop,
35
42
  cloneSizeRecipeInputs,
36
43
  createRecipeFamilies,
37
44
  defaultRecipeInputs,
@@ -39,6 +46,7 @@ export {
39
46
  generateSizeRecipeTables,
40
47
  generateSizeRecipes,
41
48
  mergeRecipeInputs,
49
+ recipeFromTokenChannels,
42
50
  resolveSizeRecipeInputs,
43
51
  } from "./recipeInputs";
44
52
  export type {
@@ -52,13 +60,17 @@ export type {
52
60
  SizeRecipeFamilyName,
53
61
  SizeRecipeIconVariant,
54
62
  SizeRecipeInputs,
63
+ SizeRecipeRadiusStop,
55
64
  SizeRecipeRatios,
56
65
  SizeRecipeTables,
57
66
  SizeRecipeToken,
67
+ SizeRecipeTokenChannels,
68
+ SizeRecipeTokenTable,
58
69
  SizeRecipeTypeVariant,
59
70
  } from "./recipeInputs";
60
71
 
61
72
  import {
73
+ cloneSizeRecipeInputs,
62
74
  defaultRecipeInputs,
63
75
  generateSizeRecipes,
64
76
  sizeRecipeFromHeight as recipeFromHeight,
@@ -88,6 +100,7 @@ function adoptFamily(target: SizeRecipeFamily, source: SizeRecipeFamily): void {
88
100
  replaceRecord(target.typeVariants, source.typeVariants);
89
101
  replaceRecord(target.iconVariants, source.iconVariants);
90
102
  replaceRecord(target.heightVariants, source.heightVariants);
103
+ replaceRecord(target.radii, source.radii);
91
104
  }
92
105
 
93
106
  const initial = generateSizeRecipes(defaultRecipeInputs);
@@ -106,6 +119,7 @@ export const sizeRecipeTypeVariants = recipeFamilies.control.typeVariants;
106
119
  export const sizeRecipeIconVariants = recipeFamilies.control.iconVariants;
107
120
  export const SIZE_RECIPE_HEIGHTS = recipeFamilies.control.heights;
108
121
  export const SIZE_RECIPE_HEIGHTS_TOUCH = recipeFamilies.control.touchHeights;
122
+ export const SIZE_RECIPE_RADII = recipeFamilies.control.radii;
109
123
  export const SIZE_RECIPE_RATIOS = recipeFamilies.control.ratios;
110
124
  export const SIZE_RECIPE_COMPACT_RATIOS = recipeFamilies.controlCompact.ratios;
111
125
 
@@ -139,6 +153,7 @@ function adoptGenerated(next: GeneratedSizeRecipes): GeneratedSizeRecipes {
139
153
  boxVariants: sizeRecipeBoxVariants,
140
154
  typeVariants: sizeRecipeTypeVariants,
141
155
  iconVariants: sizeRecipeIconVariants,
156
+ radii: SIZE_RECIPE_RADII,
142
157
  families: recipeFamilies,
143
158
  };
144
159
  return current;
@@ -150,7 +165,7 @@ export function setSizeRecipeInputs(partial: Partial<RecipeInputs> = {}): Genera
150
165
  }
151
166
 
152
167
  export function getSizeRecipeInputs(): RecipeInputs {
153
- return { ...current.inputs, ratios: { ...current.inputs.ratios } };
168
+ return cloneSizeRecipeInputs(current.inputs);
154
169
  }
155
170
 
156
171
  export function getGeneratedSizeRecipes(): GeneratedSizeRecipes {
@@ -210,3 +225,18 @@ export function sizeRecipeForToken(
210
225
  export function sizeRecipeEscape<T>(value: T): T {
211
226
  return value;
212
227
  }
228
+
229
+ /**
230
+ * LC-84 TEXT-INSET — optical vertical inset of a text field, never larger
231
+ * than the recipe padX. Extra horizontal space is legal only when an icon
232
+ * well occupies that side, not as empty-field padX.
233
+ *
234
+ * min(paddingHorizontal, floor((height − fontSize) / 2))
235
+ *
236
+ * `$4` (44 / 18 / 14) → 15. Button ladders keep recipe.paddingHorizontal (18).
237
+ */
238
+ export function textFieldInset(
239
+ recipe: Pick<SizeRecipe, "height" | "paddingHorizontal" | "fontSize">,
240
+ ): number {
241
+ return Math.min(recipe.paddingHorizontal, Math.floor((recipe.height - recipe.fontSize) / 2));
242
+ }
@@ -2,6 +2,7 @@ import { ToastProvider } from "@tamagui/toast";
2
2
  import { useContext, useMemo } from "react";
3
3
  import { type CreateTamaguiProps, TamaguiProvider, type TamaguiProviderProps } from "tamagui";
4
4
  import type { PropsWithChildren } from "react";
5
+ import { CornerSmoothingStyles } from "./cornerSmoothing";
5
6
  import { FontKnobStyles } from "./FontKnobStyles";
6
7
  import { PresetContext, type PresetContextValue } from "./PresetContext";
7
8
  import { defaultPreset } from "./presets";
@@ -61,6 +62,7 @@ function KnobBridge({ children }: PropsWithChildren) {
61
62
  const tree = (
62
63
  <>
63
64
  <FontKnobStyles />
65
+ <CornerSmoothingStyles />
64
66
  {children}
65
67
  </>
66
68
  );
@@ -15,6 +15,15 @@ describe("ThemeProvider knob injectors", () => {
15
15
  expect(bridge).toContain("<FontKnobStyles");
16
16
  });
17
17
 
18
+ it("mounts CornerSmoothingStyles inside KnobBridge so the squircle sheet is not tree-shaken", () => {
19
+ const src = readFileSync(join(here, "theme.tsx"), "utf8");
20
+ expect(src).toMatch(/import \{ CornerSmoothingStyles \} from "\.\/cornerSmoothing"/);
21
+ const bridge = src.slice(src.indexOf("function KnobBridge"));
22
+ expect(bridge).toContain("<CornerSmoothingStyles");
23
+ const native = readFileSync(join(here, "theme.native.tsx"), "utf8");
24
+ expect(native).toContain("<CornerSmoothingStyles");
25
+ });
26
+
18
27
  it("does not invent a second knob store — cookies still feed PresetContext", () => {
19
28
  const src = readFileSync(join(here, "theme.tsx"), "utf8");
20
29
  expect(src).toContain("readPresetCookie");
@@ -5,6 +5,7 @@ import { type CreateTamaguiProps, TamaguiProvider, type TamaguiProviderProps } f
5
5
  import type { PropsWithChildren } from "react";
6
6
  import { animationDurations } from "./animations/css";
7
7
  import { getCookieWatchList, readOverridesCookie, readPresetCookie } from "./cookies";
8
+ import { CornerSmoothingStyles } from "./cornerSmoothing";
8
9
  import { FontKnobStyles } from "./FontKnobStyles";
9
10
  import { PresetContext, type PresetContextValue } from "./PresetContext";
10
11
  import { defaultPreset } from "./presets";
@@ -157,9 +158,10 @@ export function ThemeProvider<T extends CreateTamaguiProps>({
157
158
  * If an ancestor PresetContext already exists (e.g. from a Storybook decorator),
158
159
  * the bridge is a pass-through to avoid overriding it.
159
160
  *
160
- * FontKnobStyles mounts here (not only in Storybook CreatePreview) so
161
- * headingFont/bodyFont restyle every shipped app that uses ThemeProvider.
162
- * Hairline and corner-smoothing already self-mount on module load.
161
+ * FontKnobStyles and CornerSmoothingStyles mount here (not only in
162
+ * Storybook CreatePreview) so heading/body fonts and the corner-shape
163
+ * stylesheet restyle every shipped app that uses ThemeProvider. Hairline
164
+ * still self-mounts on module load.
163
165
  */
164
166
  function KnobBridge({ children }: PropsWithChildren) {
165
167
  const existingCtx = useContext(PresetContext);
@@ -192,6 +194,7 @@ function KnobBridge({ children }: PropsWithChildren) {
192
194
  const tree = (
193
195
  <>
194
196
  <FontKnobStyles />
197
+ <CornerSmoothingStyles />
195
198
  {children}
196
199
  </>
197
200
  );
@@ -13,12 +13,16 @@ import type { ThemeName } from "@tamagui/web";
13
13
  import { PresetContext, type PresetContextValue } from "./PresetContext";
14
14
  import type { Preset } from "./preset.types";
15
15
  import { defaultKnobs } from "./knobs";
16
+ import { defaultPreset } from "./presets";
16
17
  import { Tint, useTintDepth } from "./Tint";
17
18
 
18
19
  // ---------------------------------------------------------------------------
19
20
  // Helpers
20
21
  // ---------------------------------------------------------------------------
21
22
 
23
+ /** Decorative nest family (R12) — the same four names as `defaultPreset.tints`. */
24
+ const FAMILY: ThemeName[] = ["orange", "blue", "purple", "pink"];
25
+
22
26
  function makePreset(tints: ThemeName[]): Preset {
23
27
  return {
24
28
  theme: "light" as ThemeName,
@@ -42,71 +46,70 @@ function collectThemes(html: string): string[] {
42
46
  return Array.from(html.matchAll(/data-tamagui-theme="([^"]+)"/g), (m) => m[1]);
43
47
  }
44
48
 
49
+ function nestTints(
50
+ count: number,
51
+ innermost: ReactNode = createElement("span", null, "inner"),
52
+ ): ReactNode {
53
+ let node: ReactNode = innermost;
54
+ for (let i = 0; i < count; i++) {
55
+ node = createElement(Tint, null, node);
56
+ }
57
+ return node;
58
+ }
59
+
45
60
  // ---------------------------------------------------------------------------
46
61
  // Tests
47
62
  // ---------------------------------------------------------------------------
48
63
 
49
64
  describe("Tint", () => {
50
- it("selects tints[1 % length] for a single <Tint>", () => {
51
- const tints: ThemeName[] = ["red", "green", "blue"];
65
+ it("defaultPreset.tints is the four-name decorative family", () => {
66
+ expect(defaultPreset.tints).toEqual(FAMILY);
67
+ });
68
+
69
+ it("selects tints[0] for a single <Tint> (depth 1)", () => {
52
70
  const tree = wrapWithPreset(
53
- tints,
71
+ FAMILY,
54
72
  createElement(Tint, null, createElement("span", null, "hello")),
55
73
  );
56
74
  const html = renderToHtml(tree);
57
75
  const themes = collectThemes(html);
58
- // depth = 0 (parent) + 1 = 1 → tints[1 % 3] = "green"
59
- expect(themes).toEqual(["green"]);
76
+ // depth = 0 (parent) + 1 = 1 → tints[(1 - 1) % 4] = "orange"
77
+ expect(themes).toEqual(["orange"]);
60
78
  });
61
79
 
62
80
  it("increments depth correctly with nested <Tint>", () => {
63
- const tints: ThemeName[] = ["red", "green", "blue"];
64
81
  const tree = wrapWithPreset(
65
- tints,
82
+ FAMILY,
66
83
  createElement(Tint, null, createElement(Tint, null, createElement("span", null, "inner"))),
67
84
  );
68
85
  const html = renderToHtml(tree);
69
86
  const themes = collectThemes(html);
70
- // outer: depth=1 → tints[1%3]="green", inner: depth=2 → tints[2%3]="blue"
71
- expect(themes).toEqual(["green", "blue"]);
87
+ // outer: depth=1 → tints[0]="orange", inner: depth=2 → tints[1]="blue"
88
+ expect(themes).toEqual(["orange", "blue"]);
72
89
  });
73
90
 
74
91
  it("wraps via modulo when depth exceeds tints.length", () => {
75
- const tints: ThemeName[] = ["red", "green", "blue"];
76
- // 4 nested Tints: depths 1,2,3,4 → indices 1,2,0,1 → green,blue,red,green
77
- const tree = wrapWithPreset(
78
- tints,
79
- createElement(
80
- Tint,
81
- null,
82
- createElement(
83
- Tint,
84
- null,
85
- createElement(Tint, null, createElement(Tint, null, createElement("span", null, "deep"))),
86
- ),
87
- ),
88
- );
92
+ // 5 nested Tints: depths 1..5 → indices 0,1,2,3,0 → orange,blue,purple,pink,orange
93
+ const tree = wrapWithPreset(FAMILY, nestTints(5, createElement("span", null, "deep")));
89
94
  const html = renderToHtml(tree);
90
95
  const themes = collectThemes(html);
91
- expect(themes).toEqual(["green", "blue", "red", "green"]);
96
+ expect(themes).toEqual(["orange", "blue", "purple", "pink", "orange"]);
92
97
  });
93
98
 
94
99
  it("applies alt prop offset", () => {
95
- const tints: ThemeName[] = ["red", "green", "blue"];
96
- // alt=2 at top level: depth = 0 + 1 + 2 = 3 → tints[3%3] = "red"
100
+ // alt=2 at top level: depth = 0 + 1 + 2 = 3 → tints[(3 - 1) % 4] = "purple"
97
101
  const tree = wrapWithPreset(
98
- tints,
102
+ FAMILY,
99
103
  createElement(Tint, { alt: 2 }, createElement("span", null, "alt")),
100
104
  );
101
105
  const html = renderToHtml(tree);
102
106
  const themes = collectThemes(html);
103
- expect(themes).toEqual(["red"]);
107
+ expect(themes).toEqual(["purple"]);
104
108
  });
105
109
 
106
110
  it("skips Theme wrapper when disable is true", () => {
107
- const tints: ThemeName[] = ["red", "green", "blue"];
108
111
  const tree = wrapWithPreset(
109
- tints,
112
+ FAMILY,
110
113
  createElement(Tint, { disable: true }, createElement("span", null, "no theme")),
111
114
  );
112
115
  const html = renderToHtml(tree);
@@ -115,10 +118,9 @@ describe("Tint", () => {
115
118
  });
116
119
 
117
120
  it("still increments depth when disabled (so children see correct depth)", () => {
118
- const tints: ThemeName[] = ["red", "green", "blue"];
119
- // disabled outer (depth=1, no theme wrap), inner (depth=2, wraps with tints[2%3]="blue")
121
+ // disabled outer (depth=1, no theme wrap), inner (depth=2, wraps with tints[1]="blue")
120
122
  const tree = wrapWithPreset(
121
- tints,
123
+ FAMILY,
122
124
  createElement(
123
125
  Tint,
124
126
  { disable: true },
@@ -163,14 +165,13 @@ describe("useTintDepth", () => {
163
165
  });
164
166
 
165
167
  it("returns current depth inside nested <Tint> components", () => {
166
- const tints: ThemeName[] = ["red", "green", "blue"];
167
168
  let capturedDepth = -1;
168
169
  const Probe = () => {
169
170
  capturedDepth = useTintDepth();
170
171
  return createElement("span", null, "probe");
171
172
  };
172
173
  const tree = wrapWithPreset(
173
- tints,
174
+ FAMILY,
174
175
  createElement(Tint, null, createElement(Tint, null, createElement(Probe))),
175
176
  );
176
177
  renderToHtml(tree);
@@ -1,5 +1,5 @@
1
1
  import { createContext, useContext, useEffect, useMemo, useState } from "react";
2
- import type { SizeTokens } from "tamagui";
2
+ import { type SizeTokens, useThemeName } from "tamagui";
3
3
  import { defaultKnobs, type Knobs } from "./knobs";
4
4
  import type { IntentOverride } from "./preset.types";
5
5
  import type { KnobPropsOverride, ResolvedKnobs } from "./recipes";
@@ -111,12 +111,14 @@ export const solidButtonIntents = new Set(["error", "warning", "success"]);
111
111
  * 6. Apply density (space only; compact option wins when provided)
112
112
  * 7. Re-clamp to Surface ceiling (compact option cannot exceed parent)
113
113
  * 8. OS reduced-motion forces animation="none"
114
- * 9. Call resolveKnobs(mergedKnobs, override) — override merges last
114
+ * 9. Call resolveKnobs(mergedKnobs, override, scheme) — override merges last
115
115
  */
116
116
  export function useResolvedKnobs(options?: UseResolvedKnobsOptions): ResolvedKnobs {
117
117
  const presetCtx = usePresetContext();
118
118
  const contextIntent = useIntentContext();
119
119
  const prefersReducedMotion = usePrefersReducedMotion();
120
+ const themeName = useThemeName() as string | undefined;
121
+ const scheme = themeName?.startsWith("dark") ? "dark" : "light";
120
122
  const surfaceCtx = SurfaceContext.useStyledContext();
121
123
  const surfaceSize = surfaceCtx?.size ?? "unset";
122
124
  const surfaceDensity = surfaceCtx?.density ?? "unset";
@@ -186,7 +188,7 @@ export function useResolvedKnobs(options?: UseResolvedKnobsOptions): ResolvedKno
186
188
  merged = { ...merged, animation: "none" };
187
189
  }
188
190
 
189
- const resolved = resolveKnobs(merged, override);
191
+ const resolved = resolveKnobs(merged, override, scheme);
190
192
 
191
193
  if (intent && component === "Button" && solidButtonIntents.has(intent)) {
192
194
  const labelColor = merged.fillStyle === "filled" ? "$color" : "$color11";
@@ -210,5 +212,6 @@ export function useResolvedKnobs(options?: UseResolvedKnobsOptions): ResolvedKno
210
212
  prefersReducedMotion,
211
213
  surfaceSize,
212
214
  surfaceDensity,
215
+ scheme,
213
216
  ]);
214
217
  }
@@ -65,7 +65,7 @@ export interface FigmaRadiusMode {
65
65
  /** `Sizing` collection variable to alias (e.g. `radius-4`). */
66
66
  figmaName: string;
67
67
  px: number;
68
- /** Outer/nested radius fragment (`knobProps.borderRadiusOuter`). */
68
+ /** Outer/nested radius fragment (`knobProps.borderRadiusNested`). */
69
69
  outerToken: string;
70
70
  outerFigmaName: string;
71
71
  outerPx: number;
@@ -1 +1 @@
1
- {"version":3,"file":"figmaTokens.d.ts","sourceRoot":"","sources":["../../src/figma/figmaTokens.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"figmaTokens.d.ts","sourceRoot":"","sources":["../../src/figma/figmaTokens.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"font.d.ts","sourceRoot":"","sources":["../src/font.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAiChD,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,WAAW,EACrD,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG;IAAE,MAAM,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAA;CAAE,GACzC,CAAC,CAWH"}
1
+ {"version":3,"file":"font.d.ts","sourceRoot":"","sources":["../src/font.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAoBhD,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,WAAW,EACrD,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG;IAAE,MAAM,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAA;CAAE,GACzC,CAAC,CAWH"}
@@ -23,15 +23,21 @@ export declare function ensureKeyboardModalityTracking(): void;
23
23
  */
24
24
  export declare function wasKeyboardFocus(): boolean;
25
25
  /**
26
- * Standard manual ring props — the SAME geometry as `FOCUS_VISIBLE_RING`
27
- * (2px solid `$outlineColor`, inset −2), spread here so manually-painted
28
- * rings and `focusVisibleStyle` rings can never drift apart.
26
+ * Standard manual ring props — the INSET ring geometry
27
+ * (`FOCUS_VISIBLE_RING_INSET`: 2px solid `$outlineColor`, offset −2), spread
28
+ * here so manually-painted rings can never drift from the theme contract.
29
+ * Manual painting exists for roving-focus / activedescendant composites
30
+ * (ToggleGroup segments inside a clipped Group, menu/listbox rows under a
31
+ * clipped overlay, table rows and cells) — items inside clipping ancestors,
32
+ * which is exactly the LC-71 §4 inset carve-out; an offset-0 band there
33
+ * would be severed by the clip. Standalone controls take the offset-0
34
+ * `FOCUS_VISIBLE_RING` via `focusVisibleStyle` / `ensureFocusVisibleRing()`.
29
35
  */
30
36
  export declare const keyboardFocusRingProps: {
37
+ readonly outlineOffset: number;
31
38
  readonly outlineWidth: number;
32
39
  readonly outlineStyle: "solid";
33
40
  readonly outlineColor: string;
34
- readonly outlineOffset: number;
35
41
  };
36
42
  /**
37
43
  * LC-71 RING-ANATOMY — CSS-synchronous ring on a composite's outer boundary.
@@ -41,7 +47,7 @@ export declare const keyboardFocusRingProps: {
41
47
  * spawned the inner-Area ring cascade). Inner targets suppress their own
42
48
  * outline so the box carries exactly one ring. Class: `mp-composite-ring`.
43
49
  */
44
- export declare const compositeFocusRingCss = "/* LC-71: ring on the composite outer box; never on the inner field.\n Inset (\u22122px): these frames use overflow:hidden to clip children to radius,\n which severs an outer ring. Do NOT use :has(:focus-visible) on every\n descendant \u2014 that would ring the whole SearchInput when the clear \u2715 is\n focused (two focusables \u2192 two rings, chip-dismiss rule). */\n.mp-composite-ring:focus-visible,\n.mp-composite-ring:has(input:focus),\n.mp-composite-ring:has(textarea:focus),\n.mp-composite-ring:has(.mp-input-area:focus) {\n outline: 2px solid var(--outlineColor, var(--c-outlineColor, CanvasText));\n outline-offset: -2px;\n}\n.mp-composite-ring-deep:has(:focus-visible) {\n outline: 2px solid var(--outlineColor, var(--c-outlineColor, CanvasText));\n outline-offset: -2px;\n}\n.mp-composite-ring input,\n.mp-composite-ring textarea,\n.mp-composite-ring [contenteditable],\n.mp-input-area,\n.mp-input-area:focus,\n.mp-input-area:focus-visible {\n outline: none !important;\n box-shadow: none !important;\n}\n.mp-chip-dismiss:focus-visible {\n outline: none !important;\n}\n.mp-chip-dismiss:focus-visible .mp-chip-dismiss-ring {\n outline: 2px solid var(--outlineColor, var(--c-outlineColor, CanvasText));\n outline-offset: 2px;\n}";
50
+ export declare const compositeFocusRingCss = "/* LC-71: ring on the composite outer box; never on the inner field.\n Inset (\u22122px): these frames use overflow:hidden to clip children to radius,\n which severs an outer ring. Do NOT use :has(:focus-visible) on every\n descendant \u2014 that would ring the whole SearchInput when the clear \u2715 is\n focused (two focusables \u2192 two rings, chip-dismiss rule). */\n.mp-composite-ring:focus-visible,\n.mp-composite-ring:has(input:focus),\n.mp-composite-ring:has(textarea:focus),\n.mp-composite-ring:has(.mp-input-area:focus) {\n outline: 2px solid var(--outlineColor, var(--c-outlineColor, CanvasText));\n outline-offset: -2px;\n}\n.mp-composite-ring-deep:has(:focus-visible) {\n outline: 2px solid var(--outlineColor, var(--c-outlineColor, CanvasText));\n outline-offset: -2px;\n}\n.mp-composite-ring input,\n.mp-composite-ring textarea,\n.mp-composite-ring [contenteditable],\n.mp-input-area,\n.mp-input-area:focus,\n.mp-input-area:focus-visible {\n outline: none !important;\n box-shadow: none !important;\n}\n.mp-chip-dismiss:focus-visible {\n outline: none !important;\n}\n.mp-chip-dismiss:focus-visible .mp-chip-dismiss-ring {\n outline: 2px solid var(--outlineColor, var(--c-outlineColor, CanvasText));\n outline-offset: 0;\n}";
45
51
  /** Idempotently mount the composite-boundary focus-ring stylesheet. */
46
52
  export declare function ensureCompositeFocusRing(): void;
47
53
  //# sourceMappingURL=keyboardFocusRing.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"keyboardFocusRing.d.ts","sourceRoot":"","sources":["../src/keyboardFocusRing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH;;;;GAIG;AACH,wBAAgB,8BAA8B,SAK7C;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,IAAI,OAAO,CAG1C;AAID;;;;GAIG;AACH,eAAO,MAAM,sBAAsB;;;;;CAEzB,CAAC;AAEX;;;;;;;GAOG;AACH,eAAO,MAAM,qBAAqB,gvCA+BhC,CAAC;AAIH,uEAAuE;AACvE,wBAAgB,wBAAwB,IAAI,IAAI,CAO/C"}
1
+ {"version":3,"file":"keyboardFocusRing.d.ts","sourceRoot":"","sources":["../src/keyboardFocusRing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH;;;;GAIG;AACH,wBAAgB,8BAA8B,SAK7C;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,IAAI,OAAO,CAG1C;AAID;;;;;;;;;;GAUG;AACH,eAAO,MAAM,sBAAsB;;;;;CAEzB,CAAC;AAEX;;;;;;;GAOG;AACH,eAAO,MAAM,qBAAqB,8uCA+BhC,CAAC;AAIH,uEAAuE;AACvE,wBAAgB,wBAAwB,IAAI,IAAI,CAO/C"}
@@ -21,7 +21,8 @@ export interface FontScopeTables {
21
21
  /**
22
22
  * Resolve the concrete font-family stack registered in the Tamagui config for
23
23
  * a font category. Returns undefined when the category font is not registered
24
- * (the caller then leaves the default family in place).
24
+ * (the caller then uses `fontCategoryStacks` so the knob still writes
25
+ * `--f-family` — live SHC console had the style tag and skipped the family).
25
26
  */
26
27
  export declare function getConfiguredFontFamily(category: FontCategory): string | undefined;
27
28
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"FontKnobStyles.d.ts","sourceRoot":"","sources":["../../src/theme/FontKnobStyles.tsx"],"names":[],"mappings":"AA6BA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAM5C;;;;;;;GAOG;AACH,eAAO,MAAM,mBAAmB,EAAE,MAAM,CACtC,YAAY,EACZ;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,SAAS,GAAG,SAAS,CAAA;CAAE,CAgBxE,CAAC;AAqBF,4EAA4E;AAC5E,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACxC;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,YAAY,GAAG,MAAM,GAAG,SAAS,CAelF;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,SAAS,GAAG,MAAM,GAAG,eAAe,GAAG,SAAS,CAgC9F;AAkCD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,WAAW,EAAE,YAAY,EACzB,QAAQ,EAAE,YAAY,EACtB,aAAa,GAAE,CAAC,QAAQ,EAAE,YAAY,KAAK,MAAM,GAAG,SAAmC,EACvF,aAAa,GAAE,CACb,KAAK,EAAE,SAAS,GAAG,MAAM,KACtB,eAAe,GAAG,SAAmC,GACzD,MAAM,CAkBR;AAED;;;;GAIG;AACH,wBAAgB,cAAc,SAgC7B"}
1
+ {"version":3,"file":"FontKnobStyles.d.ts","sourceRoot":"","sources":["../../src/theme/FontKnobStyles.tsx"],"names":[],"mappings":"AA8BA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAM5C;;;;;;;GAOG;AACH,eAAO,MAAM,mBAAmB,EAAE,MAAM,CACtC,YAAY,EACZ;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,SAAS,GAAG,SAAS,CAAA;CAAE,CAgBxE,CAAC;AAqBF,4EAA4E;AAC5E,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACxC;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,YAAY,GAAG,MAAM,GAAG,SAAS,CAelF;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,SAAS,GAAG,MAAM,GAAG,eAAe,GAAG,SAAS,CAgC9F;AAkCD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,WAAW,EAAE,YAAY,EACzB,QAAQ,EAAE,YAAY,EACtB,aAAa,GAAE,CAAC,QAAQ,EAAE,YAAY,KAAK,MAAM,GAAG,SAAmC,EACvF,aAAa,GAAE,CACb,KAAK,EAAE,SAAS,GAAG,MAAM,KACtB,eAAe,GAAG,SAAmC,GACzD,MAAM,CAkBR;AAED;;;;GAIG;AACH,wBAAgB,cAAc,SAgC7B"}
@@ -7,7 +7,8 @@ export interface TintProps extends PropsWithChildren {
7
7
  }
8
8
  /**
9
9
  * Depth-based tint component. Each nesting level increments the depth
10
- * and selects a tint from the preset's tints array via modulo.
10
+ * and selects a tint from the preset's tints array via
11
+ * `tints[(depth - 1) % length]` so the first Tint is `tints[0]`.
11
12
  *
12
13
  * Applies a tint sub-theme based on nesting depth.
13
14
  */
@@ -1 +1 @@
1
- {"version":3,"file":"Tint.d.ts","sourceRoot":"","sources":["../../src/theme/Tint.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,iBAAiB,EAA6B,MAAM,OAAO,CAAC;AAM1E,MAAM,WAAW,SAAU,SAAQ,iBAAiB;IAClD,sEAAsE;IACtE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,kEAAkE;IAClE,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;;;GAKG;AACH,wBAAgB,IAAI,CAAC,EAAE,QAAQ,EAAE,GAAO,EAAE,OAAO,EAAE,GAAG,KAAK,EAAE,EAAE,SAAS,2CAiBvE;AAED;;GAEG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAErC"}
1
+ {"version":3,"file":"Tint.d.ts","sourceRoot":"","sources":["../../src/theme/Tint.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,iBAAiB,EAA6B,MAAM,OAAO,CAAC;AAM1E,MAAM,WAAW,SAAU,SAAQ,iBAAiB;IAClD,sEAAsE;IACtE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,kEAAkE;IAClE,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;;;;GAMG;AACH,wBAAgB,IAAI,CAAC,EAAE,QAAQ,EAAE,GAAO,EAAE,OAAO,EAAE,GAAG,KAAK,EAAE,EAAE,SAAS,2CAiBvE;AAED;;GAEG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAErC"}
@@ -1,8 +1,21 @@
1
+ /**
2
+ * The CSS driver's named durations.
3
+ *
4
+ * `bouncy`, `lazy`, `slow`, `medium` and `quick` are Tamagui's own names and
5
+ * carry Tamagui's own values — a shared name that means something different
6
+ * here is worse than no name, because every consumer reading `animation="medium"`
7
+ * against the upstream docs gets a different answer than the one it renders.
8
+ * MPO-19 X10: `medium` was `ease-in-out 250ms` against stock `ease-in 300ms`,
9
+ * with no improvement argument written down anywhere.
10
+ *
11
+ * `tooltip`, `snappy` and `gentle` are additive — names Tamagui does not use,
12
+ * so they are ours to set and are not a contradiction.
13
+ */
1
14
  export declare const animationConfig: {
2
15
  readonly bouncy: "ease-in 200ms";
3
16
  readonly lazy: "ease-in 600ms";
4
17
  readonly slow: "ease-in 500ms";
5
- readonly medium: "ease-in-out 250ms";
18
+ readonly medium: "ease-in 300ms";
6
19
  readonly quick: "ease-in 100ms";
7
20
  readonly tooltip: "ease-in 400ms";
8
21
  readonly snappy: "ease-out 80ms";
@@ -14,7 +27,7 @@ export declare const animations: import("@tamagui/web").AnimationDriver<{
14
27
  readonly bouncy: "ease-in 200ms";
15
28
  readonly lazy: "ease-in 600ms";
16
29
  readonly slow: "ease-in 500ms";
17
- readonly medium: "ease-in-out 250ms";
30
+ readonly medium: "ease-in 300ms";
18
31
  readonly quick: "ease-in 100ms";
19
32
  readonly tooltip: "ease-in 400ms";
20
33
  readonly snappy: "ease-out 80ms";
@@ -1 +1 @@
1
- {"version":3,"file":"css.d.ts","sourceRoot":"","sources":["../../../src/theme/animations/css.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,eAAe;;;;;;;;;CASlB,CAAC;AAEX,MAAM,MAAM,aAAa,GAAG,MAAM,OAAO,eAAe,CAAC;AACzD,eAAO,MAAM,cAAc,EAAmC,aAAa,EAAE,CAAC;AAC9E,eAAO,MAAM,UAAU;;;;;;;;;EAAoC,CAAC;AAO5D,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,aAAa,EAAE,MAAM,CAE3B,CAAC"}
1
+ {"version":3,"file":"css.d.ts","sourceRoot":"","sources":["../../../src/theme/animations/css.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,eAAe;;;;;;;;;CASlB,CAAC;AAEX,MAAM,MAAM,aAAa,GAAG,MAAM,OAAO,eAAe,CAAC;AACzD,eAAO,MAAM,cAAc,EAAmC,aAAa,EAAE,CAAC;AAC9E,eAAO,MAAM,UAAU;;;;;;;;;EAAoC,CAAC;AAO5D,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,aAAa,EAAE,MAAM,CAE3B,CAAC"}