@newtonedev/components 0.1.12 → 0.1.14

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 (227) hide show
  1. package/dist/_COMPONENT_TEMPLATE/ComponentName.styles.d.ts +4 -3
  2. package/dist/_COMPONENT_TEMPLATE/ComponentName.styles.d.ts.map +1 -1
  3. package/dist/_COMPONENT_TEMPLATE/ComponentName.types.d.ts +1 -1
  4. package/dist/_COMPONENT_TEMPLATE/ComponentName.types.d.ts.map +1 -1
  5. package/dist/composites/actions/Button/Button.d.ts +11 -1
  6. package/dist/composites/actions/Button/Button.d.ts.map +1 -1
  7. package/dist/composites/actions/Button/Button.styles.d.ts +1 -1
  8. package/dist/composites/actions/Button/Button.styles.d.ts.map +1 -1
  9. package/dist/composites/actions/Button/Button.types.d.ts +11 -1
  10. package/dist/composites/actions/Button/Button.types.d.ts.map +1 -1
  11. package/dist/composites/branding/LogoMonogram/LogoMonogram.d.ts +10 -0
  12. package/dist/composites/branding/LogoMonogram/LogoMonogram.d.ts.map +1 -0
  13. package/dist/composites/branding/LogoMonogram/LogoMonogram.types.d.ts +35 -0
  14. package/dist/composites/branding/LogoMonogram/LogoMonogram.types.d.ts.map +1 -0
  15. package/dist/composites/branding/LogoMonogram/index.d.ts +3 -0
  16. package/dist/composites/branding/LogoMonogram/index.d.ts.map +1 -0
  17. package/dist/composites/branding/LogoWordmark/LogoWordmark.d.ts +9 -0
  18. package/dist/composites/branding/LogoWordmark/LogoWordmark.d.ts.map +1 -0
  19. package/dist/composites/branding/LogoWordmark/LogoWordmark.types.d.ts +26 -0
  20. package/dist/composites/branding/LogoWordmark/LogoWordmark.types.d.ts.map +1 -0
  21. package/dist/composites/branding/LogoWordmark/index.d.ts +3 -0
  22. package/dist/composites/branding/LogoWordmark/index.d.ts.map +1 -0
  23. package/dist/composites/display/Chip/Chip.d.ts +25 -0
  24. package/dist/composites/display/Chip/Chip.d.ts.map +1 -0
  25. package/dist/composites/display/Chip/Chip.styles.d.ts +29 -0
  26. package/dist/composites/display/Chip/Chip.styles.d.ts.map +1 -0
  27. package/dist/composites/display/Chip/Chip.types.d.ts +63 -0
  28. package/dist/composites/display/Chip/Chip.types.d.ts.map +1 -0
  29. package/dist/composites/display/Chip/index.d.ts +3 -0
  30. package/dist/composites/display/Chip/index.d.ts.map +1 -0
  31. package/dist/composites/form-controls/Select/Select.d.ts.map +1 -1
  32. package/dist/composites/form-controls/Select/Select.styles.d.ts +2 -2
  33. package/dist/composites/form-controls/Select/Select.styles.d.ts.map +1 -1
  34. package/dist/composites/form-controls/Select/SelectOption.d.ts.map +1 -1
  35. package/dist/composites/form-controls/TextInput/TextInput.d.ts.map +1 -1
  36. package/dist/composites/form-controls/TextInput/TextInput.styles.d.ts +2 -2
  37. package/dist/composites/form-controls/TextInput/TextInput.styles.d.ts.map +1 -1
  38. package/dist/composites/form-controls/Toggle/Toggle.d.ts.map +1 -1
  39. package/dist/composites/form-controls/Toggle/Toggle.styles.d.ts +2 -2
  40. package/dist/composites/form-controls/Toggle/Toggle.styles.d.ts.map +1 -1
  41. package/dist/composites/layout/AppShell/AppShell.d.ts.map +1 -1
  42. package/dist/composites/layout/AppShell/AppShell.styles.d.ts +2 -2
  43. package/dist/composites/layout/AppShell/AppShell.styles.d.ts.map +1 -1
  44. package/dist/composites/layout/Card/Card.d.ts.map +1 -1
  45. package/dist/composites/layout/Card/Card.styles.d.ts +2 -2
  46. package/dist/composites/layout/Card/Card.styles.d.ts.map +1 -1
  47. package/dist/composites/layout/Card/Card.types.d.ts +1 -1
  48. package/dist/composites/layout/Card/Card.types.d.ts.map +1 -1
  49. package/dist/composites/layout/ContentCard/ContentCard.d.ts +33 -0
  50. package/dist/composites/layout/ContentCard/ContentCard.d.ts.map +1 -0
  51. package/dist/composites/layout/ContentCard/ContentCard.styles.d.ts +10 -0
  52. package/dist/composites/layout/ContentCard/ContentCard.styles.d.ts.map +1 -0
  53. package/dist/composites/layout/ContentCard/ContentCard.types.d.ts +52 -0
  54. package/dist/composites/layout/ContentCard/ContentCard.types.d.ts.map +1 -0
  55. package/dist/composites/layout/ContentCard/index.d.ts +3 -0
  56. package/dist/composites/layout/ContentCard/index.d.ts.map +1 -0
  57. package/dist/composites/layout/Divider/Divider.d.ts +25 -0
  58. package/dist/composites/layout/Divider/Divider.d.ts.map +1 -0
  59. package/dist/composites/layout/Divider/Divider.styles.d.ts +8 -0
  60. package/dist/composites/layout/Divider/Divider.styles.d.ts.map +1 -0
  61. package/dist/composites/layout/Divider/Divider.types.d.ts +25 -0
  62. package/dist/composites/layout/Divider/Divider.types.d.ts.map +1 -0
  63. package/dist/composites/layout/Divider/index.d.ts +3 -0
  64. package/dist/composites/layout/Divider/index.d.ts.map +1 -0
  65. package/dist/composites/layout/Navbar/Navbar.d.ts.map +1 -1
  66. package/dist/composites/layout/Navbar/Navbar.styles.d.ts +4 -2
  67. package/dist/composites/layout/Navbar/Navbar.styles.d.ts.map +1 -1
  68. package/dist/composites/layout/Sidebar/Sidebar.d.ts.map +1 -1
  69. package/dist/composites/layout/Sidebar/Sidebar.styles.d.ts +4 -2
  70. package/dist/composites/layout/Sidebar/Sidebar.styles.d.ts.map +1 -1
  71. package/dist/composites/overlays/Popover/Popover.d.ts.map +1 -1
  72. package/dist/composites/overlays/Popover/Popover.styles.d.ts +2 -2
  73. package/dist/composites/overlays/Popover/Popover.styles.d.ts.map +1 -1
  74. package/dist/composites/range-inputs/ColorScaleSlider/ColorScaleSlider.d.ts +1 -1
  75. package/dist/composites/range-inputs/ColorScaleSlider/ColorScaleSlider.d.ts.map +1 -1
  76. package/dist/composites/range-inputs/ColorScaleSlider/ColorScaleSlider.styles.d.ts +1 -1
  77. package/dist/composites/range-inputs/ColorScaleSlider/ColorScaleSlider.styles.d.ts.map +1 -1
  78. package/dist/composites/range-inputs/ColorScaleSlider/ColorScaleSlider.types.d.ts +2 -0
  79. package/dist/composites/range-inputs/ColorScaleSlider/ColorScaleSlider.types.d.ts.map +1 -1
  80. package/dist/composites/range-inputs/HueSlider/HueSlider.d.ts +3 -4
  81. package/dist/composites/range-inputs/HueSlider/HueSlider.d.ts.map +1 -1
  82. package/dist/composites/range-inputs/HueSlider/HueSlider.styles.d.ts +3 -3
  83. package/dist/composites/range-inputs/HueSlider/HueSlider.styles.d.ts.map +1 -1
  84. package/dist/composites/range-inputs/Slider/Slider.styles.d.ts +1 -1
  85. package/dist/composites/range-inputs/Slider/Slider.styles.d.ts.map +1 -1
  86. package/dist/index.cjs +1609 -1693
  87. package/dist/index.cjs.map +1 -1
  88. package/dist/index.d.ts +22 -24
  89. package/dist/index.d.ts.map +1 -1
  90. package/dist/index.js +1454 -1621
  91. package/dist/index.js.map +1 -1
  92. package/dist/primitives/Frame/Frame.d.ts +1 -35
  93. package/dist/primitives/Frame/Frame.d.ts.map +1 -1
  94. package/dist/primitives/Frame/Frame.styles.d.ts +13 -3
  95. package/dist/primitives/Frame/Frame.styles.d.ts.map +1 -1
  96. package/dist/primitives/Frame/Frame.types.d.ts +99 -1
  97. package/dist/primitives/Frame/Frame.types.d.ts.map +1 -1
  98. package/dist/primitives/Frame/Frame.utils.d.ts +1 -1
  99. package/dist/primitives/Frame/Frame.utils.d.ts.map +1 -1
  100. package/dist/primitives/Frame/index.d.ts +1 -1
  101. package/dist/primitives/Frame/index.d.ts.map +1 -1
  102. package/dist/primitives/Icon/Icon.d.ts.map +1 -1
  103. package/dist/primitives/Icon/Icon.types.d.ts +2 -2
  104. package/dist/primitives/Icon/Icon.types.d.ts.map +1 -1
  105. package/dist/primitives/Text/Text.d.ts +6 -4
  106. package/dist/primitives/Text/Text.d.ts.map +1 -1
  107. package/dist/primitives/Text/Text.spans.d.ts.map +1 -1
  108. package/dist/primitives/Text/Text.types.d.ts +4 -7
  109. package/dist/primitives/Text/Text.types.d.ts.map +1 -1
  110. package/dist/primitives/Wrapper/Wrapper.d.ts +1 -1
  111. package/dist/primitives/Wrapper/Wrapper.d.ts.map +1 -1
  112. package/dist/primitives/Wrapper/Wrapper.styles.d.ts +10 -2
  113. package/dist/primitives/Wrapper/Wrapper.styles.d.ts.map +1 -1
  114. package/dist/primitives/Wrapper/Wrapper.types.d.ts +31 -1
  115. package/dist/primitives/Wrapper/Wrapper.types.d.ts.map +1 -1
  116. package/package.json +3 -2
  117. package/src/_COMPONENT_TEMPLATE/ComponentName.styles.ts +4 -4
  118. package/src/_COMPONENT_TEMPLATE/ComponentName.tsx +2 -2
  119. package/src/_COMPONENT_TEMPLATE/ComponentName.types.ts +1 -1
  120. package/src/composites/actions/Button/Button.styles.ts +72 -55
  121. package/src/composites/actions/Button/Button.tsx +35 -14
  122. package/src/composites/actions/Button/Button.types.ts +13 -1
  123. package/src/composites/branding/LogoMonogram/LogoMonogram.tsx +29 -0
  124. package/src/composites/branding/LogoMonogram/LogoMonogram.types.ts +35 -0
  125. package/src/composites/branding/LogoMonogram/index.ts +2 -0
  126. package/src/composites/branding/LogoWordmark/LogoWordmark.tsx +29 -0
  127. package/src/composites/branding/LogoWordmark/LogoWordmark.types.ts +25 -0
  128. package/src/composites/branding/LogoWordmark/index.ts +2 -0
  129. package/src/composites/display/Chip/Chip.styles.ts +189 -0
  130. package/src/composites/display/Chip/Chip.tsx +97 -0
  131. package/src/composites/display/Chip/Chip.types.ts +74 -0
  132. package/src/composites/display/Chip/index.ts +2 -0
  133. package/src/composites/form-controls/Select/Select.styles.ts +10 -10
  134. package/src/composites/form-controls/Select/Select.tsx +9 -7
  135. package/src/composites/form-controls/Select/SelectOption.tsx +10 -8
  136. package/src/composites/form-controls/TextInput/TextInput.styles.ts +12 -9
  137. package/src/composites/form-controls/TextInput/TextInput.tsx +7 -5
  138. package/src/composites/form-controls/Toggle/Toggle.styles.ts +10 -7
  139. package/src/composites/form-controls/Toggle/Toggle.tsx +4 -3
  140. package/src/composites/layout/AppShell/AppShell.styles.ts +8 -4
  141. package/src/composites/layout/AppShell/AppShell.tsx +6 -2
  142. package/src/composites/layout/Card/Card.styles.ts +10 -5
  143. package/src/composites/layout/Card/Card.tsx +4 -3
  144. package/src/composites/layout/Card/Card.types.ts +1 -1
  145. package/src/composites/layout/ContentCard/ContentCard.styles.ts +44 -0
  146. package/src/composites/layout/ContentCard/ContentCard.tsx +71 -0
  147. package/src/composites/layout/ContentCard/ContentCard.types.ts +60 -0
  148. package/src/composites/layout/ContentCard/index.ts +2 -0
  149. package/src/composites/layout/Divider/Divider.styles.ts +30 -0
  150. package/src/composites/layout/Divider/Divider.tsx +46 -0
  151. package/src/composites/layout/Divider/Divider.types.ts +28 -0
  152. package/src/composites/layout/Divider/index.ts +2 -0
  153. package/src/composites/layout/Navbar/Navbar.styles.ts +7 -5
  154. package/src/composites/layout/Navbar/Navbar.tsx +4 -3
  155. package/src/composites/layout/Sidebar/Sidebar.styles.ts +7 -5
  156. package/src/composites/layout/Sidebar/Sidebar.tsx +4 -3
  157. package/src/composites/overlays/Popover/Popover.styles.ts +7 -5
  158. package/src/composites/overlays/Popover/Popover.tsx +4 -3
  159. package/src/composites/range-inputs/ColorScaleSlider/ColorScaleSlider.styles.ts +4 -5
  160. package/src/composites/range-inputs/ColorScaleSlider/ColorScaleSlider.tsx +6 -2
  161. package/src/composites/range-inputs/ColorScaleSlider/ColorScaleSlider.types.ts +2 -0
  162. package/src/composites/range-inputs/HueSlider/HueSlider.styles.ts +13 -20
  163. package/src/composites/range-inputs/HueSlider/HueSlider.tsx +7 -8
  164. package/src/composites/range-inputs/Slider/Slider.styles.ts +8 -9
  165. package/src/composites/range-inputs/Slider/Slider.tsx +1 -1
  166. package/src/index.ts +108 -61
  167. package/src/primitives/Frame/Frame.styles.ts +55 -11
  168. package/src/primitives/Frame/Frame.tsx +140 -142
  169. package/src/primitives/Frame/Frame.types.ts +119 -1
  170. package/src/primitives/Frame/Frame.utils.ts +1 -1
  171. package/src/primitives/Frame/index.ts +4 -0
  172. package/src/primitives/Icon/Icon.tsx +9 -7
  173. package/src/primitives/Icon/Icon.types.ts +2 -2
  174. package/src/primitives/Text/Text.spans.ts +9 -5
  175. package/src/primitives/Text/Text.tsx +33 -22
  176. package/src/primitives/Text/Text.types.ts +4 -7
  177. package/src/primitives/Wrapper/Wrapper.styles.ts +33 -1
  178. package/src/primitives/Wrapper/Wrapper.tsx +23 -4
  179. package/src/primitives/Wrapper/Wrapper.types.ts +45 -0
  180. package/dist/fonts/GoogleFontLoader.d.ts +0 -20
  181. package/dist/fonts/GoogleFontLoader.d.ts.map +0 -1
  182. package/dist/fonts/IconFontLoader.d.ts +0 -13
  183. package/dist/fonts/IconFontLoader.d.ts.map +0 -1
  184. package/dist/fonts/SelfHostedFontLoader.d.ts +0 -14
  185. package/dist/fonts/SelfHostedFontLoader.d.ts.map +0 -1
  186. package/dist/fonts/buildGoogleFontsUrl.d.ts +0 -2
  187. package/dist/fonts/buildGoogleFontsUrl.d.ts.map +0 -1
  188. package/dist/fonts/measureFont.d.ts +0 -19
  189. package/dist/fonts/measureFont.d.ts.map +0 -1
  190. package/dist/fonts/reportQueue.d.ts +0 -7
  191. package/dist/fonts/reportQueue.d.ts.map +0 -1
  192. package/dist/fonts/useLocalCalibration.d.ts +0 -19
  193. package/dist/fonts/useLocalCalibration.d.ts.map +0 -1
  194. package/dist/fonts/useTypographyCalibrations.d.ts +0 -11
  195. package/dist/fonts/useTypographyCalibrations.d.ts.map +0 -1
  196. package/dist/theme/FrameContext.d.ts +0 -26
  197. package/dist/theme/FrameContext.d.ts.map +0 -1
  198. package/dist/theme/NewtoneProvider.d.ts +0 -40
  199. package/dist/theme/NewtoneProvider.d.ts.map +0 -1
  200. package/dist/theme/defaults.d.ts +0 -8
  201. package/dist/theme/defaults.d.ts.map +0 -1
  202. package/dist/theme/types.d.ts +0 -156
  203. package/dist/theme/types.d.ts.map +0 -1
  204. package/dist/theme/useBreakpoint.d.ts +0 -9
  205. package/dist/theme/useBreakpoint.d.ts.map +0 -1
  206. package/dist/tokens/computeTokens.d.ts +0 -151
  207. package/dist/tokens/computeTokens.d.ts.map +0 -1
  208. package/dist/tokens/types.d.ts +0 -162
  209. package/dist/tokens/types.d.ts.map +0 -1
  210. package/dist/tokens/useTokens.d.ts +0 -26
  211. package/dist/tokens/useTokens.d.ts.map +0 -1
  212. package/src/fonts/GoogleFontLoader.tsx +0 -80
  213. package/src/fonts/IconFontLoader.tsx +0 -51
  214. package/src/fonts/SelfHostedFontLoader.tsx +0 -44
  215. package/src/fonts/buildGoogleFontsUrl.ts +0 -2
  216. package/src/fonts/measureFont.ts +0 -55
  217. package/src/fonts/reportQueue.ts +0 -54
  218. package/src/fonts/useLocalCalibration.ts +0 -97
  219. package/src/fonts/useTypographyCalibrations.ts +0 -15
  220. package/src/theme/FrameContext.tsx +0 -31
  221. package/src/theme/NewtoneProvider.tsx +0 -84
  222. package/src/theme/defaults.ts +0 -71
  223. package/src/theme/types.ts +0 -191
  224. package/src/theme/useBreakpoint.ts +0 -14
  225. package/src/tokens/computeTokens.ts +0 -516
  226. package/src/tokens/types.ts +0 -146
  227. package/src/tokens/useTokens.ts +0 -62
@@ -2,20 +2,15 @@ import React, { useMemo } from 'react';
2
2
  import { View, Pressable, Text } from 'react-native';
3
3
  import type { ViewStyle, TextStyle } from 'react-native';
4
4
  import type { FrameProps } from './Frame.types';
5
- import type { ElevationLevel, FrameElevation } from '../../theme/types';
6
- import { srgbToHex } from 'newtone';
7
- import { FrameContext, useFrameContext } from '../../theme/FrameContext';
8
- import { useNewtoneTheme } from '../../theme/NewtoneProvider';
9
- import { computeTokens } from '../../tokens/computeTokens';
5
+ import type { ElevationLevel, FrameElevation, ElevationName, ThemeName, AppearanceName } from 'newtone-api';
6
+ import { FrameContext, useFrameContext } from 'newtone-api';
7
+ import { useNewtoneTheme, _ThemeContext } from 'newtone-api';
8
+ import { computeTokens } from 'newtone-api';
10
9
  import { getFrameStyles } from './Frame.styles';
11
10
  import { useFocusVisible } from '../useFocusVisible';
12
11
 
13
12
  /**
14
13
  * Wrap raw string/number children in <Text> so they display correctly.
15
- *
16
- * In React Native, raw text like <View>"hello"</View> will crash on native
17
- * and show console warnings on web. All text must be inside a <Text> element.
18
- * This helper scans children and auto-wraps any bare strings or numbers.
19
14
  */
20
15
  function wrapTextChildren(
21
16
  children: React.ReactNode,
@@ -29,16 +24,15 @@ function wrapTextChildren(
29
24
  });
30
25
  }
31
26
 
27
+ /** Map numeric elevation levels to named elevations for the swatches layer. */
28
+ const ELEVATION_MAP: Record<ElevationLevel, ElevationName> = {
29
+ 0: 'sunken',
30
+ 1: 'grounded',
31
+ 2: 'elevated',
32
+ };
33
+
32
34
  /**
33
35
  * Convert user-facing FrameElevation (-2..2) to internal ElevationLevel (0..2).
34
- *
35
- * | Frame | Internal | Background |
36
- * |:-----:|:--------:|:-----------|
37
- * | -2 | 0 | Sunken |
38
- * | -1 | 0 | Sunken |
39
- * | 0 | 1 | Default |
40
- * | 1 | 2 | Elevated |
41
- * | 2 | 2 | Elevated |
42
36
  */
43
37
  function toElevationLevel(frameElevation: FrameElevation): ElevationLevel {
44
38
  if (frameElevation <= -1) return 0;
@@ -54,45 +48,16 @@ function toElevationLevel(frameElevation: FrameElevation): ElevationLevel {
54
48
  * and interactivity capabilities.
55
49
  *
56
50
  * Frames can be nested. Inner frames override outer frames for their subtree.
57
- * Unspecified props inherit from the nearest parent Frame.
58
- *
59
- * @example
60
- * ```tsx
61
- * // Basic layout
62
- * <Frame direction="horizontal" gap="md" padding="lg" align="center">
63
- * <Button onPress={() => {}}>Save</Button>
64
- * <Button variant="tertiary" onPress={() => {}}>Cancel</Button>
65
- * </Frame>
66
- * ```
67
- *
68
- * @example
69
- * ```tsx
70
- * // Card-like frame
71
- * <Frame
72
- * elevation={2}
73
- * radius="lg"
74
- * padding="xl"
75
- * bordered
76
- * onPress={() => navigate('/details')}
77
- * >
78
- * <Text>Clickable card</Text>
79
- * </Frame>
80
- * ```
81
- *
82
- * @example
83
- * ```tsx
84
- * // Grid layout (web)
85
- * <Frame layout="grid" columns={3} gap="md" padding="lg">
86
- * <Frame radius="md" padding="md" bordered>Cell 1</Frame>
87
- * <Frame radius="md" padding="md" bordered>Cell 2</Frame>
88
- * <Frame radius="md" padding="md" bordered>Cell 3</Frame>
89
- * </Frame>
90
- * ```
91
51
  */
92
52
  export function Frame({
93
53
  children,
94
54
  // Elevation
95
55
  elevation,
56
+ // Scheme
57
+ scheme,
58
+ // Theme / Appearance
59
+ theme,
60
+ appearance,
96
61
  // Layout
97
62
  layout,
98
63
  direction,
@@ -113,6 +78,16 @@ export function Frame({
113
78
  maxWidth,
114
79
  minHeight,
115
80
  maxHeight,
81
+ // Positioning
82
+ position,
83
+ top,
84
+ right,
85
+ bottom,
86
+ left,
87
+ zIndex,
88
+ overflow,
89
+ pointerEvents,
90
+ opacity,
116
91
  // Appearance
117
92
  radius,
118
93
  bordered,
@@ -130,44 +105,54 @@ export function Frame({
130
105
  // Style override
131
106
  style,
132
107
  }: FrameProps) {
133
- // Read the global theme configuration and current color mode (light/dark).
134
- const { config, mode } = useNewtoneTheme();
135
- // Read the elevation from the nearest parent Frame (if nested).
108
+ const themeCtx = useNewtoneTheme();
109
+ const { mode, gamut } = themeCtx;
136
110
  const parentFrameCtx = useFrameContext();
137
111
 
138
- // The user-facing elevation (-2 to 2) controls visual depth (shadows, background).
112
+ // Resolve which config to use: explicit scheme > inherited from parent > default.
113
+ const resolvedConfig = useMemo(() => {
114
+ if (scheme && themeCtx.schemes) {
115
+ const schemeConfig = themeCtx.schemes[scheme];
116
+ if (schemeConfig) return schemeConfig;
117
+ }
118
+ return themeCtx.config;
119
+ }, [scheme, themeCtx.schemes, themeCtx.config]);
120
+
121
+ // Track whether this Frame switches the scheme (needs theme context override).
122
+ const isSchemeOverride = resolvedConfig !== themeCtx.config;
123
+
124
+ // Resolve theme/appearance: explicit prop > parent Frame > defaults.
125
+ const resolvedTheme: ThemeName = theme ?? parentFrameCtx?.theme ?? 'primary';
126
+ const resolvedAppearance: AppearanceName = appearance ?? parentFrameCtx?.appearance ?? 'main';
127
+
139
128
  const resolvedFrameElevation: FrameElevation = elevation ?? 0;
140
129
 
141
- // Convert user-facing elevation to internal level (0-2) for token computation.
142
- // When no elevation is set, inherit from parent Frame or default to 1.
143
130
  const resolvedElevation: ElevationLevel = elevation !== undefined
144
131
  ? toElevationLevel(elevation)
145
132
  : parentFrameCtx?.elevation ?? 1;
146
133
 
147
- // Generate the design tokens (colors, spacing, fonts) for this Frame.
148
- // Frame computes its own tokens instead of using useTokens() because
149
- // useTokens() reads from FrameContext — but this Frame IS the provider,
150
- // so it needs to compute fresh tokens from the resolved theme/elevation.
151
- // Wrapped in useMemo so it only recalculates when the theme/mode/elevation changes.
134
+ // Compute tokens with gamut pre-applied (no [gamut] indexing needed downstream).
152
135
  const tokens = useMemo(() => {
153
136
  return computeTokens(
154
- config.colorSystem,
137
+ resolvedConfig.colorSystem,
155
138
  mode,
156
- resolvedElevation,
157
- config.spacing,
158
- config.radius,
159
- config.typography,
160
- config.icons,
161
- config.tokenOverrides,
139
+ gamut,
140
+ ELEVATION_MAP[resolvedElevation],
141
+ resolvedConfig.spacing,
142
+ resolvedConfig.radius,
143
+ resolvedConfig.typography,
144
+ resolvedConfig.icons,
145
+ resolvedConfig.themeMappings,
146
+ resolvedConfig.swatchDefaults,
162
147
  );
163
- }, [config, mode, resolvedElevation]);
148
+ }, [resolvedConfig, mode, gamut, resolvedElevation]);
164
149
 
165
- // Calculate all visual styles (background, layout, border, shadow, etc.).
166
- // Only recalculates when one of the style-related props changes.
167
150
  const styles = useMemo(
168
151
  () => getFrameStyles({
169
152
  tokens,
170
153
  frameElevation: resolvedFrameElevation,
154
+ theme: resolvedTheme,
155
+ appearance: resolvedAppearance,
171
156
  layout,
172
157
  direction,
173
158
  wrap,
@@ -184,29 +169,53 @@ export function Frame({
184
169
  maxWidth,
185
170
  minHeight,
186
171
  maxHeight,
172
+ position,
173
+ top,
174
+ right,
175
+ bottom,
176
+ left,
177
+ zIndex,
178
+ overflow,
179
+ opacity,
187
180
  radius,
188
181
  bordered,
189
182
  disabled,
190
183
  }),
191
184
  [
192
- tokens, resolvedFrameElevation,
185
+ tokens, resolvedFrameElevation, resolvedTheme, resolvedAppearance,
193
186
  layout, direction, wrap, reverse, columns, rows,
194
187
  align, justify, padding, gap,
195
188
  width, height, minWidth, maxWidth, minHeight, maxHeight,
189
+ position, top, right, bottom, left, zIndex, overflow, opacity,
196
190
  radius, bordered, disabled,
197
191
  ],
198
192
  );
199
193
 
200
- // This is the value that child components will inherit via FrameContext.
201
- // Any nested Frame, Text, Icon, etc. will read this theme and elevation.
194
+ // Resolved scheme name: explicit prop > inherited from parent Frame.
195
+ const resolvedScheme = scheme ?? parentFrameCtx?.scheme;
196
+
202
197
  const contextValue = useMemo(
203
- () => ({ elevation: resolvedElevation, tokens }),
204
- [resolvedElevation, tokens],
198
+ () => ({
199
+ elevation: resolvedElevation,
200
+ tokens,
201
+ scheme: resolvedScheme,
202
+ theme: resolvedTheme,
203
+ appearance: resolvedAppearance,
204
+ }),
205
+ [resolvedElevation, tokens, resolvedScheme, resolvedTheme, resolvedAppearance],
205
206
  );
206
207
 
207
- // Some styles only work on web browsers (CSS grid, inset shadows).
208
- // We collect them separately and cast them to ViewStyle so React Native
209
- // doesn't complain about unknown properties (they're silently ignored on native).
208
+ // When this Frame switches the scheme, override ThemeContext so that
209
+ // useNewtoneTheme().config returns the scheme's config for descendants.
210
+ const schemeThemeCtx = useMemo(() => {
211
+ if (!isSchemeOverride) return null;
212
+ return {
213
+ ...themeCtx,
214
+ config: resolvedConfig,
215
+ activeScheme: scheme ?? themeCtx.activeScheme,
216
+ };
217
+ }, [isSchemeOverride, themeCtx, resolvedConfig, scheme]);
218
+
210
219
  const webOverrides: ViewStyle[] = [];
211
220
  if (styles.gridWebStyle) {
212
221
  webOverrides.push(styles.gridWebStyle as unknown as ViewStyle);
@@ -215,94 +224,83 @@ export function Frame({
215
224
  webOverrides.push({ boxShadow: styles.insetBoxShadow } as unknown as ViewStyle);
216
225
  }
217
226
 
218
- // Normalize user's custom styles into an array for merging.
219
227
  const userStyles = Array.isArray(style) ? style : style ? [style] : [];
220
228
 
221
- // If the Frame has onPress or href, it needs to respond to taps/clicks.
222
- // In that case we render a Pressable (tappable); otherwise a plain View.
229
+
223
230
  const isInteractive = onPress !== undefined || href !== undefined;
224
231
 
225
- // Detect keyboard-only focus (Tab, arrows) vs mouse/touch focus.
226
- // Only shows a visible focus ring when the user navigated via keyboard,
227
- // matching the browser's native :focus-visible behavior.
228
232
  const { isFocusVisible, focusProps } = useFocusVisible();
229
233
 
230
- // Focus ring style: 2px solid outline in the theme's interactive color,
231
- // offset by 2px so it doesn't overlap the Frame's border.
232
- // Uses CSS outline properties — silently ignored on native platforms.
234
+ // Focus ring: uses the emphasis background of the resolved theme as the outline color.
233
235
  const focusRingStyle = isFocusVisible && !disabled ? {
234
236
  outlineWidth: 2,
235
237
  outlineStyle: 'solid',
236
- outlineColor: srgbToHex(tokens.accent.fill.srgb),
238
+ outlineColor: tokens.colors[resolvedTheme].emphasis.background,
237
239
  outlineOffset: 2,
238
- } as unknown as ViewStyle : undefined; // web-only
240
+ } as unknown as ViewStyle : undefined;
239
241
 
240
- // Spread focus event handlers onto the Pressable. These are web-only
241
- // handlers supported by react-native-web — silently ignored on native.
242
- const webFocusProps = isInteractive ? focusProps as any : {}; // web-only
242
+ const webFocusProps = isInteractive ? focusProps as any : {};
243
243
 
244
- // Default text style for any raw strings/numbers passed as children.
245
- // Uses the theme's primary text color, default font, and base size.
246
244
  const textStyle = useMemo<TextStyle>(
247
245
  () => ({
248
- color: srgbToHex(tokens.textPrimary.srgb),
246
+ color: tokens.colors[resolvedTheme][resolvedAppearance].fontPrimary,
249
247
  fontSize: tokens.typography.fontSizes['05'],
250
248
  fontFamily: tokens.typography.fonts.main.family,
251
249
  lineHeight: tokens.typography.lineHeights['06'],
252
250
  }),
253
- [tokens],
251
+ [tokens, resolvedTheme, resolvedAppearance],
254
252
  );
255
- // Auto-wrap bare text ("hello") in <Text> elements (required by React Native).
256
- // Wrapped in useMemo so it only re-scans children when they or the text style changes.
257
253
  const wrappedChildren = useMemo(
258
254
  () => wrapTextChildren(children, textStyle),
259
255
  [children, textStyle],
260
256
  );
261
257
 
262
- // FrameContext.Provider shares this Frame's theme and elevation with all
263
- // descendants, so nested components automatically pick up the right colors.
258
+ const content = isInteractive ? (
259
+ <Pressable
260
+ ref={ref}
261
+ testID={testID}
262
+ nativeID={nativeID}
263
+ pointerEvents={pointerEvents}
264
+ accessibilityLabel={accessibilityLabel}
265
+ accessibilityHint={accessibilityHint}
266
+ accessibilityState={disabled ? { disabled: true } : undefined}
267
+ onPress={onPress}
268
+ disabled={disabled}
269
+ {...(href ? { href, accessibilityRole: 'link' as const } : { accessibilityRole: 'button' as const })}
270
+ {...webFocusProps}
271
+ style={({ pressed }) => [
272
+ styles.container,
273
+ pressed && !disabled && styles.pressed,
274
+ focusRingStyle,
275
+ ...webOverrides,
276
+ ...userStyles,
277
+ ]}
278
+ >
279
+ {wrappedChildren}
280
+ </Pressable>
281
+ ) : (
282
+ <View
283
+ ref={ref}
284
+ testID={testID}
285
+ nativeID={nativeID}
286
+ pointerEvents={pointerEvents}
287
+ accessibilityLabel={accessibilityLabel}
288
+ accessibilityHint={accessibilityHint}
289
+ style={[styles.container, ...webOverrides, ...userStyles]}
290
+ >
291
+ {wrappedChildren}
292
+ </View>
293
+ );
294
+
295
+ const wrappedContent = schemeThemeCtx ? (
296
+ <_ThemeContext.Provider value={schemeThemeCtx}>
297
+ {content}
298
+ </_ThemeContext.Provider>
299
+ ) : content;
300
+
264
301
  return (
265
302
  <FrameContext.Provider value={contextValue}>
266
- {isInteractive ? (
267
- // Pressable handles taps. When href is set, react-native-web renders
268
- // it as an <a> tag so it works like a regular link on the web.
269
- <Pressable
270
- ref={ref}
271
- testID={testID}
272
- nativeID={nativeID}
273
- accessibilityLabel={accessibilityLabel}
274
- accessibilityHint={accessibilityHint}
275
- // Tell screen readers this is disabled so assistive technology can announce it.
276
- accessibilityState={disabled ? { disabled: true } : undefined}
277
- onPress={onPress}
278
- disabled={disabled}
279
- {...(href ? { href, accessibilityRole: 'link' as const } : { accessibilityRole: 'button' as const })}
280
- {...webFocusProps}
281
- // The style callback receives { pressed: true/false } so we can
282
- // change the background when the user is actively pressing.
283
- style={({ pressed }) => [
284
- styles.container,
285
- pressed && !disabled && styles.pressed,
286
- focusRingStyle,
287
- ...webOverrides,
288
- ...userStyles,
289
- ]}
290
- >
291
- {wrappedChildren}
292
- </Pressable>
293
- ) : (
294
- // Non-interactive Frame: just a plain View with no tap handling.
295
- <View
296
- ref={ref}
297
- testID={testID}
298
- nativeID={nativeID}
299
- accessibilityLabel={accessibilityLabel}
300
- accessibilityHint={accessibilityHint}
301
- style={[styles.container, ...webOverrides, ...userStyles]}
302
- >
303
- {wrappedChildren}
304
- </View>
305
- )}
303
+ {wrappedContent}
306
304
  </FrameContext.Provider>
307
305
  );
308
306
  }
@@ -1,5 +1,5 @@
1
1
  import type { View, ViewStyle, GestureResponderEvent } from 'react-native';
2
- import type { FrameElevation } from '../../theme/types';
2
+ import type { FrameElevation, ThemeName, AppearanceName } from 'newtone-api';
3
3
 
4
4
  // ── Spacing Types ──────────────────────────────────────────────
5
5
 
@@ -76,6 +76,20 @@ export interface RadiusCorners {
76
76
  */
77
77
  export type RadiusProp = RadiusValue | RadiusCorners;
78
78
 
79
+ // ── Positioning Types ──────────────────────────────────────────
80
+
81
+ /** CSS position mode. `'fixed'` and `'sticky'` are web-only (via react-native-web). */
82
+ export type PositionType = 'absolute' | 'relative' | 'fixed' | 'sticky';
83
+
84
+ /** Position offset value: pixels (number) or percentage string (e.g. `'50%'`). */
85
+ export type OffsetValue = number | string;
86
+
87
+ /** Overflow clipping mode. */
88
+ export type OverflowMode = 'visible' | 'hidden' | 'scroll';
89
+
90
+ /** Pointer-events mode (View prop, not a style). */
91
+ export type PointerEventsMode = 'auto' | 'none' | 'box-none' | 'box-only';
92
+
79
93
  // ── Sizing Types ───────────────────────────────────────────────
80
94
 
81
95
  /**
@@ -159,6 +173,23 @@ export interface FrameProps {
159
173
  */
160
174
  readonly elevation?: FrameElevation;
161
175
 
176
+ /**
177
+ * Named scheme to use for this Frame's subtree.
178
+ * Looks up the scheme's config from the ancestor NewtoneProvider's `schemes` map
179
+ * and uses it instead of the default for token computation and context.
180
+ * Ignored when the provider is in single-config mode.
181
+ *
182
+ * @example
183
+ * ```tsx
184
+ * <NewtoneProvider schemes={{ Default: config1, Dark: config2 }} defaultScheme="Default">
185
+ * <Frame scheme="Dark">
186
+ * {/* Children here get the "Dark" scheme's tokens *​/}
187
+ * </Frame>
188
+ * </NewtoneProvider>
189
+ * ```
190
+ */
191
+ readonly scheme?: string;
192
+
162
193
  // ── Layout ──
163
194
 
164
195
  /** Layout mode. @default 'flex' */
@@ -248,8 +279,95 @@ export interface FrameProps {
248
279
  /** Maximum height in pixels. */
249
280
  readonly maxHeight?: number;
250
281
 
282
+ // ── Positioning ──
283
+
284
+ /**
285
+ * CSS position mode.
286
+ *
287
+ * `'fixed'` and `'sticky'` are web-only (supported via react-native-web).
288
+ * React Native natively supports `'absolute'` and `'relative'`.
289
+ */
290
+ readonly position?: PositionType;
291
+
292
+ /** Offset from the top edge. Accepts pixels (number) or percentage string. */
293
+ readonly top?: OffsetValue;
294
+
295
+ /** Offset from the right edge. Accepts pixels (number) or percentage string. */
296
+ readonly right?: OffsetValue;
297
+
298
+ /** Offset from the bottom edge. Accepts pixels (number) or percentage string. */
299
+ readonly bottom?: OffsetValue;
300
+
301
+ /** Offset from the left edge. Accepts pixels (number) or percentage string. */
302
+ readonly left?: OffsetValue;
303
+
304
+ /** Stacking order. Higher values render above lower values. */
305
+ readonly zIndex?: number;
306
+
307
+ /**
308
+ * Content overflow behavior.
309
+ *
310
+ * When `radius` is set with positive values, Frame auto-applies `overflow: 'hidden'`
311
+ * to clip content at rounded corners. Setting `overflow` explicitly overrides this.
312
+ */
313
+ readonly overflow?: OverflowMode;
314
+
315
+ /**
316
+ * Controls whether the element can be the target of touch/pointer events.
317
+ *
318
+ * - `'auto'` — default, element and children receive events
319
+ * - `'none'` — element and children are invisible to events (pass through)
320
+ * - `'box-none'` — element ignores events but children can receive them
321
+ * - `'box-only'` — element receives events but children cannot
322
+ */
323
+ readonly pointerEvents?: PointerEventsMode;
324
+
325
+ /**
326
+ * Opacity of the element (0 = fully transparent, 1 = fully opaque).
327
+ *
328
+ * When set explicitly, overrides the automatic `0.5` opacity applied by `disabled`.
329
+ */
330
+ readonly opacity?: number;
331
+
251
332
  // ── Appearance ──
252
333
 
334
+ /**
335
+ * Color theme for this Frame's visual rendering (background, border, text).
336
+ *
337
+ * Selects which of the 6 themes to use from the resolved tokens.
338
+ * Does not affect scheme selection — use `scheme` for that.
339
+ *
340
+ * @default 'primary'
341
+ *
342
+ * @example
343
+ * ```tsx
344
+ * <Frame theme="error" appearance="tinted">
345
+ * <Text>Error banner</Text>
346
+ * </Frame>
347
+ * ```
348
+ */
349
+ readonly theme?: ThemeName;
350
+
351
+ /**
352
+ * Appearance variant within the selected theme.
353
+ *
354
+ * Controls the visual emphasis of the Frame's chrome (background, border):
355
+ * - `'main'` — default surface (passive contrast)
356
+ * - `'emphasis'` — prominent, filled surface (active contrast)
357
+ * - `'tinted'` — subtle tinted surface
358
+ * - `'strong'` — bold, high-contrast variant
359
+ *
360
+ * @default 'main'
361
+ *
362
+ * @example
363
+ * ```tsx
364
+ * <Frame theme="primary" appearance="emphasis">
365
+ * <Text>CTA section</Text>
366
+ * </Frame>
367
+ * ```
368
+ */
369
+ readonly appearance?: AppearanceName;
370
+
253
371
  /**
254
372
  * Border radius.
255
373
  *
@@ -1,5 +1,5 @@
1
1
  import type { ViewStyle } from 'react-native';
2
- import type { ResolvedTokens } from '../../tokens/types';
2
+ import type { ResolvedTokens } from 'newtone-api';
3
3
  import type {
4
4
  SpacingValue,
5
5
  SpacingAxes,
@@ -17,5 +17,9 @@ export type {
17
17
  Direction,
18
18
  Alignment,
19
19
  Justification,
20
+ PositionType,
21
+ OffsetValue,
22
+ OverflowMode,
23
+ PointerEventsMode,
20
24
  } from './Frame.types';
21
25
  export type { ResolvedCorners } from './Frame.utils';
@@ -1,7 +1,6 @@
1
1
  import React, { useMemo } from 'react';
2
2
  import { Text, type TextStyle } from 'react-native';
3
- import { srgbToHex } from 'newtone';
4
- import { useTokens } from '../../tokens/useTokens';
3
+ import { useTokens, useFrameContext } from 'newtone-api';
5
4
  import type { IconProps } from './Icon.types';
6
5
 
7
6
  /**
@@ -18,8 +17,8 @@ import type { IconProps } from './Icon.types';
18
17
  * ```
19
18
  */
20
19
  export function Icon({
21
- name,
22
- size,
20
+ name = 'add',
21
+ size = 24,
23
22
  opticalSize,
24
23
  fill = 0,
25
24
  color,
@@ -33,6 +32,9 @@ export function Icon({
33
32
  }: IconProps) {
34
33
  // Inherit tokens from nearest parent Frame via FrameContext.
35
34
  const tokens = useTokens();
35
+ const frameCtx = useFrameContext();
36
+ const resolvedTheme = frameCtx?.theme ?? 'primary';
37
+ const resolvedAppearance = frameCtx?.appearance ?? 'main';
36
38
 
37
39
  // Build the icon's style from the theme tokens and props.
38
40
  // Wrapped in useMemo so it only recalculates when the inputs change,
@@ -54,8 +56,8 @@ export function Icon({
54
56
  // Use explicit opticalSize if provided, otherwise auto-calculate from fontSize.
55
57
  const opsz = opticalSize ?? getOpticalSize(fontSize);
56
58
 
57
- // Use the provided color, or fall back to the theme's primary text color.
58
- const iconColor = color ?? srgbToHex(tokens.textPrimary.srgb);
59
+ // Use the provided color, or fall back to the inherited theme/appearance text color.
60
+ const iconColor = color ?? tokens.colors[resolvedTheme][resolvedAppearance].fontPrimary;
59
61
 
60
62
  // Build the font family name from the theme's icon variant setting.
61
63
  // Example: variant 'rounded' → 'Material Symbols Rounded'
@@ -81,7 +83,7 @@ export function Icon({
81
83
  fontVariationSettings, // web-only: controls the variable font axes listed above
82
84
  ...style,
83
85
  } as TextStyle; // Cast needed because web-only properties aren't in RN's type definitions
84
- }, [tokens, size, opticalSize, fill, color, style]);
86
+ }, [tokens, size, opticalSize, fill, color, style, resolvedTheme, resolvedAppearance]);
85
87
 
86
88
  // Material Symbols renders icons as text ligatures — the icon name (like "home")
87
89
  // is passed as text content, and the font renders it as the icon glyph.
@@ -23,9 +23,9 @@ export interface IconProps {
23
23
  * @example `'home'`, `'settings'`, `'check'`, `'expand_more'`, `'delete'`, `'add'`, `'search'`
24
24
  * @see {@link https://fonts.google.com/icons Browse all icons}
25
25
  */
26
- readonly name: string;
26
+ readonly name?: string;
27
27
 
28
- /** Font size in pixels. @default tokens.typography.fontSizes['05'] (16px) */
28
+ /** Font size in pixels. @default 24 */
29
29
  readonly size?: number;
30
30
 
31
31
  /** Optical size for variable font axis. Adjusts stroke weight for readability at small sizes. @default same as size */
@@ -1,7 +1,8 @@
1
1
  import React, { useContext, useMemo } from 'react';
2
2
  import { Text as RNText } from 'react-native';
3
3
  import type { TextStyle } from 'react-native';
4
- import { useTokens } from '../../tokens/useTokens';
4
+ import { useTokens, useFrameContext } from 'newtone-api';
5
+ import type { ThemeName, AppearanceName } from 'newtone-api';
5
6
  import { TextScopeContext, resolveTextColor } from './Text';
6
7
  import type { TextSpanProps, TextWeight } from './Text.types';
7
8
 
@@ -11,18 +12,21 @@ import type { TextSpanProps, TextWeight } from './Text.types';
11
12
  * Only inline formatting properties (color, weight, italic, underline, highlight) are exposed.
12
13
  */
13
14
  export function TextSpan({ children, color, weight, italic, underline, highlight, style }: TextSpanProps) {
14
- const tokens = useTokens(1);
15
+ const tokens = useTokens();
15
16
  const scopeCtx = useContext(TextScopeContext);
17
+ const frameCtx = useFrameContext();
18
+ const resolvedTheme: ThemeName = frameCtx?.theme ?? 'primary';
19
+ const resolvedAppearance: AppearanceName = frameCtx?.appearance ?? 'main';
16
20
 
17
21
  const spanStyle = useMemo<TextStyle>(() => {
18
22
  const s: TextStyle = {};
19
- if (color) s.color = resolveTextColor(color, tokens);
23
+ if (color) s.color = resolveTextColor(color, tokens, resolvedTheme, resolvedAppearance);
20
24
  if (weight && scopeCtx) s.fontWeight = String(scopeCtx.weights[weight]) as TextStyle['fontWeight'];
21
25
  if (italic) s.fontStyle = 'italic';
22
26
  if (underline) s.textDecorationLine = 'underline';
23
- if (highlight) s.backgroundColor = resolveTextColor(highlight, tokens);
27
+ if (highlight) s.backgroundColor = resolveTextColor(highlight, tokens, resolvedTheme, resolvedAppearance);
24
28
  return s;
25
- }, [tokens, scopeCtx, color, weight, italic, underline, highlight]);
29
+ }, [tokens, scopeCtx, color, weight, italic, underline, highlight, resolvedTheme, resolvedAppearance]);
26
30
 
27
31
  return React.createElement(
28
32
  RNText,