@multiplatform.one/theme 7.34.4 → 7.36.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.
- package/package.json +20 -5
- package/src/audit/constraintAudit.spec.ts +274 -87
- package/src/audit/constraintAudit.ts +135 -120
- package/src/audit/constraintScope.spec.ts +1 -1
- package/src/audit/designTokens.ts +42 -0
- package/src/audit/themeMatrix.spec.ts +83 -2
- package/src/audit/themeMatrix.ts +33 -1
- package/src/figma/figmaTokens.spec.ts +26 -34
- package/src/figma/figmaTokens.ts +26 -50
- package/src/fonts/createFontLoader.ts +4 -1
- package/src/menuRow.ts +4 -1
- package/src/theme/FontKnobStyles.tsx +2 -1
- package/src/theme/Preset.stories.tsx +0 -1
- package/src/theme/Preset.tsx +6 -1
- package/src/theme/PresetContext.ts +4 -0
- package/src/theme/Surface.spec.tsx +114 -67
- package/src/theme/Surface.stories.tsx +8 -25
- package/src/theme/Surface.tsx +87 -43
- package/src/theme/accentFloor.spec.ts +37 -11
- package/src/theme/activateThemeConfig.ts +22 -6
- package/src/theme/animations/animations.spec.ts +62 -0
- package/src/theme/animations/css.ts +9 -35
- package/src/theme/animations/reactNative.ts +98 -25
- package/src/theme/boundaryFloor.spec.ts +57 -0
- package/src/theme/chartPalette.spec.ts +56 -27
- package/src/theme/chartPalette.ts +33 -28
- package/src/theme/colorRules.spec.ts +32 -72
- package/src/theme/cookies.spec.ts +49 -18
- package/src/theme/cookies.ts +36 -7
- package/src/theme/cookiesPersistence.spec.ts +5 -5
- package/src/theme/createDefaultThemeConfig.spec.ts +182 -20
- package/src/theme/createDefaultThemeConfig.ts +34 -28
- package/src/theme/createThemes.ts +169 -218
- package/src/theme/defaults/accent.ts +3 -35
- package/src/theme/defaults/base.ts +14 -53
- package/src/theme/defaults/builderOptions.ts +68 -85
- package/src/theme/defaults/categoryFonts.ts +10 -0
- package/src/theme/defaults/fonts.spec.ts +84 -4
- package/src/theme/defaults/fonts.ts +166 -124
- package/src/theme/defaults/house.ts +48 -0
- package/src/theme/defaults/index.ts +1 -0
- package/src/theme/defaults/neutralRamp.spec.ts +52 -47
- package/src/theme/devtools/ColorLineVisualizer.tsx +6 -6
- package/src/theme/devtools/ThemeDevtoolsPanel.setMarkers.spec.tsx +13 -6
- package/src/theme/devtools/ThemeDevtoolsPanel.tsx +14 -77
- package/src/theme/devtools/animationOptions.spec.ts +23 -37
- package/src/theme/devtools/animationOptions.ts +4 -6
- package/src/theme/devtools/buildSnapshot.ts +0 -3
- package/src/theme/devtools/knobIcons.tsx +4 -24
- package/src/theme/focusState.spec.ts +9 -30
- package/src/theme/index.ts +17 -0
- package/src/theme/intentFill.spec.ts +42 -0
- package/src/theme/knobs.ts +14 -42
- package/src/theme/layoutTokens.ts +15 -1
- package/src/theme/layoutTokensHooks.spec.tsx +8 -4
- package/src/theme/mergedTokens.ts +20 -0
- package/src/theme/motion.spec.ts +84 -0
- package/src/theme/motion.ts +53 -0
- package/src/theme/motionStops.ts +17 -0
- package/src/theme/motionTiming.ts +59 -0
- package/src/theme/mpo13Proof.spec.ts +43 -46
- package/src/theme/mpo19Proof.spec.ts +13 -14
- package/src/theme/namedInkFloor.spec.ts +60 -0
- package/src/theme/overlaySurfaces.tsx +330 -0
- package/src/theme/preset.types.ts +16 -0
- package/src/theme/presetPreview.ts +32 -0
- package/src/theme/presetRoot.spec.tsx +89 -0
- package/src/theme/presetStore.persistence.spec.ts +141 -0
- package/src/theme/presetStore.spec.ts +205 -0
- package/src/theme/presetStore.ts +286 -0
- package/src/theme/presetUserSync.spec.tsx +168 -0
- package/src/theme/presetUserSync.ts +47 -0
- package/src/theme/presets.spec.ts +22 -15
- package/src/theme/presets.ts +22 -10
- package/src/theme/radiusClass.spec.ts +60 -41
- package/src/theme/radiusClass.ts +17 -4
- package/src/theme/radiusClassTable.generated.ts +3 -3
- package/src/theme/readableColor.spec.ts +59 -152
- package/src/theme/readableColor.ts +34 -174
- package/src/theme/recipeInputs.ts +153 -295
- package/src/theme/recipes.ts +58 -18
- package/src/theme/resolveKnobs.spec.ts +528 -588
- package/src/theme/resolveKnobs.ts +250 -325
- package/src/theme/scale/ScaleStyles.native.tsx +19 -0
- package/src/theme/scale/ScaleStyles.tsx +48 -0
- package/src/theme/scale/activeScale.ts +30 -0
- package/src/theme/scale/deriveRecipeInputs.ts +110 -0
- package/src/theme/scale/generateScale.spec.ts +260 -0
- package/src/theme/scale/generateScale.ts +147 -0
- package/src/theme/scale/index.ts +54 -0
- package/src/theme/scale/installScaleConfig.native.spec.ts +77 -0
- package/src/theme/scale/installScaleConfig.native.ts +57 -0
- package/src/theme/scale/installScaleConfig.ts +7 -0
- package/src/theme/scale/scaleAttribute.ts +21 -0
- package/src/theme/scale/scaleCss.probe.spec.tsx +64 -0
- package/src/theme/scale/scaleCss.spec.ts +132 -0
- package/src/theme/scale/scaleCss.ts +95 -0
- package/src/theme/scale/scaleFonts.ts +106 -0
- package/src/theme/scale/scaleInputs.ts +322 -0
- package/src/theme/scale/themeScales.ts +181 -0
- package/src/theme/scale/useActiveScale.spec.tsx +129 -0
- package/src/theme/scale/useActiveScale.ts +95 -0
- package/src/theme/schemeStore.spec.ts +81 -0
- package/src/theme/schemeStore.ts +67 -0
- package/src/theme/shared.spec.ts +7 -6
- package/src/theme/shared.tsx +37 -3
- package/src/theme/sizeLadder.spec.ts +37 -36
- package/src/theme/sizeRecipes.spec.ts +179 -85
- package/src/theme/sizeRecipes.ts +48 -25
- package/src/theme/subsetThemes.spec.ts +9 -3
- package/src/theme/switchThumb.spec.ts +69 -0
- package/src/theme/syntaxFloor.spec.ts +11 -8
- package/src/theme/theme.hydration.native.spec.tsx +51 -0
- package/src/theme/theme.native.tsx +59 -21
- package/src/theme/theme.palette.spec.tsx +76 -0
- package/src/theme/theme.preset.native.spec.tsx +73 -0
- package/src/theme/theme.provider.spec.ts +143 -1
- package/src/theme/theme.tsx +61 -25
- package/src/theme/tileSizes.ts +6 -7
- package/src/theme/transitionProps.native.ts +25 -2
- package/src/theme/transitionProps.spec.ts +34 -0
- package/src/theme/transitionProps.ts +26 -2
- package/src/theme/usePresetSwitch.native.spec.tsx +68 -0
- package/src/theme/usePresetSwitch.native.ts +32 -0
- package/src/theme/usePresetSwitch.spec.tsx +103 -0
- package/src/theme/usePresetSwitch.ts +54 -0
- package/src/theme/useResolvedKnobs.hydration.spec.tsx +2 -2
- package/src/theme/useResolvedKnobs.ts +23 -90
- package/src/theme/useResolvedKnobsBehavior.spec.tsx +25 -30
- package/src/theme/useSchemePreference.native.ts +33 -0
- package/src/theme/useSchemePreference.spec.tsx +51 -0
- package/src/theme/useSchemePreference.ts +32 -0
- package/src/theme/useSchemePreference.web.spec.tsx +32 -0
- package/src/theme/useTheme.native.spec.tsx +68 -0
- package/src/theme/useTheme.native.ts +49 -24
- package/src/theme/useTheme.ts +7 -4
- package/types/audit/constraintAudit.d.ts +6 -5
- package/types/audit/constraintAudit.d.ts.map +1 -1
- package/types/audit/designTokens.d.ts +13 -0
- package/types/audit/designTokens.d.ts.map +1 -0
- package/types/audit/themeMatrix.d.ts +10 -0
- package/types/audit/themeMatrix.d.ts.map +1 -1
- package/types/figma/figmaTokens.d.ts +11 -28
- package/types/figma/figmaTokens.d.ts.map +1 -1
- package/types/fonts/createFontLoader.d.ts.map +1 -1
- package/types/menuRow.d.ts +4 -1
- package/types/menuRow.d.ts.map +1 -1
- package/types/theme/FontKnobStyles.d.ts.map +1 -1
- package/types/theme/Preset.d.ts.map +1 -1
- package/types/theme/PresetContext.d.ts +4 -0
- package/types/theme/PresetContext.d.ts.map +1 -1
- package/types/theme/Surface.d.ts +57 -26
- package/types/theme/Surface.d.ts.map +1 -1
- package/types/theme/activateThemeConfig.d.ts +5 -3
- package/types/theme/activateThemeConfig.d.ts.map +1 -1
- package/types/theme/animations/css.d.ts +27 -34
- package/types/theme/animations/css.d.ts.map +1 -1
- package/types/theme/animations/reactNative.d.ts +196 -45
- package/types/theme/animations/reactNative.d.ts.map +1 -1
- package/types/theme/chartPalette.d.ts +11 -3
- package/types/theme/chartPalette.d.ts.map +1 -1
- package/types/theme/colorRules.d.ts +1 -1
- package/types/theme/cookies.d.ts +12 -1
- package/types/theme/cookies.d.ts.map +1 -1
- package/types/theme/createDefaultThemeConfig.d.ts +13 -13
- package/types/theme/createDefaultThemeConfig.d.ts.map +1 -1
- package/types/theme/createThemes.d.ts +58 -2
- package/types/theme/createThemes.d.ts.map +1 -1
- package/types/theme/defaults/accent.d.ts.map +1 -1
- package/types/theme/defaults/base.d.ts.map +1 -1
- package/types/theme/defaults/builderOptions.d.ts.map +1 -1
- package/types/theme/defaults/categoryFonts.d.ts +10 -0
- package/types/theme/defaults/categoryFonts.d.ts.map +1 -1
- package/types/theme/defaults/fonts.d.ts +330 -30
- package/types/theme/defaults/fonts.d.ts.map +1 -1
- package/types/theme/defaults/house.d.ts +8 -0
- package/types/theme/defaults/house.d.ts.map +1 -0
- package/types/theme/defaults/index.d.ts +1 -0
- package/types/theme/defaults/index.d.ts.map +1 -1
- package/types/theme/devtools/ThemeDevtoolsPanel.d.ts +3 -3
- package/types/theme/devtools/ThemeDevtoolsPanel.d.ts.map +1 -1
- package/types/theme/devtools/animationOptions.d.ts.map +1 -1
- package/types/theme/devtools/buildSnapshot.d.ts.map +1 -1
- package/types/theme/devtools/knobIcons.d.ts +0 -3
- package/types/theme/devtools/knobIcons.d.ts.map +1 -1
- package/types/theme/index.d.ts +11 -0
- package/types/theme/index.d.ts.map +1 -1
- package/types/theme/knobs.d.ts +8 -36
- package/types/theme/knobs.d.ts.map +1 -1
- package/types/theme/layoutTokens.d.ts +13 -1
- package/types/theme/layoutTokens.d.ts.map +1 -1
- package/types/theme/mergedTokens.d.ts +11 -0
- package/types/theme/mergedTokens.d.ts.map +1 -0
- package/types/theme/motion.d.ts +17 -0
- package/types/theme/motion.d.ts.map +1 -0
- package/types/theme/motionStops.d.ts +3 -0
- package/types/theme/motionStops.d.ts.map +1 -0
- package/types/theme/motionTiming.d.ts +36 -0
- package/types/theme/motionTiming.d.ts.map +1 -0
- package/types/theme/overlaySurfaces.d.ts +70 -0
- package/types/theme/overlaySurfaces.d.ts.map +1 -0
- package/types/theme/preset.types.d.ts +16 -0
- package/types/theme/preset.types.d.ts.map +1 -1
- package/types/theme/presetPreview.d.ts +14 -0
- package/types/theme/presetPreview.d.ts.map +1 -0
- package/types/theme/presetStore.d.ts +40 -0
- package/types/theme/presetStore.d.ts.map +1 -0
- package/types/theme/presetUserSync.d.ts +7 -0
- package/types/theme/presetUserSync.d.ts.map +1 -0
- package/types/theme/presets.d.ts +12 -4
- package/types/theme/presets.d.ts.map +1 -1
- package/types/theme/radiusClass.d.ts +5 -2
- package/types/theme/radiusClass.d.ts.map +1 -1
- package/types/theme/radiusClassTable.generated.d.ts +3 -3
- package/types/theme/readableColor.d.ts +11 -49
- package/types/theme/readableColor.d.ts.map +1 -1
- package/types/theme/recipeInputs.d.ts +39 -70
- package/types/theme/recipeInputs.d.ts.map +1 -1
- package/types/theme/recipes.d.ts +79 -18
- package/types/theme/recipes.d.ts.map +1 -1
- package/types/theme/resolveKnobs.d.ts +64 -32
- package/types/theme/resolveKnobs.d.ts.map +1 -1
- package/types/theme/scale/ScaleStyles.d.ts +18 -0
- package/types/theme/scale/ScaleStyles.d.ts.map +1 -0
- package/types/theme/scale/ScaleStyles.native.d.ts +10 -0
- package/types/theme/scale/ScaleStyles.native.d.ts.map +1 -0
- package/types/theme/scale/activeScale.d.ts +13 -0
- package/types/theme/scale/activeScale.d.ts.map +1 -0
- package/types/theme/scale/deriveRecipeInputs.d.ts +10 -0
- package/types/theme/scale/deriveRecipeInputs.d.ts.map +1 -0
- package/types/theme/scale/generateScale.d.ts +38 -0
- package/types/theme/scale/generateScale.d.ts.map +1 -0
- package/types/theme/scale/index.d.ts +17 -0
- package/types/theme/scale/index.d.ts.map +1 -0
- package/types/theme/scale/installScaleConfig.d.ts +4 -0
- package/types/theme/scale/installScaleConfig.d.ts.map +1 -0
- package/types/theme/scale/installScaleConfig.native.d.ts +13 -0
- package/types/theme/scale/installScaleConfig.native.d.ts.map +1 -0
- package/types/theme/scale/scaleAttribute.d.ts +14 -0
- package/types/theme/scale/scaleAttribute.d.ts.map +1 -0
- package/types/theme/scale/scaleCss.d.ts +25 -0
- package/types/theme/scale/scaleCss.d.ts.map +1 -0
- package/types/theme/scale/scaleFonts.d.ts +16 -0
- package/types/theme/scale/scaleFonts.d.ts.map +1 -0
- package/types/theme/scale/scaleInputs.d.ts +66 -0
- package/types/theme/scale/scaleInputs.d.ts.map +1 -0
- package/types/theme/scale/themeScales.d.ts +52 -0
- package/types/theme/scale/themeScales.d.ts.map +1 -0
- package/types/theme/scale/useActiveScale.d.ts +31 -0
- package/types/theme/scale/useActiveScale.d.ts.map +1 -0
- package/types/theme/schemeStore.d.ts +6 -0
- package/types/theme/schemeStore.d.ts.map +1 -0
- package/types/theme/shared.d.ts +18 -0
- package/types/theme/shared.d.ts.map +1 -1
- package/types/theme/sizeRecipes.d.ts +20 -15
- package/types/theme/sizeRecipes.d.ts.map +1 -1
- package/types/theme/theme.d.ts +2 -1
- package/types/theme/theme.d.ts.map +1 -1
- package/types/theme/theme.native.d.ts +10 -1
- package/types/theme/theme.native.d.ts.map +1 -1
- package/types/theme/tileSizes.d.ts +6 -7
- package/types/theme/tileSizes.d.ts.map +1 -1
- package/types/theme/transitionProps.d.ts +3 -1
- package/types/theme/transitionProps.d.ts.map +1 -1
- package/types/theme/transitionProps.native.d.ts +3 -1
- package/types/theme/transitionProps.native.d.ts.map +1 -1
- package/types/theme/usePresetSwitch.d.ts +10 -0
- package/types/theme/usePresetSwitch.d.ts.map +1 -0
- package/types/theme/usePresetSwitch.native.d.ts +10 -0
- package/types/theme/usePresetSwitch.native.d.ts.map +1 -0
- package/types/theme/useResolvedKnobs.d.ts +13 -13
- package/types/theme/useResolvedKnobs.d.ts.map +1 -1
- package/types/theme/useSchemePreference.d.ts +8 -0
- package/types/theme/useSchemePreference.d.ts.map +1 -0
- package/types/theme/useSchemePreference.native.d.ts +8 -0
- package/types/theme/useSchemePreference.native.d.ts.map +1 -0
- package/types/theme/useTheme.d.ts +4 -2
- package/types/theme/useTheme.d.ts.map +1 -1
- package/types/theme/useTheme.native.d.ts +10 -1
- package/types/theme/useTheme.native.d.ts.map +1 -1
|
@@ -1,16 +1,22 @@
|
|
|
1
|
+
import { motionFor } from "./motion";
|
|
1
2
|
import { isTouchable, isWeb, isWebTouchable } from "@multiplatform.one/platform";
|
|
2
|
-
import
|
|
3
|
+
import { getConfig } from "@tamagui/web";
|
|
4
|
+
import type { FontSizeTokens } from "tamagui";
|
|
3
5
|
// Importing the class constant also mounts the corner-shape stylesheet at
|
|
4
6
|
// theme load (web only, idempotent) — see cornerSmoothing.ts.
|
|
5
7
|
import { cornerSmoothClassName } from "./cornerSmoothing";
|
|
8
|
+
import { headingCategoryFontKeys } from "./defaults/fonts";
|
|
6
9
|
import {
|
|
7
10
|
ensureFocusVisibleRing,
|
|
8
11
|
STATE_LAYER_HOVER,
|
|
9
12
|
STATE_LAYER_PRESS,
|
|
10
13
|
withDefaultStateLayer,
|
|
11
14
|
} from "./focusState";
|
|
15
|
+
import { fontCategoryStacks } from "./fontCategoryStacks";
|
|
12
16
|
import type { CornerSmoothing, Elevation, Knobs, PageTitleScale, StateKnobs } from "./knobs";
|
|
13
17
|
import { pressTargetHitSlop } from "./layoutTokens";
|
|
18
|
+
import { getActiveScaleTokens } from "./scale/activeScale";
|
|
19
|
+
import { SIZE_KEYS, type SizeKey } from "./scale/scaleInputs";
|
|
14
20
|
import type {
|
|
15
21
|
ControlStateProps,
|
|
16
22
|
DisabledRecipe,
|
|
@@ -25,45 +31,6 @@ import type {
|
|
|
25
31
|
} from "./recipes";
|
|
26
32
|
import { sizeRecipeForToken } from "./sizeRecipes";
|
|
27
33
|
|
|
28
|
-
// ── Density (compact) stepping ────────────────────────────────
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* @deprecated Size is no longer coupled to density (LC-76). Compact density
|
|
32
|
-
* steps space only. Kept exported because figmaTokens historically imported it.
|
|
33
|
-
*/
|
|
34
|
-
export const compactSizeMap: Record<Knobs["size"], Knobs["size"]> = {
|
|
35
|
-
small: "small",
|
|
36
|
-
medium: "small",
|
|
37
|
-
large: "medium",
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
/** Step space down one level for compact density. */
|
|
41
|
-
export const compactSpaceMap: Record<Knobs["space"], Knobs["space"]> = {
|
|
42
|
-
small: "small",
|
|
43
|
-
medium: "small",
|
|
44
|
-
large: "medium",
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Apply density mode to knobs before token resolution.
|
|
49
|
-
*
|
|
50
|
-
* - `density: "compact"` steps **space only** (`compactSpaceMap`). Size is an
|
|
51
|
-
* independent axis (LC-76) — compact must not shrink controls.
|
|
52
|
-
* - `compactOverride` wins over the density knob when provided:
|
|
53
|
-
* `true` forces compact space stepping; `false` forces comfortable (no step).
|
|
54
|
-
*/
|
|
55
|
-
export function applyDensity(knobs: Knobs, compactOverride?: boolean): Knobs {
|
|
56
|
-
const useCompact = compactOverride !== undefined ? compactOverride : knobs.density === "compact";
|
|
57
|
-
if (!useCompact) {
|
|
58
|
-
return knobs.density === "comfortable" ? knobs : { ...knobs, density: "comfortable" };
|
|
59
|
-
}
|
|
60
|
-
return {
|
|
61
|
-
...knobs,
|
|
62
|
-
density: "compact",
|
|
63
|
-
space: compactSpaceMap[knobs.space],
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
|
|
67
34
|
// ── Knob-to-token mapping tables ──────────────────────────────
|
|
68
35
|
|
|
69
36
|
export const borderRadiusMap = {
|
|
@@ -74,6 +41,14 @@ export const borderRadiusMap = {
|
|
|
74
41
|
full: "$12",
|
|
75
42
|
} as const;
|
|
76
43
|
|
|
44
|
+
export const containerRadiusMap = {
|
|
45
|
+
none: "$0",
|
|
46
|
+
small: "$4",
|
|
47
|
+
medium: "$6",
|
|
48
|
+
large: "$8",
|
|
49
|
+
full: "$12",
|
|
50
|
+
} as const;
|
|
51
|
+
|
|
77
52
|
// Radius for chrome nested INSIDE a radius-bearing frame. Smaller than
|
|
78
53
|
// `borderRadiusMap` at every stop that has room to be — a child corner equal
|
|
79
54
|
// to its parent's reads as a second frame rather than as part of one.
|
|
@@ -108,16 +83,21 @@ const cornerSmoothingStyle: Record<CornerSmoothing, { className: string } | unde
|
|
|
108
83
|
? { round: undefined, smooth: { className: cornerSmoothClassName } }
|
|
109
84
|
: { round: undefined, smooth: undefined };
|
|
110
85
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
86
|
+
// ── Spacing roles (SP-GAP, A9) ────────────────────────────────
|
|
87
|
+
// Fixed `$space` keys. Their px come from the active scale, so a preset
|
|
88
|
+
// that wants more or less room changes token values, never these keys.
|
|
89
|
+
export const spacingRoles = {
|
|
90
|
+
gapSm: "$2",
|
|
91
|
+
gap: "$3",
|
|
92
|
+
gapLg: "$5",
|
|
93
|
+
panelPadding: "$4",
|
|
94
|
+
featurePadding: "$6",
|
|
115
95
|
} as const;
|
|
116
96
|
|
|
117
97
|
// Feature tier (SF-FEATURE, MPO-21 GL-59 ruling B): the Bento recipe's $10
|
|
118
|
-
// radius
|
|
119
|
-
//
|
|
120
|
-
//
|
|
98
|
+
// radius over the `featurePadding` role ($6). Uncapped by padding: the
|
|
99
|
+
// tightest pair (full 44 over 28) keeps a child corner at the inset inside
|
|
100
|
+
// the arc (padding >= radius x 0.29).
|
|
121
101
|
const featureRadiusMap = {
|
|
122
102
|
none: "$0",
|
|
123
103
|
small: "$9",
|
|
@@ -126,21 +106,11 @@ const featureRadiusMap = {
|
|
|
126
106
|
full: "$12",
|
|
127
107
|
} as const;
|
|
128
108
|
|
|
129
|
-
const featurePaddingMap = {
|
|
130
|
-
small: "$5",
|
|
131
|
-
medium: "$6",
|
|
132
|
-
large: "$7",
|
|
133
|
-
} as const;
|
|
134
|
-
|
|
135
109
|
// ── Page-title step (D-11 hero-H1 dial) ───────────────────────
|
|
136
|
-
//
|
|
137
|
-
// scale (1.4x Inter, defaults/fonts.ts) $8 = 32px and $10 = 64px, so
|
|
138
|
-
// `moderate` lands in the page-title class while `display` keeps the
|
|
139
|
-
// marketing hero step. Screens never pick a heading step by hand — they
|
|
140
|
-
// spread `knobProps.pageTitle`, so one knob flip re-scales every title.
|
|
110
|
+
// Default type 15: moderate is 40px; display is 52px.
|
|
141
111
|
const pageTitleSizeMap: Record<PageTitleScale, FontSizeTokens> = {
|
|
142
|
-
moderate: "$
|
|
143
|
-
display: "$
|
|
112
|
+
moderate: "$10",
|
|
113
|
+
display: "$12",
|
|
144
114
|
};
|
|
145
115
|
|
|
146
116
|
/**
|
|
@@ -157,39 +127,39 @@ export function resolvePageTitleScale(scale: PageTitleScale): { size: FontSizeTo
|
|
|
157
127
|
// ── Container radius cap (Axiom 1 CLIP) ──────────────────────
|
|
158
128
|
// A padded container's effective radius must not exceed its own padding:
|
|
159
129
|
// past that, the corner curve sweeps through the cell where children sit
|
|
160
|
-
// (radius > padding clips child corners).
|
|
161
|
-
//
|
|
162
|
-
//
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
medium: 9,
|
|
171
|
-
large: 16,
|
|
172
|
-
full: 50,
|
|
130
|
+
// (radius > padding clips child corners). Both px are read off the installed
|
|
131
|
+
// scale: the knob's `$radius` key against the `panelPadding` role's `$space`.
|
|
132
|
+
// Containers step two radius keys above controls; large and full hit the cap.
|
|
133
|
+
|
|
134
|
+
const radiusKeyMap: Record<Knobs["borderRadius"], "0" | "4" | "6" | "8" | "12"> = {
|
|
135
|
+
none: "0",
|
|
136
|
+
small: "4",
|
|
137
|
+
medium: "6",
|
|
138
|
+
large: "8",
|
|
139
|
+
full: "12",
|
|
173
140
|
};
|
|
174
141
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
};
|
|
142
|
+
/** The installed scale's `panelPadding` role in px. */
|
|
143
|
+
export function panelPaddingPx(): number {
|
|
144
|
+
return getActiveScaleTokens().space[spacingRoles.panelPadding];
|
|
145
|
+
}
|
|
180
146
|
|
|
181
147
|
/**
|
|
182
148
|
* Cap a container's radius at its own padding (Axiom 1 CLIP: "my children's
|
|
183
149
|
* pixel clips first → cap at padding"). Returns the radius token untouched
|
|
184
150
|
* while it fits, or the padding px number once the token would exceed it.
|
|
185
151
|
*/
|
|
186
|
-
export function capContainerRadius(
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
152
|
+
export function capContainerRadius(borderRadius: Knobs["borderRadius"]): string | number {
|
|
153
|
+
const radiusPx = getActiveScaleTokens().radius[radiusKeyMap[borderRadius]];
|
|
154
|
+
const paddingPx = panelPaddingPx();
|
|
155
|
+
return radiusPx > paddingPx ? paddingPx : containerRadiusMap[borderRadius];
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export function capFieldRadius(borderRadius: Knobs["borderRadius"]): string | number {
|
|
159
|
+
const token = borderRadiusMap[borderRadius];
|
|
160
|
+
const radiusPx = getActiveScaleTokens().radius[token.slice(1) as "0" | "2" | "4" | "6" | "12"];
|
|
161
|
+
const paddingPx = panelPaddingPx();
|
|
162
|
+
return radiusPx > paddingPx ? paddingPx : token;
|
|
193
163
|
}
|
|
194
164
|
|
|
195
165
|
/**
|
|
@@ -208,93 +178,93 @@ export function radiusStopFromToken(token: unknown): Knobs["borderRadius"] | und
|
|
|
208
178
|
* `StackedRow` is one row of a vertical stack: it also carries
|
|
209
179
|
* `data-stack-position`, and only its outer corners take the cap.
|
|
210
180
|
* `OwnInset` pads with a fixed token instead of panelPadding, so the audit
|
|
211
|
-
* caps it at its measured padding
|
|
181
|
+
* caps it at its measured padding.
|
|
212
182
|
*/
|
|
213
183
|
export type CappedContainer = "PageSection" | "TextArea" | "StackedRow" | "OwnInset";
|
|
214
184
|
|
|
215
185
|
export interface ContainerCapDeclaration {
|
|
216
186
|
"data-constraint-container": CappedContainer;
|
|
217
187
|
"data-radius-knob": Knobs["borderRadius"];
|
|
218
|
-
"data-space-knob": Knobs["space"];
|
|
219
188
|
}
|
|
220
189
|
|
|
221
190
|
/**
|
|
222
|
-
* Declare a container that spreads `containerRadius`, with the knob
|
|
191
|
+
* Declare a container that spreads `containerRadius`, with the knob stop it
|
|
223
192
|
* resolved, so the constraint audit checks its painted corners against
|
|
224
|
-
* `capContainerRadius` for
|
|
225
|
-
*
|
|
226
|
-
* Returns nothing when `radiusToken` is not a
|
|
227
|
-
* `
|
|
193
|
+
* `capContainerRadius` for that stop on the page's scale, or `capFieldRadius`
|
|
194
|
+
* for TextArea's `fieldRadius`. The input is always the CONTROL token. The cap is a
|
|
195
|
+
* padding px value, not a radius token. Returns nothing when `radiusToken` is not a
|
|
196
|
+
* `borderRadiusMap` token: a part whose radius did not come from the knob
|
|
228
197
|
* makes no claim.
|
|
229
198
|
*
|
|
230
199
|
* @example
|
|
231
|
-
* <YStack {...knobProps.containerRadius} {...containerCapProps("PageSection", knobProps.borderRadius.borderRadius
|
|
200
|
+
* <YStack {...knobProps.containerRadius} {...containerCapProps("PageSection", knobProps.borderRadius.borderRadius)} />
|
|
232
201
|
*/
|
|
233
202
|
export function containerCapProps(
|
|
234
203
|
container: CappedContainer,
|
|
235
204
|
radiusToken: unknown,
|
|
236
|
-
space: string,
|
|
237
205
|
): ContainerCapDeclaration | undefined {
|
|
238
206
|
const radiusKnob = radiusStopFromToken(radiusToken);
|
|
239
|
-
if (!radiusKnob
|
|
207
|
+
if (!radiusKnob) return undefined;
|
|
240
208
|
return {
|
|
241
209
|
"data-constraint-container": container,
|
|
242
210
|
"data-radius-knob": radiusKnob,
|
|
243
|
-
"data-space-knob": space as Knobs["space"],
|
|
244
211
|
};
|
|
245
212
|
}
|
|
246
213
|
|
|
247
|
-
const
|
|
248
|
-
small: "$2",
|
|
249
|
-
medium: "$4",
|
|
250
|
-
large: "$5",
|
|
251
|
-
} as const;
|
|
214
|
+
const sizeKeys = new Set<string>(SIZE_KEYS);
|
|
252
215
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
large: "$6",
|
|
258
|
-
} as const;
|
|
216
|
+
/** `sizeToken` when it is a `$size` key, else `$true`. */
|
|
217
|
+
export function normalizeSizeToken(sizeToken: string | undefined): SizeKey {
|
|
218
|
+
return sizeToken !== undefined && sizeKeys.has(sizeToken) ? (sizeToken as SizeKey) : "$true";
|
|
219
|
+
}
|
|
259
220
|
|
|
260
|
-
const
|
|
261
|
-
small: "$3",
|
|
262
|
-
medium: "$4",
|
|
263
|
-
large: "$5",
|
|
264
|
-
} as const;
|
|
221
|
+
const nestedControlRatio = 32 / 44;
|
|
265
222
|
|
|
266
|
-
/**
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
}
|
|
223
|
+
/** Painted px of a nested hit-target (calendar days) at `sizeToken`: 32/44 of the token's `$size`. */
|
|
224
|
+
export function nestedControlPx(sizeToken: string): number {
|
|
225
|
+
return Math.round(
|
|
226
|
+
getActiveScaleTokens().size[normalizeSizeToken(sizeToken)] * nestedControlRatio,
|
|
227
|
+
);
|
|
228
|
+
}
|
|
272
229
|
|
|
273
230
|
/**
|
|
274
|
-
*
|
|
275
|
-
*
|
|
276
|
-
*
|
|
231
|
+
* The whole key one below a size key ($4 / $true -> $3, $3 -> $2,
|
|
232
|
+
* $3.5 -> $3, floor $1). Parts drawn one step under their control (tags,
|
|
233
|
+
* avatars) read it.
|
|
277
234
|
*/
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
}
|
|
235
|
+
export function sizeKeyBelow(sizeToken: string): `$${number}` {
|
|
236
|
+
const key = normalizeSizeToken(sizeToken);
|
|
237
|
+
const step = key === "$true" ? 4 : Number.parseFloat(key.slice(1));
|
|
238
|
+
return `$${Math.min(16, Math.max(1, Math.ceil(step) - 1))}`;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/** The body font key at the size key; half steps round up, and `$true` stays `$true`. */
|
|
242
|
+
export function labelFontSizeFor(sizeToken: string): FontSizeTokens {
|
|
243
|
+
const key = normalizeSizeToken(sizeToken);
|
|
244
|
+
if (key === "$true") return "$true";
|
|
245
|
+
return `$${Math.min(16, Math.max(1, Math.ceil(Number.parseFloat(key.slice(1)))))}` as FontSizeTokens;
|
|
246
|
+
}
|
|
283
247
|
|
|
284
248
|
const headingFontMap = {
|
|
285
249
|
"sans-serif": "$heading",
|
|
286
|
-
serif:
|
|
287
|
-
mono:
|
|
288
|
-
slab:
|
|
289
|
-
rounded:
|
|
290
|
-
condensed:
|
|
291
|
-
cursive:
|
|
292
|
-
handwriting:
|
|
293
|
-
pixel:
|
|
294
|
-
blackletter:
|
|
295
|
-
geometric:
|
|
250
|
+
serif: `$${headingCategoryFontKeys.serif}`,
|
|
251
|
+
mono: `$${headingCategoryFontKeys.mono}`,
|
|
252
|
+
slab: `$${headingCategoryFontKeys.slab}`,
|
|
253
|
+
rounded: `$${headingCategoryFontKeys.rounded}`,
|
|
254
|
+
condensed: `$${headingCategoryFontKeys.condensed}`,
|
|
255
|
+
cursive: `$${headingCategoryFontKeys.cursive}`,
|
|
256
|
+
handwriting: `$${headingCategoryFontKeys.handwriting}`,
|
|
257
|
+
pixel: `$${headingCategoryFontKeys.pixel}`,
|
|
258
|
+
blackletter: `$${headingCategoryFontKeys.blackletter}`,
|
|
259
|
+
geometric: `$${headingCategoryFontKeys.geometric}`,
|
|
296
260
|
} as const;
|
|
297
261
|
|
|
262
|
+
export function getHeadingFontFamily(category: Knobs["headingFont"]): string {
|
|
263
|
+
const family = getConfig().fontsParsed[headingFontMap[category]]?.family;
|
|
264
|
+
const value = typeof family === "string" ? family : family?.val;
|
|
265
|
+
return typeof value === "string" ? value : fontCategoryStacks[category];
|
|
266
|
+
}
|
|
267
|
+
|
|
298
268
|
const bodyFontMap = {
|
|
299
269
|
"sans-serif": "$body",
|
|
300
270
|
serif: "$serif",
|
|
@@ -314,149 +284,66 @@ const fontWeightMap = {
|
|
|
314
284
|
bold: "700",
|
|
315
285
|
} as const;
|
|
316
286
|
|
|
317
|
-
//
|
|
318
|
-
//
|
|
319
|
-
//
|
|
320
|
-
//
|
|
321
|
-
// be cast to java.lang.Double" (SB-N-04). The numbers are the exact
|
|
322
|
-
// tokens.size values Tamagui resolves those tokens to ($1=20, $2=28, $4=44),
|
|
323
|
-
// so Tamagui consumers render identical shadows either way.
|
|
287
|
+
// Stops are `$size` keys; the shadow is Tamagui's getSizedElevation of that
|
|
288
|
+
// key's px on the active scale. Native gets the px NUMBER: knobProps.elevation
|
|
289
|
+
// is spread onto plain RN Views, and Android's RCTView `elevation` is a Double,
|
|
290
|
+
// so a token string kills the ReactHost (SB-N-04).
|
|
324
291
|
//
|
|
325
|
-
// Controls are flat at the default `small` stop (MPO-96
|
|
326
|
-
//
|
|
327
|
-
//
|
|
328
|
-
//
|
|
329
|
-
//
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
:
|
|
335
|
-
|
|
336
|
-
export const overlayElevationMap: Record<string, string | number | undefined> = isWeb
|
|
337
|
-
? { none: undefined, small: "$2", medium: "$3", large: "$5" }
|
|
338
|
-
: { none: undefined, small: 28, medium: 36, large: 52 };
|
|
339
|
-
|
|
340
|
-
/** Scheme the resolver was invoked with. Defaults light so unit tests stay stable. */
|
|
341
|
-
export type ColorScheme = "light" | "dark";
|
|
342
|
-
|
|
343
|
-
// Existing 12-step ramp (colorRules groups). Do not invent overlay tokens:
|
|
344
|
-
// dark elevation is a lighter $color step. Overlay/card rest on $color1;
|
|
345
|
-
// filled inputs rest on $color2 and step from there.
|
|
346
|
-
const elevationTintOverlay: Record<Elevation, string> = {
|
|
347
|
-
none: "$color1",
|
|
348
|
-
small: "$color2",
|
|
349
|
-
medium: "$color3",
|
|
350
|
-
large: "$color4",
|
|
351
|
-
};
|
|
352
|
-
|
|
353
|
-
const elevationTintInput: Record<Elevation, string> = {
|
|
354
|
-
none: "$color2",
|
|
355
|
-
small: "$color3",
|
|
356
|
-
medium: "$color4",
|
|
357
|
-
large: "$color5",
|
|
358
|
-
};
|
|
359
|
-
|
|
360
|
-
type ElevationKey = "$1" | "$2" | "$3" | "$4" | "$5";
|
|
361
|
-
|
|
362
|
-
// Axiom 14 light source: y-offset:blur = 0.5. Metrics match the maps
|
|
363
|
-
// InputParts / getElevationWrapperProps already shipped. `$3` and `$5` are
|
|
364
|
-
// the overlay-only stops (MPO-99): the reference §8 popper and dialog
|
|
365
|
-
// shadows. Native paints one shadow, so it takes the first web layer.
|
|
366
|
-
const elevationShadowMetrics: Record<
|
|
367
|
-
ElevationKey,
|
|
368
|
-
{ offsetY: number; radius: number; android: number }
|
|
369
|
-
> = {
|
|
370
|
-
$1: { offsetY: 1, radius: 2, android: 2 },
|
|
371
|
-
$2: { offsetY: 3, radius: 6, android: 4 },
|
|
372
|
-
$3: { offsetY: 4, radius: 8, android: 6 },
|
|
373
|
-
$4: { offsetY: 10, radius: 20, android: 8 },
|
|
374
|
-
$5: { offsetY: 12, radius: 24, android: 12 },
|
|
375
|
-
};
|
|
376
|
-
|
|
377
|
-
const elevationKeyAlias: Record<string, ElevationKey> = {
|
|
378
|
-
$1: "$1",
|
|
379
|
-
$2: "$2",
|
|
380
|
-
$3: "$3",
|
|
381
|
-
$4: "$4",
|
|
382
|
-
$5: "$5",
|
|
383
|
-
"20": "$1",
|
|
384
|
-
"28": "$2",
|
|
385
|
-
"36": "$3",
|
|
386
|
-
"44": "$4",
|
|
387
|
-
"52": "$5",
|
|
388
|
-
};
|
|
389
|
-
|
|
390
|
-
const webShadowLight: Record<ElevationKey, string> = {
|
|
391
|
-
$1: "0 1px 2px rgba(0,0,0,0.12), 0 0 1px rgba(0,0,0,0.08)",
|
|
392
|
-
$2: "0 3px 6px rgba(0,0,0,0.15), 0 2px 4px rgba(0,0,0,0.10)",
|
|
393
|
-
$3: "0 4px 8px rgba(0,0,0,0.10), 0 12px 32px rgba(0,0,0,0.08)",
|
|
394
|
-
$4: "0 10px 20px rgba(0,0,0,0.15), 0 3px 6px rgba(0,0,0,0.10)",
|
|
395
|
-
$5: "0 12px 24px rgba(0,0,0,0.12)",
|
|
292
|
+
// Controls are flat at the default `small` stop (MPO-96). Overlays climb a
|
|
293
|
+
// distinct ladder above the control map so a dialog over a scrim and a Button
|
|
294
|
+
// on the page never read as the same distance (DG-OVL-01 / E-OVERLAY). The
|
|
295
|
+
// overlay default is `$4`, the control height: on the 40px default scale `$3`
|
|
296
|
+
// (32px) casts a 22px band, under the 24px that lifts a dialog off its scrim.
|
|
297
|
+
export const elevationMap: Record<Elevation, SizeKey | undefined> = {
|
|
298
|
+
none: undefined,
|
|
299
|
+
small: undefined,
|
|
300
|
+
medium: "$2",
|
|
301
|
+
large: "$4",
|
|
396
302
|
};
|
|
397
303
|
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
$4: "0 10px 20px rgba(255,255,255,0.12), 0 3px 6px rgba(255,255,255,0.08)",
|
|
404
|
-
$5: "0 12px 24px rgba(255,255,255,0.12)",
|
|
304
|
+
export const overlayElevationMap: Record<Elevation, SizeKey | undefined> = {
|
|
305
|
+
none: undefined,
|
|
306
|
+
small: "$4",
|
|
307
|
+
medium: "$4.5",
|
|
308
|
+
large: "$5",
|
|
405
309
|
};
|
|
406
310
|
|
|
407
|
-
|
|
408
|
-
$1: 0.12,
|
|
409
|
-
$2: 0.15,
|
|
410
|
-
$3: 0.1,
|
|
411
|
-
$4: 0.15,
|
|
412
|
-
$5: 0.12,
|
|
413
|
-
};
|
|
311
|
+
export type ColorScheme = "light" | "dark";
|
|
414
312
|
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
$4: 0.12,
|
|
420
|
-
$5: 0.12,
|
|
421
|
-
};
|
|
313
|
+
export function sizedElevation(px: number): { offsetY: number; radius: number; android: number } {
|
|
314
|
+
const offsetY = Math.round(px / 4 + 1);
|
|
315
|
+
return { offsetY, radius: Math.round(px / 2 + 2), android: 2 * offsetY };
|
|
316
|
+
}
|
|
422
317
|
|
|
423
|
-
function
|
|
424
|
-
|
|
425
|
-
return elevationKeyAlias[String(token)];
|
|
318
|
+
function platformElevation(key: SizeKey | undefined): string | number | undefined {
|
|
319
|
+
return key && (isWeb ? key : getActiveScaleTokens().size[key]);
|
|
426
320
|
}
|
|
427
321
|
|
|
428
|
-
function elevationPaint(
|
|
429
|
-
token: string | number | undefined,
|
|
430
|
-
scheme: ColorScheme,
|
|
431
|
-
): ElevationPaintProps {
|
|
432
|
-
const key = elevationTokenKey(token);
|
|
322
|
+
function elevationPaint(key: SizeKey | undefined): ElevationPaintProps {
|
|
433
323
|
if (!key) return {};
|
|
434
|
-
const
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
324
|
+
const { offsetY, radius } = sizedElevation(getActiveScaleTokens().size[key]);
|
|
325
|
+
return {
|
|
326
|
+
shadowColor: "$shadowColor",
|
|
327
|
+
shadowOffset: { width: 0, height: offsetY },
|
|
328
|
+
shadowOpacity: 1,
|
|
329
|
+
shadowRadius: radius,
|
|
330
|
+
...(isWeb ? { style: { boxShadow: `0px ${offsetY}px ${radius}px var(--shadowColor)` } } : {}),
|
|
441
331
|
};
|
|
442
|
-
if (isWeb) {
|
|
443
|
-
paint.style = { boxShadow: dark ? webShadowDark[key] : webShadowLight[key] };
|
|
444
|
-
}
|
|
445
|
-
return paint;
|
|
446
332
|
}
|
|
447
333
|
|
|
448
|
-
function elevationChromeFor(
|
|
449
|
-
token: string | number | undefined,
|
|
450
|
-
scheme: ColorScheme,
|
|
451
|
-
): ElevationChromeProps {
|
|
452
|
-
const key = elevationTokenKey(token);
|
|
334
|
+
function elevationChromeFor(key: SizeKey | undefined): ElevationChromeProps {
|
|
453
335
|
if (!key) return {};
|
|
454
336
|
return {
|
|
455
|
-
...elevationPaint(
|
|
456
|
-
elevation:
|
|
337
|
+
...elevationPaint(key),
|
|
338
|
+
elevation: sizedElevation(getActiveScaleTokens().size[key]).android,
|
|
457
339
|
};
|
|
458
340
|
}
|
|
459
341
|
|
|
342
|
+
// v5's surface2 template steps: fill and edge two palette steps above the
|
|
343
|
+
// base `$background`/`$borderColor` (color2/color4), one above Card's surface1.
|
|
344
|
+
// The fill alone would land on the base edge step and erase the border.
|
|
345
|
+
const surface2 = { fill: "$color4", edge: "$color6" } as const;
|
|
346
|
+
|
|
460
347
|
// Legibility floor: body/label text must never land below ~4.5:1 against its
|
|
461
348
|
// surface. $color11 is the lowest ramp step that clears AA on color1/2
|
|
462
349
|
// surfaces, so "low" clamps there ($color9 measured 1.29-3.43:1 — SB-R-01).
|
|
@@ -527,10 +414,7 @@ function resolveDisabled(disabledStyle: Knobs["disabledStyle"]): DisabledRecipe
|
|
|
527
414
|
|
|
528
415
|
// ── State style resolver ──────────────────────────────────────
|
|
529
416
|
|
|
530
|
-
function resolveStateKnobs(
|
|
531
|
-
state: StateKnobs,
|
|
532
|
-
scheme: ColorScheme,
|
|
533
|
-
): {
|
|
417
|
+
function resolveStateKnobs(state: StateKnobs): {
|
|
534
418
|
control: ControlStateProps;
|
|
535
419
|
text: TextStateProps;
|
|
536
420
|
elevation: ElevationStateProps;
|
|
@@ -548,16 +432,10 @@ function resolveStateKnobs(
|
|
|
548
432
|
if (state.borderRadius !== undefined) {
|
|
549
433
|
control.borderRadius = borderRadiusMap[state.borderRadius];
|
|
550
434
|
}
|
|
551
|
-
if (state.space !== undefined) {
|
|
552
|
-
control.padding = gapMap[state.space];
|
|
553
|
-
}
|
|
554
435
|
if (state.elevation !== undefined) {
|
|
555
436
|
const token = elevationMap[state.elevation];
|
|
556
|
-
elevation.elevation = token;
|
|
557
|
-
Object.assign(elevation, elevationPaint(token
|
|
558
|
-
if (scheme === "dark" && state.elevation !== "none") {
|
|
559
|
-
elevation.backgroundColor = elevationTintOverlay[state.elevation];
|
|
560
|
-
}
|
|
437
|
+
elevation.elevation = platformElevation(token);
|
|
438
|
+
Object.assign(elevation, elevationPaint(token));
|
|
561
439
|
}
|
|
562
440
|
if (state.textAccent !== undefined) {
|
|
563
441
|
text.color = textAccentColorMap[state.textAccent];
|
|
@@ -576,28 +454,49 @@ function resolveStateKnobs(
|
|
|
576
454
|
|
|
577
455
|
// ── Resolver ──────────────────────────────────────────────────
|
|
578
456
|
|
|
457
|
+
/** Vertical inset of a top bar; with the default control height it lands the bar at 52. */
|
|
458
|
+
const barPaddingToken = "$2";
|
|
459
|
+
/**
|
|
460
|
+
* A content card on a painted parent sits one ramp step off it, so the box
|
|
461
|
+
* reads without a shadow (nested surfaces never re-elevate).
|
|
462
|
+
*/
|
|
463
|
+
export const paintedCardFill = "$color2";
|
|
464
|
+
|
|
465
|
+
/** What the subtree supplies beyond knobs: the pointer surface and the control size token. */
|
|
466
|
+
export interface ResolveKnobsEnvironment {
|
|
467
|
+
touch?: boolean;
|
|
468
|
+
/** Tamagui size key the controls render at. Default `$true`. */
|
|
469
|
+
sizeToken?: string;
|
|
470
|
+
/**
|
|
471
|
+
* An ancestor already paints a surface box: `elevatedSurface` goes border
|
|
472
|
+
* only (no second shadow) and `cardSurface` steps one fill off the parent.
|
|
473
|
+
*/
|
|
474
|
+
painted?: boolean;
|
|
475
|
+
}
|
|
476
|
+
|
|
579
477
|
export function resolveKnobs(
|
|
580
478
|
knobs: Knobs,
|
|
581
479
|
override?: KnobPropsOverride,
|
|
582
480
|
scheme: ColorScheme = "light",
|
|
583
|
-
environment?:
|
|
481
|
+
environment?: ResolveKnobsEnvironment,
|
|
584
482
|
): ResolvedKnobs {
|
|
585
483
|
const radiusToken = borderRadiusMap[knobs.borderRadius];
|
|
586
484
|
const nestedRadiusToken = nestedRadiusMap[knobs.borderRadius];
|
|
587
|
-
const sizeToken =
|
|
588
|
-
const
|
|
485
|
+
const sizeToken = normalizeSizeToken(environment?.sizeToken);
|
|
486
|
+
const painted = environment?.painted ?? false;
|
|
487
|
+
const nestedPx = nestedControlPx(sizeToken);
|
|
589
488
|
const nestedHitSlop = pressTargetHitSlop(nestedPx);
|
|
590
|
-
const
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
});
|
|
594
|
-
const
|
|
595
|
-
const
|
|
489
|
+
const touch = environment?.touch ?? (isTouchable || isWebTouchable);
|
|
490
|
+
const sizeRecipe = sizeRecipeForToken(sizeToken, { touch });
|
|
491
|
+
const menuRowRecipe = sizeRecipeForToken(sizeToken, { touch, family: "menuRow" });
|
|
492
|
+
const tabRecipe = sizeRecipeForToken(sizeToken, { touch, family: "tab" });
|
|
493
|
+
const scaleTokens = getActiveScaleTokens();
|
|
494
|
+
const paddingToken = spacingRoles.panelPadding;
|
|
596
495
|
const fontWeightValue = fontWeightMap[knobs.fontWeight];
|
|
597
496
|
const bodyFontValue = bodyFontMap[knobs.bodyFont] ?? "$body";
|
|
598
|
-
const
|
|
497
|
+
const motion = motionFor(knobs.animation);
|
|
599
498
|
|
|
600
|
-
const containerRadiusValue = capContainerRadius(knobs.borderRadius
|
|
499
|
+
const containerRadiusValue = capContainerRadius(knobs.borderRadius);
|
|
601
500
|
|
|
602
501
|
// Corner smoothing rides every radius-bearing fragment so consumers need
|
|
603
502
|
// zero edits (KNOB-TOTALITY, LC-18). Identity guard (Axiom 3 NULL +
|
|
@@ -624,29 +523,18 @@ export function resolveKnobs(
|
|
|
624
523
|
: borderWidthMap[knobs.borderWidth];
|
|
625
524
|
const elevationToken = elevationMap[knobs.elevation];
|
|
626
525
|
const overlayElevationToken = overlayElevationMap[knobs.elevation];
|
|
627
|
-
const overlayPaintProps = elevationPaint(overlayElevationToken
|
|
628
|
-
const elevationChrome = elevationChromeFor(elevationToken
|
|
629
|
-
const dark = scheme === "dark";
|
|
630
|
-
// MPO-19 X5: `$background` (not `$color1`) so surface1/2/3 sub-themes move
|
|
631
|
-
// the fill. Dark + elevation still steps the filled rest fill up the ramp
|
|
632
|
-
// (LC-85).
|
|
633
|
-
const overlayFill =
|
|
634
|
-
dark && knobs.elevation !== "none" ? elevationTintOverlay[knobs.elevation] : "$background";
|
|
526
|
+
const overlayPaintProps = elevationPaint(overlayElevationToken);
|
|
527
|
+
const elevationChrome = elevationChromeFor(elevationToken);
|
|
635
528
|
const textAccentColor = textAccentColorMap[knobs.textAccent] ?? "$color";
|
|
636
529
|
// One input surface (DF-19 / Axiom 5 ONE SOURCE): Timeline's composer,
|
|
637
530
|
// RichEditor and form fields agree on what an input surface is.
|
|
638
531
|
// MPO-19 X6: `$background` not `$color2` so a surface sub-theme can move it.
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
const hover = resolveStateKnobs(knobs.hover, scheme);
|
|
647
|
-
const press = resolveStateKnobs(knobs.press, scheme);
|
|
648
|
-
const focus = resolveStateKnobs(knobs.focus, scheme);
|
|
649
|
-
const focusVisible = resolveStateKnobs(knobs.focusVisible, scheme);
|
|
532
|
+
const inputBackground = outlined ? "transparent" : "$background";
|
|
533
|
+
|
|
534
|
+
const hover = resolveStateKnobs(knobs.hover);
|
|
535
|
+
const press = resolveStateKnobs(knobs.press);
|
|
536
|
+
const focus = resolveStateKnobs(knobs.focus);
|
|
537
|
+
const focusVisible = resolveStateKnobs(knobs.focusVisible);
|
|
650
538
|
|
|
651
539
|
const nonEmpty = <T extends object>(obj: T): T | undefined =>
|
|
652
540
|
Object.keys(obj).length > 0 ? obj : undefined;
|
|
@@ -695,9 +583,11 @@ export function resolveKnobs(
|
|
|
695
583
|
// spread this after their radius-bearing fragment. Non-container parts
|
|
696
584
|
// keep the uncapped `borderRadius` fragment.
|
|
697
585
|
containerRadius: { borderRadius: containerRadiusValue, ...containerSmoothing },
|
|
698
|
-
|
|
699
|
-
|
|
586
|
+
fieldRadius: { borderRadius: capFieldRadius(knobs.borderRadius), ...containerSmoothing },
|
|
587
|
+
elevation: platformElevation(elevationToken),
|
|
588
|
+
overlayElevation: platformElevation(overlayElevationToken),
|
|
700
589
|
elevationChrome,
|
|
590
|
+
thumbShadow: elevationChromeFor("$0.5"),
|
|
701
591
|
scheme,
|
|
702
592
|
surface: {
|
|
703
593
|
backgroundColor: "$background",
|
|
@@ -705,24 +595,33 @@ export function resolveKnobs(
|
|
|
705
595
|
borderWidth: borderWidthValue,
|
|
706
596
|
},
|
|
707
597
|
cardSurface: {
|
|
708
|
-
backgroundColor:
|
|
598
|
+
backgroundColor: painted ? paintedCardFill : "$background",
|
|
709
599
|
borderRadius: containerRadiusValue,
|
|
710
600
|
padding: paddingToken,
|
|
711
601
|
...containerSmoothing,
|
|
712
602
|
},
|
|
713
|
-
elevatedSurface:
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
603
|
+
elevatedSurface: painted
|
|
604
|
+
? {
|
|
605
|
+
backgroundColor: "$background",
|
|
606
|
+
borderColor: "$borderColor",
|
|
607
|
+
borderWidth: Math.max(borderWidthValue, borderWidthMap.small),
|
|
608
|
+
borderRadius: radiusToken,
|
|
609
|
+
elevation: undefined,
|
|
610
|
+
...scaleSmoothing,
|
|
611
|
+
}
|
|
612
|
+
: {
|
|
613
|
+
backgroundColor: scheme === "dark" ? surface2.fill : "$background",
|
|
614
|
+
borderColor: scheme === "dark" ? surface2.edge : "$borderColor",
|
|
615
|
+
borderWidth: borderWidthValue,
|
|
616
|
+
borderRadius: radiusToken,
|
|
617
|
+
elevation: platformElevation(overlayElevationToken),
|
|
618
|
+
...overlayPaintProps,
|
|
619
|
+
...scaleSmoothing,
|
|
620
|
+
},
|
|
722
621
|
featureSurface: {
|
|
723
622
|
backgroundColor: "$background",
|
|
724
623
|
borderRadius: featureRadiusMap[knobs.borderRadius],
|
|
725
|
-
padding:
|
|
624
|
+
padding: spacingRoles.featurePadding,
|
|
726
625
|
// Bento marketing/auth card shadow: ~0px 9px 12px rgba(0,0,0,0.02) —
|
|
727
626
|
// softer/wider than any elevationMap token, so explicit shadow props.
|
|
728
627
|
shadowColor: "rgba(0,0,0,0.02)",
|
|
@@ -733,13 +632,14 @@ export function resolveKnobs(
|
|
|
733
632
|
},
|
|
734
633
|
inputSurface: { borderWidth: borderWidthValue },
|
|
735
634
|
panelPadding: { padding: paddingToken },
|
|
736
|
-
|
|
737
|
-
|
|
635
|
+
gapSm: { gap: spacingRoles.gapSm },
|
|
636
|
+
gap: { gap: spacingRoles.gap },
|
|
637
|
+
gapLg: { gap: spacingRoles.gapLg },
|
|
738
638
|
sizeToken,
|
|
739
639
|
label: {
|
|
740
|
-
fontSize:
|
|
640
|
+
fontSize: labelFontSizeFor(sizeToken),
|
|
741
641
|
fontFamily: bodyFontValue,
|
|
742
|
-
fontWeight:
|
|
642
|
+
...(knobs.fontWeight === "bold" ? { fontWeight: "700" } : {}),
|
|
743
643
|
},
|
|
744
644
|
nestedControl: {
|
|
745
645
|
px: nestedPx,
|
|
@@ -758,25 +658,50 @@ export function resolveKnobs(
|
|
|
758
658
|
},
|
|
759
659
|
controlType: { fontSize: sizeRecipe.fontSize },
|
|
760
660
|
controlIcon: { width: sizeRecipe.iconSize, height: sizeRecipe.iconSize },
|
|
661
|
+
menuRow: {
|
|
662
|
+
minHeight: menuRowRecipe.height,
|
|
663
|
+
paddingHorizontal: menuRowRecipe.paddingHorizontal,
|
|
664
|
+
gap: menuRowRecipe.gap,
|
|
665
|
+
},
|
|
666
|
+
menuRowType: { fontSize: menuRowRecipe.fontSize },
|
|
667
|
+
tab: {
|
|
668
|
+
height: tabRecipe.height,
|
|
669
|
+
paddingHorizontal: tabRecipe.paddingHorizontal,
|
|
670
|
+
gap: tabRecipe.gap,
|
|
671
|
+
},
|
|
672
|
+
tabType: { fontSize: tabRecipe.fontSize },
|
|
673
|
+
tabHitSlop: pressTargetHitSlop(tabRecipe.height),
|
|
674
|
+
bar: {
|
|
675
|
+
frame: {
|
|
676
|
+
paddingVertical: barPaddingToken,
|
|
677
|
+
paddingHorizontal: paddingToken,
|
|
678
|
+
minHeight: scaleTokens.size.$true + 2 * scaleTokens.space[barPaddingToken],
|
|
679
|
+
},
|
|
680
|
+
sizeToken: "$3",
|
|
681
|
+
},
|
|
761
682
|
heading: {
|
|
762
683
|
fontFamily: headingFontMap[knobs.headingFont],
|
|
763
|
-
fontWeight:
|
|
684
|
+
...(knobs.fontWeight === "bold" ? { fontWeight: "700" } : {}),
|
|
764
685
|
},
|
|
765
686
|
pageTitle: resolvePageTitleScale(knobs.pageTitleScale ?? "moderate"),
|
|
766
687
|
body: {
|
|
767
688
|
fontFamily: bodyFontValue,
|
|
768
689
|
fontWeight: fontWeightValue,
|
|
769
690
|
},
|
|
691
|
+
typography: {
|
|
692
|
+
mutedMono: { fontFamily: "$mono", color: textAccentColorMap.low },
|
|
693
|
+
tabular: isWeb
|
|
694
|
+
? { style: { fontVariantNumeric: "tabular-nums" } }
|
|
695
|
+
: { fontVariant: ["tabular-nums"] },
|
|
696
|
+
},
|
|
770
697
|
textWeight: { fontWeight: fontWeightValue },
|
|
771
|
-
|
|
698
|
+
motion,
|
|
699
|
+
transition: motion.control.transition,
|
|
772
700
|
outlined,
|
|
773
701
|
pointy: knobs.borderRadius === "none",
|
|
774
702
|
textAccent: knobs.textAccent,
|
|
775
703
|
textAccentColor,
|
|
776
704
|
inputBackground,
|
|
777
|
-
space: knobs.space,
|
|
778
|
-
size: knobs.size,
|
|
779
|
-
density: knobs.density,
|
|
780
705
|
// House decisions — behavioral pass-through (no token mapping).
|
|
781
706
|
fieldLabelPlacement: knobs.fieldLabelPlacement,
|
|
782
707
|
requiredMarking: knobs.requiredMarking,
|