@newtonedev/components 0.1.0 → 0.1.2

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 (181) hide show
  1. package/dist/AppShell/AppShell.d.ts +4 -0
  2. package/dist/AppShell/AppShell.d.ts.map +1 -0
  3. package/dist/AppShell/AppShell.styles.d.ts +16 -0
  4. package/dist/AppShell/AppShell.styles.d.ts.map +1 -0
  5. package/dist/AppShell/AppShell.types.d.ts +8 -0
  6. package/dist/AppShell/AppShell.types.d.ts.map +1 -0
  7. package/dist/AppShell/index.d.ts +3 -0
  8. package/dist/AppShell/index.d.ts.map +1 -0
  9. package/dist/Button/Button.d.ts +9 -4
  10. package/dist/Button/Button.d.ts.map +1 -1
  11. package/dist/Button/Button.styles.d.ts +33 -26
  12. package/dist/Button/Button.styles.d.ts.map +1 -1
  13. package/dist/Button/Button.types.d.ts +17 -2
  14. package/dist/Button/Button.types.d.ts.map +1 -1
  15. package/dist/ColorScaleSlider/ColorScaleSlider.d.ts +13 -0
  16. package/dist/ColorScaleSlider/ColorScaleSlider.d.ts.map +1 -0
  17. package/dist/ColorScaleSlider/ColorScaleSlider.styles.d.ts +54 -0
  18. package/dist/ColorScaleSlider/ColorScaleSlider.styles.d.ts.map +1 -0
  19. package/dist/ColorScaleSlider/ColorScaleSlider.types.d.ts +25 -0
  20. package/dist/ColorScaleSlider/ColorScaleSlider.types.d.ts.map +1 -0
  21. package/dist/ColorScaleSlider/index.d.ts +3 -0
  22. package/dist/ColorScaleSlider/index.d.ts.map +1 -0
  23. package/dist/Frame/Frame.d.ts +48 -0
  24. package/dist/Frame/Frame.d.ts.map +1 -0
  25. package/dist/Frame/Frame.styles.d.ts +39 -0
  26. package/dist/Frame/Frame.styles.d.ts.map +1 -0
  27. package/dist/Frame/Frame.types.d.ts +115 -0
  28. package/dist/Frame/Frame.types.d.ts.map +1 -0
  29. package/dist/Frame/Frame.utils.d.ts +39 -0
  30. package/dist/Frame/Frame.utils.d.ts.map +1 -0
  31. package/dist/Frame/index.d.ts +4 -0
  32. package/dist/Frame/index.d.ts.map +1 -0
  33. package/dist/HueSlider/HueSlider.d.ts +1 -1
  34. package/dist/HueSlider/HueSlider.d.ts.map +1 -1
  35. package/dist/HueSlider/HueSlider.styles.d.ts +47 -5
  36. package/dist/HueSlider/HueSlider.styles.d.ts.map +1 -1
  37. package/dist/HueSlider/HueSlider.types.d.ts +1 -0
  38. package/dist/HueSlider/HueSlider.types.d.ts.map +1 -1
  39. package/dist/Icon/Icon.d.ts +36 -0
  40. package/dist/Icon/Icon.d.ts.map +1 -0
  41. package/dist/Navbar/Navbar.d.ts +4 -0
  42. package/dist/Navbar/Navbar.d.ts.map +1 -0
  43. package/dist/Navbar/Navbar.styles.d.ts +31 -0
  44. package/dist/Navbar/Navbar.styles.d.ts.map +1 -0
  45. package/dist/Navbar/Navbar.types.d.ts +14 -0
  46. package/dist/Navbar/Navbar.types.d.ts.map +1 -0
  47. package/dist/Navbar/index.d.ts +3 -0
  48. package/dist/Navbar/index.d.ts.map +1 -0
  49. package/dist/Popover/Popover.d.ts +4 -0
  50. package/dist/Popover/Popover.d.ts.map +1 -0
  51. package/dist/Popover/Popover.styles.d.ts +9 -0
  52. package/dist/Popover/Popover.styles.d.ts.map +1 -0
  53. package/dist/Popover/Popover.types.d.ts +37 -0
  54. package/dist/Popover/Popover.types.d.ts.map +1 -0
  55. package/dist/Popover/index.d.ts +4 -0
  56. package/dist/Popover/index.d.ts.map +1 -0
  57. package/dist/Popover/usePopover.d.ts +3 -0
  58. package/dist/Popover/usePopover.d.ts.map +1 -0
  59. package/dist/Select/Select.d.ts +1 -8
  60. package/dist/Select/Select.d.ts.map +1 -1
  61. package/dist/Select/Select.styles.d.ts +32 -5
  62. package/dist/Select/Select.styles.d.ts.map +1 -1
  63. package/dist/Select/Select.types.d.ts +25 -1
  64. package/dist/Select/Select.types.d.ts.map +1 -1
  65. package/dist/Select/SelectOption.d.ts +13 -0
  66. package/dist/Select/SelectOption.d.ts.map +1 -0
  67. package/dist/Select/useSelect.d.ts +15 -0
  68. package/dist/Select/useSelect.d.ts.map +1 -0
  69. package/dist/Sidebar/Sidebar.d.ts +4 -0
  70. package/dist/Sidebar/Sidebar.d.ts.map +1 -0
  71. package/dist/Sidebar/Sidebar.styles.d.ts +31 -0
  72. package/dist/Sidebar/Sidebar.styles.d.ts.map +1 -0
  73. package/dist/Sidebar/Sidebar.types.d.ts +14 -0
  74. package/dist/Sidebar/Sidebar.types.d.ts.map +1 -0
  75. package/dist/Sidebar/index.d.ts +3 -0
  76. package/dist/Sidebar/index.d.ts.map +1 -0
  77. package/dist/Slider/Slider.d.ts +1 -1
  78. package/dist/Slider/Slider.d.ts.map +1 -1
  79. package/dist/Slider/Slider.styles.d.ts +48 -8
  80. package/dist/Slider/Slider.styles.d.ts.map +1 -1
  81. package/dist/Slider/Slider.types.d.ts +1 -0
  82. package/dist/Slider/Slider.types.d.ts.map +1 -1
  83. package/dist/TextInput/TextInput.styles.d.ts +3 -1
  84. package/dist/TextInput/TextInput.styles.d.ts.map +1 -1
  85. package/dist/Toggle/Toggle.styles.d.ts +2 -1
  86. package/dist/Toggle/Toggle.styles.d.ts.map +1 -1
  87. package/dist/fonts/GoogleFontLoader.d.ts +19 -0
  88. package/dist/fonts/GoogleFontLoader.d.ts.map +1 -0
  89. package/dist/fonts/IconFontLoader.d.ts +13 -0
  90. package/dist/fonts/IconFontLoader.d.ts.map +1 -0
  91. package/dist/fonts/buildGoogleFontsUrl.d.ts +17 -0
  92. package/dist/fonts/buildGoogleFontsUrl.d.ts.map +1 -0
  93. package/dist/fonts/googleFonts.d.ts +20 -0
  94. package/dist/fonts/googleFonts.d.ts.map +1 -0
  95. package/dist/index.cjs +2303 -205
  96. package/dist/index.cjs.map +1 -1
  97. package/dist/index.d.ts +27 -3
  98. package/dist/index.d.ts.map +1 -1
  99. package/dist/index.js +2279 -200
  100. package/dist/index.js.map +1 -1
  101. package/dist/registry/codegen.d.ts +11 -0
  102. package/dist/registry/codegen.d.ts.map +1 -0
  103. package/dist/registry/index.d.ts +4 -0
  104. package/dist/registry/index.d.ts.map +1 -0
  105. package/dist/registry/registry.d.ts +7 -0
  106. package/dist/registry/registry.d.ts.map +1 -0
  107. package/dist/registry/types.d.ts +32 -0
  108. package/dist/registry/types.d.ts.map +1 -0
  109. package/dist/theme/FrameContext.d.ts +24 -0
  110. package/dist/theme/FrameContext.d.ts.map +1 -0
  111. package/dist/theme/NewtoneProvider.d.ts.map +1 -1
  112. package/dist/theme/defaults.d.ts.map +1 -1
  113. package/dist/theme/types.d.ts +64 -1
  114. package/dist/theme/types.d.ts.map +1 -1
  115. package/dist/tokens/computeTokens.d.ts +55 -3
  116. package/dist/tokens/computeTokens.d.ts.map +1 -1
  117. package/dist/tokens/types.d.ts +52 -0
  118. package/dist/tokens/types.d.ts.map +1 -1
  119. package/dist/tokens/useTokens.d.ts +12 -9
  120. package/dist/tokens/useTokens.d.ts.map +1 -1
  121. package/package.json +1 -1
  122. package/src/AppShell/AppShell.styles.ts +20 -0
  123. package/src/AppShell/AppShell.tsx +17 -0
  124. package/src/AppShell/AppShell.types.ts +8 -0
  125. package/src/AppShell/index.ts +2 -0
  126. package/src/Button/Button.styles.ts +74 -41
  127. package/src/Button/Button.tsx +36 -17
  128. package/src/Button/Button.types.ts +20 -2
  129. package/src/Card/Card.styles.ts +2 -2
  130. package/src/ColorScaleSlider/ColorScaleSlider.styles.ts +60 -0
  131. package/src/ColorScaleSlider/ColorScaleSlider.tsx +156 -0
  132. package/src/ColorScaleSlider/ColorScaleSlider.types.ts +25 -0
  133. package/src/ColorScaleSlider/index.ts +2 -0
  134. package/src/Frame/Frame.styles.ts +213 -0
  135. package/src/Frame/Frame.tsx +242 -0
  136. package/src/Frame/Frame.types.ts +181 -0
  137. package/src/Frame/Frame.utils.ts +189 -0
  138. package/src/Frame/index.ts +21 -0
  139. package/src/HueSlider/HueSlider.styles.ts +58 -39
  140. package/src/HueSlider/HueSlider.tsx +97 -25
  141. package/src/HueSlider/HueSlider.types.ts +1 -0
  142. package/src/Icon/Icon.tsx +76 -0
  143. package/src/Navbar/Navbar.styles.ts +37 -0
  144. package/src/Navbar/Navbar.tsx +32 -0
  145. package/src/Navbar/Navbar.types.ts +14 -0
  146. package/src/Navbar/index.ts +2 -0
  147. package/src/Popover/Popover.styles.ts +39 -0
  148. package/src/Popover/Popover.tsx +103 -0
  149. package/src/Popover/Popover.types.ts +40 -0
  150. package/src/Popover/index.ts +3 -0
  151. package/src/Popover/usePopover.ts +26 -0
  152. package/src/Select/Select.styles.ts +49 -10
  153. package/src/Select/Select.tsx +127 -36
  154. package/src/Select/Select.types.ts +30 -1
  155. package/src/Select/SelectOption.tsx +104 -0
  156. package/src/Select/useSelect.ts +129 -0
  157. package/src/Sidebar/Sidebar.styles.ts +37 -0
  158. package/src/Sidebar/Sidebar.tsx +27 -0
  159. package/src/Sidebar/Sidebar.types.ts +14 -0
  160. package/src/Sidebar/index.ts +2 -0
  161. package/src/Slider/Slider.styles.ts +53 -25
  162. package/src/Slider/Slider.tsx +89 -24
  163. package/src/Slider/Slider.types.ts +1 -0
  164. package/src/TextInput/TextInput.styles.ts +9 -7
  165. package/src/Toggle/Toggle.styles.ts +4 -3
  166. package/src/fonts/GoogleFontLoader.tsx +63 -0
  167. package/src/fonts/IconFontLoader.tsx +49 -0
  168. package/src/fonts/buildGoogleFontsUrl.ts +31 -0
  169. package/src/fonts/googleFonts.ts +87 -0
  170. package/src/index.ts +70 -2
  171. package/src/registry/codegen.ts +132 -0
  172. package/src/registry/index.ts +17 -0
  173. package/src/registry/registry.ts +402 -0
  174. package/src/registry/types.ts +35 -0
  175. package/src/theme/FrameContext.tsx +29 -0
  176. package/src/theme/NewtoneProvider.tsx +9 -1
  177. package/src/theme/defaults.ts +51 -0
  178. package/src/theme/types.ts +66 -1
  179. package/src/tokens/computeTokens.ts +103 -46
  180. package/src/tokens/types.ts +52 -0
  181. package/src/tokens/useTokens.ts +30 -15
@@ -5,17 +5,34 @@ import type { DynamicRange, PaletteConfig } from 'newtone';
5
5
  */
6
6
  export type ColorMode = 'light' | 'dark';
7
7
 
8
+ /**
9
+ * Font configuration for a single font slot
10
+ */
11
+ export interface FontConfig {
12
+ readonly type: 'system' | 'google' | 'custom';
13
+ readonly family: string; // 'ui-monospace' | 'Roboto' | custom name
14
+ readonly customUrl?: string; // Supabase Storage URL for custom fonts
15
+ readonly fallback: string; // CSS fallback stack
16
+ }
17
+
8
18
  /**
9
19
  * Theme names for different UI contexts
10
20
  */
11
21
  export type ThemeName = 'neutral' | 'primary' | 'secondary' | 'strong';
12
22
 
13
23
  /**
14
- * Elevation levels for surfaces
24
+ * Elevation levels for surfaces (internal)
15
25
  * 0 = sunken, 1 = default, 2 = elevated
16
26
  */
17
27
  export type ElevationLevel = 0 | 1 | 2;
18
28
 
29
+ /**
30
+ * User-facing elevation for Frame component
31
+ * -2 = deeply sunken (inset shadow), -1 = sunken, 0 = default,
32
+ * 1 = elevated, 2 = prominently elevated (drop shadow)
33
+ */
34
+ export type FrameElevation = -2 | -1 | 0 | 1 | 2;
35
+
19
36
  /**
20
37
  * Theme mapping: which palette and normalizedValue to use for backgrounds/text
21
38
  * Based on playground theme preview logic
@@ -48,6 +65,54 @@ export interface NewtoneThemeConfig {
48
65
  readonly elevation: {
49
66
  readonly offsets: readonly [number, number, number]; // NV offsets for [sunken, default, elevated]
50
67
  };
68
+ readonly spacing: {
69
+ readonly xs: number; // 4 * multiplier
70
+ readonly sm: number; // 8 * multiplier
71
+ readonly md: number; // 12 * multiplier
72
+ readonly lg: number; // 16 * multiplier
73
+ readonly xl: number; // 24 * multiplier
74
+ readonly xxl: number; // 32 * multiplier
75
+ };
76
+ readonly radius: {
77
+ readonly none: number; // 0
78
+ readonly sm: number; // 4 * multiplier
79
+ readonly md: number; // 6 * multiplier
80
+ readonly lg: number; // 8 * multiplier
81
+ readonly xl: number; // 12 * multiplier
82
+ readonly pill: 999; // Fixed large value
83
+ };
84
+ readonly typography: {
85
+ readonly fonts: {
86
+ readonly mono: FontConfig;
87
+ readonly display: FontConfig;
88
+ readonly default: FontConfig;
89
+ };
90
+ readonly scale: {
91
+ readonly xs: number; // baseSize / ratio^2
92
+ readonly sm: number; // baseSize / ratio
93
+ readonly base: number; // baseSize
94
+ readonly md: number; // baseSize * ratio
95
+ readonly lg: number; // baseSize * ratio^2
96
+ readonly xl: number; // baseSize * ratio^3
97
+ readonly xxl: number; // baseSize * ratio^4
98
+ };
99
+ readonly lineHeight: {
100
+ readonly tight: number; // 1.25
101
+ readonly normal: number; // 1.5
102
+ readonly relaxed: number; // 1.75
103
+ };
104
+ readonly fontWeight: {
105
+ readonly regular: number; // 400
106
+ readonly medium: number; // 500
107
+ readonly semibold: number; // 600
108
+ readonly bold: number; // 700
109
+ };
110
+ };
111
+ readonly icons: {
112
+ readonly variant: 'outlined' | 'rounded' | 'sharp';
113
+ readonly weight: 100 | 200 | 300 | 400 | 500 | 600 | 700;
114
+ readonly autoGrade: boolean; // true = mode-aware grade (light=-25, dark=200)
115
+ };
51
116
  }
52
117
 
53
118
  /**
@@ -1,7 +1,16 @@
1
1
  import { getColor, getColorByContrast } from 'newtone';
2
- import type { ColorSystemConfig, ColorMode, ThemeMapping, ElevationLevel } from '../theme/types';
2
+ import type { PaletteConfig } from 'newtone';
3
+ import type { ColorSystemConfig, ColorMode, ThemeMapping, ElevationLevel, FontConfig } from '../theme/types';
3
4
  import type { ResolvedTokens } from './types';
4
5
 
6
+ /**
7
+ * Convert FontConfig to CSS font-family string
8
+ */
9
+ function fontConfigToFamily(font: FontConfig): string {
10
+ const family = font.family.includes(' ') ? `"${font.family}"` : font.family;
11
+ return `${family}, ${font.fallback}`;
12
+ }
13
+
5
14
  /**
6
15
  * Compute design tokens for a specific mode/theme/elevation combination.
7
16
  *
@@ -14,6 +23,10 @@ import type { ResolvedTokens } from './types';
14
23
  * @param themeMapping - Theme configuration (which palette and NV to use)
15
24
  * @param elevation - Elevation level (0=sunken, 1=default, 2=elevated)
16
25
  * @param elevationOffsets - NV offsets for each elevation level
26
+ * @param spacing - Spacing scale for paddings, gaps, and margins
27
+ * @param radius - Border radius scale for component roundness
28
+ * @param typography - Typography configuration with fonts and scales
29
+ * @param icons - Icon configuration with variant, weight, and auto-grade setting
17
30
  * @returns Resolved design tokens with all necessary colors
18
31
  *
19
32
  * @example
@@ -23,7 +36,11 @@ import type { ResolvedTokens } from './types';
23
36
  * 'light',
24
37
  * config.themes.neutral,
25
38
  * 1,
26
- * config.elevation.offsets
39
+ * config.elevation.offsets,
40
+ * config.spacing,
41
+ * config.radius,
42
+ * config.typography,
43
+ * config.icons
27
44
  * );
28
45
  * console.log(tokens.background.srgb); // { r: 0.95, g: 0.95, b: 0.95 }
29
46
  * ```
@@ -33,7 +50,20 @@ export function computeTokens(
33
50
  mode: ColorMode,
34
51
  themeMapping: ThemeMapping,
35
52
  elevation: ElevationLevel,
36
- elevationOffsets: readonly [number, number, number]
53
+ elevationOffsets: readonly [number, number, number],
54
+ spacing: { readonly xs: number; readonly sm: number; readonly md: number; readonly lg: number; readonly xl: number; readonly xxl: number },
55
+ radius: { readonly none: number; readonly sm: number; readonly md: number; readonly lg: number; readonly xl: number; readonly pill: 999 },
56
+ typography: {
57
+ readonly fonts: { readonly mono: FontConfig; readonly display: FontConfig; readonly default: FontConfig };
58
+ readonly scale: { readonly xs: number; readonly sm: number; readonly base: number; readonly md: number; readonly lg: number; readonly xl: number; readonly xxl: number };
59
+ readonly lineHeight: { readonly tight: number; readonly normal: number; readonly relaxed: number };
60
+ readonly fontWeight: { readonly regular: number; readonly medium: number; readonly semibold: number; readonly bold: number };
61
+ },
62
+ icons: {
63
+ readonly variant: 'outlined' | 'rounded' | 'sharp';
64
+ readonly weight: 100 | 200 | 300 | 400 | 500 | 600 | 700;
65
+ readonly autoGrade: boolean;
66
+ }
37
67
  ): ResolvedTokens {
38
68
  const { dynamicRange, palettes } = config;
39
69
  const palette = palettes[themeMapping.paletteIndex];
@@ -84,7 +114,7 @@ export function computeTokens(
84
114
  palette.paletteHueGrading
85
115
  );
86
116
 
87
- // Compute text colors with WCAG contrast requirements
117
+ // Compute text colors with WCAG contrast against actual background
88
118
  // Primary text: WCAG AA (4.5:1 for body text)
89
119
  const textPrimary = getColorByContrast(
90
120
  palette.hue,
@@ -93,7 +123,8 @@ export function computeTokens(
93
123
  4.5,
94
124
  effectiveTextMode,
95
125
  palette.desaturation,
96
- palette.paletteHueGrading
126
+ palette.paletteHueGrading,
127
+ background,
97
128
  );
98
129
 
99
130
  // Secondary text: Lower contrast (3.0:1 for captions)
@@ -104,7 +135,8 @@ export function computeTokens(
104
135
  3.0,
105
136
  effectiveTextMode,
106
137
  palette.desaturation,
107
- palette.paletteHueGrading
138
+ palette.paletteHueGrading,
139
+ background,
108
140
  );
109
141
 
110
142
  // Interactive colors: Use accent palette (index 1)
@@ -114,20 +146,35 @@ export function computeTokens(
114
146
  throw new Error('Accent palette (index 1) not found');
115
147
  }
116
148
 
117
- // Interactive base: WCAG AA contrast against current background
118
- const interactive = getColorByContrast(
119
- accentPalette.hue,
120
- accentPalette.saturation,
121
- dynamicRange,
122
- 4.5,
123
- effectiveTextMode,
124
- accentPalette.desaturation,
125
- accentPalette.paletteHueGrading
126
- );
149
+ // Resolve per-mode key color: light mode uses keyNormalizedValue, dark uses keyNormalizedValueDark
150
+ const resolveKeyNv = (p: PaletteConfig): number | undefined =>
151
+ mode === 'dark' ? p.keyNormalizedValueDark : p.keyNormalizedValue;
152
+
153
+ // Interactive base: use user-chosen key color or auto WCAG AA contrast
154
+ const accentKeyNv = resolveKeyNv(accentPalette);
155
+ const interactive = accentKeyNv !== undefined
156
+ ? getColor(
157
+ accentPalette.hue,
158
+ accentPalette.saturation,
159
+ dynamicRange,
160
+ accentKeyNv,
161
+ accentPalette.desaturation,
162
+ accentPalette.paletteHueGrading
163
+ )
164
+ : getColorByContrast(
165
+ accentPalette.hue,
166
+ accentPalette.saturation,
167
+ dynamicRange,
168
+ 4.5,
169
+ effectiveTextMode,
170
+ accentPalette.desaturation,
171
+ accentPalette.paletteHueGrading,
172
+ background,
173
+ );
127
174
 
128
175
  // Hover/active states: Shift NV slightly from interactive base
129
176
  // In light mode (light bg), go darker; in dark mode (dark bg), go lighter
130
- const interactiveNv = effectiveTextMode === 'light' ? 0.3 : 0.7;
177
+ const interactiveNv = accentKeyNv ?? (effectiveTextMode === 'light' ? 0.3 : 0.7);
131
178
 
132
179
  const interactiveHover = getColor(
133
180
  accentPalette.hue,
@@ -159,45 +206,36 @@ export function computeTokens(
159
206
  );
160
207
 
161
208
  // Semantic status colors: success (palette 2), warning (palette 3), error (palette 4)
162
- // Each computed at WCAG AA contrast against the mode background, same as interactive
209
+ // Each computed at WCAG AA contrast against actual background
163
210
  const successPalette = palettes[2];
164
211
  const warningPalette = palettes[3];
165
212
  const errorPalette = palettes[4];
166
213
 
214
+ const successKeyNv = successPalette ? resolveKeyNv(successPalette) : undefined;
167
215
  const success = successPalette
168
- ? getColorByContrast(
169
- successPalette.hue,
170
- successPalette.saturation,
171
- dynamicRange,
172
- 4.5,
173
- effectiveTextMode,
174
- successPalette.desaturation,
175
- successPalette.paletteHueGrading
176
- )
177
- : interactive; // Fallback to interactive if palette missing
216
+ ? (successKeyNv !== undefined
217
+ ? getColor(successPalette.hue, successPalette.saturation, dynamicRange,
218
+ successKeyNv, successPalette.desaturation, successPalette.paletteHueGrading)
219
+ : getColorByContrast(successPalette.hue, successPalette.saturation, dynamicRange,
220
+ 4.5, effectiveTextMode, successPalette.desaturation, successPalette.paletteHueGrading, background))
221
+ : interactive;
178
222
 
223
+ const warningKeyNv = warningPalette ? resolveKeyNv(warningPalette) : undefined;
179
224
  const warning = warningPalette
180
- ? getColorByContrast(
181
- warningPalette.hue,
182
- warningPalette.saturation,
183
- dynamicRange,
184
- 4.5,
185
- effectiveTextMode,
186
- warningPalette.desaturation,
187
- warningPalette.paletteHueGrading
188
- )
225
+ ? (warningKeyNv !== undefined
226
+ ? getColor(warningPalette.hue, warningPalette.saturation, dynamicRange,
227
+ warningKeyNv, warningPalette.desaturation, warningPalette.paletteHueGrading)
228
+ : getColorByContrast(warningPalette.hue, warningPalette.saturation, dynamicRange,
229
+ 4.5, effectiveTextMode, warningPalette.desaturation, warningPalette.paletteHueGrading, background))
189
230
  : interactive;
190
231
 
232
+ const errorKeyNv = errorPalette ? resolveKeyNv(errorPalette) : undefined;
191
233
  const error = errorPalette
192
- ? getColorByContrast(
193
- errorPalette.hue,
194
- errorPalette.saturation,
195
- dynamicRange,
196
- 4.5,
197
- effectiveTextMode,
198
- errorPalette.desaturation,
199
- errorPalette.paletteHueGrading
200
- )
234
+ ? (errorKeyNv !== undefined
235
+ ? getColor(errorPalette.hue, errorPalette.saturation, dynamicRange,
236
+ errorKeyNv, errorPalette.desaturation, errorPalette.paletteHueGrading)
237
+ : getColorByContrast(errorPalette.hue, errorPalette.saturation, dynamicRange,
238
+ 4.5, effectiveTextMode, errorPalette.desaturation, errorPalette.paletteHueGrading, background))
201
239
  : interactive;
202
240
 
203
241
  return {
@@ -213,5 +251,24 @@ export function computeTokens(
213
251
  success,
214
252
  warning,
215
253
  error,
254
+ spacing,
255
+ radius,
256
+ typography: {
257
+ fonts: {
258
+ mono: fontConfigToFamily(typography.fonts.mono),
259
+ display: fontConfigToFamily(typography.fonts.display),
260
+ default: fontConfigToFamily(typography.fonts.default),
261
+ },
262
+ size: typography.scale,
263
+ lineHeight: typography.lineHeight,
264
+ weight: typography.fontWeight,
265
+ },
266
+ icons: {
267
+ variant: icons.variant,
268
+ weight: icons.weight,
269
+ grade: icons.autoGrade
270
+ ? (mode === 'light' ? -25 : 200)
271
+ : 0,
272
+ },
216
273
  };
217
274
  }
@@ -28,4 +28,56 @@ export interface ResolvedTokens {
28
28
  readonly warning: ColorResult;
29
29
  /** Error/destructive state color (from error palette, index 4) */
30
30
  readonly error: ColorResult;
31
+ /** Spacing tokens for paddings, gaps, and margins */
32
+ readonly spacing: {
33
+ readonly xs: number;
34
+ readonly sm: number;
35
+ readonly md: number;
36
+ readonly lg: number;
37
+ readonly xl: number;
38
+ readonly xxl: number;
39
+ };
40
+ /** Border radius tokens for component roundness */
41
+ readonly radius: {
42
+ readonly none: number;
43
+ readonly sm: number;
44
+ readonly md: number;
45
+ readonly lg: number;
46
+ readonly xl: number;
47
+ readonly pill: 999;
48
+ };
49
+ /** Typography tokens for fonts, sizes, line heights, and weights */
50
+ readonly typography: {
51
+ readonly fonts: {
52
+ readonly mono: string; // CSS font-family string
53
+ readonly display: string; // CSS font-family string
54
+ readonly default: string; // CSS font-family string
55
+ };
56
+ readonly size: {
57
+ readonly xs: number;
58
+ readonly sm: number;
59
+ readonly base: number;
60
+ readonly md: number;
61
+ readonly lg: number;
62
+ readonly xl: number;
63
+ readonly xxl: number;
64
+ };
65
+ readonly lineHeight: {
66
+ readonly tight: number;
67
+ readonly normal: number;
68
+ readonly relaxed: number;
69
+ };
70
+ readonly weight: {
71
+ readonly regular: number;
72
+ readonly medium: number;
73
+ readonly semibold: number;
74
+ readonly bold: number;
75
+ };
76
+ };
77
+ /** Icon tokens for Material Symbols configuration */
78
+ readonly icons: {
79
+ readonly variant: 'outlined' | 'rounded' | 'sharp';
80
+ readonly weight: 100 | 200 | 300 | 400 | 500 | 600 | 700;
81
+ readonly grade: -25 | 0 | 200; // Mode-aware: light=-25, dark=200
82
+ };
31
83
  }
@@ -1,5 +1,6 @@
1
1
  import { useMemo } from 'react';
2
2
  import { useNewtoneTheme } from '../theme/NewtoneProvider';
3
+ import { useFrameContext } from '../theme/FrameContext';
3
4
  import { computeTokens } from './computeTokens';
4
5
  import type { ElevationLevel } from '../theme/types';
5
6
  import type { ResolvedTokens } from './types';
@@ -7,36 +8,50 @@ import type { ResolvedTokens } from './types';
7
8
  /**
8
9
  * Hook to compute design tokens for the current theme/mode/elevation.
9
10
  *
10
- * This hook automatically recomputes tokens when the theme configuration,
11
- * mode, theme name, or elevation changes. Results are memoized to avoid
12
- * unnecessary computation.
11
+ * Resolution order:
12
+ * 1. Explicit elevation parameter takes highest priority
13
+ * 2. FrameContext values (from nearest parent Frame) are used when elevation is omitted
14
+ * 3. Falls back to NewtoneProvider theme + elevation 1
13
15
  *
14
- * @param elevation - Elevation level (0=sunken, 1=default, 2=elevated)
16
+ * Theme always reads from FrameContext if present, falling back to NewtoneProvider.
17
+ *
18
+ * @param elevation - Elevation level (0=sunken, 1=default, 2=elevated).
19
+ * When omitted, reads from FrameContext or defaults to 1.
15
20
  * @returns Resolved design tokens with all necessary colors
16
21
  *
17
22
  * @example
18
23
  * ```tsx
19
- * function Button() {
20
- * const tokens = useTokens(1); // Default elevation
24
+ * // Inside a <Frame theme="primary" elevation={1}>:
25
+ * function MyComponent() {
26
+ * const tokens = useTokens(); // Gets primary theme, elevation 2 (mapped from Frame's 1)
21
27
  * return (
22
- * <button style={{ backgroundColor: srgbToHex(tokens.interactive.srgb) }}>
23
- * Click me
24
- * </button>
28
+ * <View style={{ backgroundColor: srgbToHex(tokens.background.srgb) }} />
25
29
  * );
26
30
  * }
27
31
  * ```
28
32
  */
29
- export function useTokens(elevation: ElevationLevel = 1): ResolvedTokens {
30
- const { config, mode, theme } = useNewtoneTheme();
33
+ export function useTokens(elevation?: ElevationLevel): ResolvedTokens {
34
+ const { config, mode, theme: providerTheme } = useNewtoneTheme();
35
+ const frameCtx = useFrameContext();
36
+
37
+ // Resolve theme: FrameContext overrides provider
38
+ const resolvedTheme = frameCtx?.theme ?? providerTheme;
39
+
40
+ // Resolve elevation: explicit param > FrameContext > default 1
41
+ const resolvedElevation: ElevationLevel = elevation ?? frameCtx?.elevation ?? 1;
31
42
 
32
43
  return useMemo(() => {
33
- const themeMapping = config.themes[theme];
44
+ const themeMapping = config.themes[resolvedTheme];
34
45
  return computeTokens(
35
46
  config.colorSystem,
36
47
  mode,
37
48
  themeMapping,
38
- elevation,
39
- config.elevation.offsets
49
+ resolvedElevation,
50
+ config.elevation.offsets,
51
+ config.spacing,
52
+ config.radius,
53
+ config.typography,
54
+ config.icons
40
55
  );
41
- }, [config, mode, theme, elevation]);
56
+ }, [config, mode, resolvedTheme, resolvedElevation]);
42
57
  }