@newtonedev/components 0.1.5 → 0.1.7

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 (69) hide show
  1. package/dist/composites/actions/Button/Button.d.ts.map +1 -1
  2. package/dist/composites/actions/Button/Button.styles.d.ts +3 -1
  3. package/dist/composites/actions/Button/Button.styles.d.ts.map +1 -1
  4. package/dist/index.cjs +603 -249
  5. package/dist/index.cjs.map +1 -1
  6. package/dist/index.d.ts +6 -5
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +599 -251
  9. package/dist/index.js.map +1 -1
  10. package/dist/primitives/Frame/Frame.d.ts +2 -3
  11. package/dist/primitives/Frame/Frame.d.ts.map +1 -1
  12. package/dist/primitives/Frame/Frame.types.d.ts +4 -15
  13. package/dist/primitives/Frame/Frame.types.d.ts.map +1 -1
  14. package/dist/primitives/Icon/Icon.d.ts +1 -1
  15. package/dist/primitives/Icon/Icon.d.ts.map +1 -1
  16. package/dist/primitives/Icon/Icon.types.d.ts +7 -12
  17. package/dist/primitives/Icon/Icon.types.d.ts.map +1 -1
  18. package/dist/primitives/Text/Text.d.ts.map +1 -1
  19. package/dist/primitives/Text/Text.types.d.ts +9 -4
  20. package/dist/primitives/Text/Text.types.d.ts.map +1 -1
  21. package/dist/primitives/Wrapper/Wrapper.d.ts +1 -1
  22. package/dist/primitives/Wrapper/Wrapper.types.d.ts +1 -1
  23. package/dist/registry/icons.d.ts +7 -0
  24. package/dist/registry/icons.d.ts.map +1 -0
  25. package/dist/registry/index.d.ts +2 -0
  26. package/dist/registry/index.d.ts.map +1 -1
  27. package/dist/registry/registry.d.ts.map +1 -1
  28. package/dist/registry/types.d.ts +1 -1
  29. package/dist/registry/types.d.ts.map +1 -1
  30. package/dist/theme/FrameContext.d.ts +7 -5
  31. package/dist/theme/FrameContext.d.ts.map +1 -1
  32. package/dist/theme/NewtoneProvider.d.ts +5 -6
  33. package/dist/theme/NewtoneProvider.d.ts.map +1 -1
  34. package/dist/theme/defaults.d.ts.map +1 -1
  35. package/dist/theme/types.d.ts +38 -24
  36. package/dist/theme/types.d.ts.map +1 -1
  37. package/dist/tokens/computeTokens.d.ts +82 -7
  38. package/dist/tokens/computeTokens.d.ts.map +1 -1
  39. package/dist/tokens/types.d.ts +58 -16
  40. package/dist/tokens/types.d.ts.map +1 -1
  41. package/dist/tokens/useTokens.d.ts +2 -23
  42. package/dist/tokens/useTokens.d.ts.map +1 -1
  43. package/package.json +1 -1
  44. package/src/composites/actions/Button/Button.styles.ts +53 -80
  45. package/src/composites/actions/Button/Button.tsx +6 -2
  46. package/src/composites/form-controls/Select/SelectOption.tsx +2 -2
  47. package/src/composites/form-controls/Toggle/Toggle.styles.ts +1 -1
  48. package/src/composites/range-inputs/ColorScaleSlider/ColorScaleSlider.styles.ts +1 -1
  49. package/src/composites/range-inputs/Slider/Slider.styles.ts +2 -2
  50. package/src/index.ts +13 -4
  51. package/src/primitives/Frame/Frame.tsx +10 -18
  52. package/src/primitives/Frame/Frame.types.ts +5 -17
  53. package/src/primitives/Icon/Icon.tsx +4 -6
  54. package/src/primitives/Icon/Icon.types.ts +7 -14
  55. package/src/primitives/Text/Text.tsx +18 -8
  56. package/src/primitives/Text/Text.types.ts +9 -4
  57. package/src/primitives/Wrapper/Wrapper.tsx +1 -1
  58. package/src/primitives/Wrapper/Wrapper.types.ts +1 -1
  59. package/src/registry/icons.ts +111 -0
  60. package/src/registry/index.ts +3 -0
  61. package/src/registry/registry.ts +40 -24
  62. package/src/registry/types.ts +1 -1
  63. package/src/theme/FrameContext.tsx +7 -5
  64. package/src/theme/NewtoneProvider.tsx +5 -10
  65. package/src/theme/defaults.ts +0 -9
  66. package/src/theme/types.ts +53 -26
  67. package/src/tokens/computeTokens.ts +338 -116
  68. package/src/tokens/types.ts +74 -16
  69. package/src/tokens/useTokens.ts +16 -33
@@ -1,5 +1,5 @@
1
1
  import type { View, ViewStyle, GestureResponderEvent } from 'react-native';
2
- import type { ThemeName, FrameElevation } from '../../theme/types';
2
+ import type { FrameElevation } from '../../theme/types';
3
3
 
4
4
  // ── Spacing Types ──────────────────────────────────────────────
5
5
 
@@ -115,14 +115,14 @@ export type Justification = 'start' | 'center' | 'end' | 'between' | 'around' |
115
115
  * // Horizontal toolbar with consistent spacing
116
116
  * <Frame direction="horizontal" gap="md" padding="lg" align="center">
117
117
  * <Button onPress={() => {}}>Save</Button>
118
- * <Button variant="ghost" onPress={() => {}}>Cancel</Button>
118
+ * <Button variant="tertiary" onPress={() => {}}>Cancel</Button>
119
119
  * </Frame>
120
120
  * ```
121
121
  *
122
122
  * @example
123
123
  * ```tsx
124
- * // Elevated card with primary theme
125
- * <Frame theme="primary" elevation={2} radius="lg" padding="xl" bordered>
124
+ * // Elevated card
125
+ * <Frame elevation={2} radius="lg" padding="xl" bordered>
126
126
  * <Text>Elevated card content</Text>
127
127
  * </Frame>
128
128
  * ```
@@ -142,19 +142,7 @@ export interface FrameProps {
142
142
  */
143
143
  readonly children: React.ReactNode;
144
144
 
145
- // ── Theme & Elevation ──
146
-
147
- /**
148
- * Theme override for this frame and all descendants.
149
- * When omitted, inherits from parent Frame or NewtoneProvider.
150
- *
151
- * @example
152
- * ```tsx
153
- * <Frame theme="primary"> // Blue-ish surface
154
- * <Frame theme="strong"> // High-contrast surface
155
- * ```
156
- */
157
- readonly theme?: ThemeName;
145
+ // ── Elevation ──
158
146
 
159
147
  /**
160
148
  * Surface elevation: controls background lightness and shadow.
@@ -23,9 +23,7 @@ export function Icon({
23
23
  opticalSize,
24
24
  fill = 0,
25
25
  color,
26
- elevation = 1,
27
26
  style,
28
- onPress,
29
27
  // Accessibility
30
28
  accessibilityLabel,
31
29
  // Testing & platform
@@ -33,8 +31,8 @@ export function Icon({
33
31
  nativeID,
34
32
  ref,
35
33
  }: IconProps) {
36
- // Get the current theme's design tokens for colors, typography, and icon settings.
37
- const tokens = useTokens(elevation);
34
+ // Inherit tokens from nearest parent Frame via FrameContext.
35
+ const tokens = useTokens();
38
36
 
39
37
  // Build the icon's style from the theme tokens and props.
40
38
  // Wrapped in useMemo so it only recalculates when the inputs change,
@@ -69,7 +67,8 @@ export function Icon({
69
67
  // wght: font weight (thinner or bolder strokes)
70
68
  // GRAD: grade (fine-tune weight without changing overall size)
71
69
  // opsz: optical size (adjusts detail level for the display size)
72
- const fontVariationSettings = `'FILL' ${fill}, 'wght' ${tokens.icons.weight}, 'GRAD' ${tokens.icons.grade}, 'opsz' ${opsz}`;
70
+ const fillValue = typeof fill === 'boolean' ? (fill ? 1 : 0) : fill;
71
+ const fontVariationSettings = `'FILL' ${fillValue}, 'wght' ${tokens.icons.weight}, 'GRAD' ${tokens.icons.grade}, 'opsz' ${opsz}`;
73
72
 
74
73
  return {
75
74
  fontFamily,
@@ -96,7 +95,6 @@ export function Icon({
96
95
  // so assistive technology doesn't try to read the ligature text (e.g. "home").
97
96
  importantForAccessibility={accessibilityLabel ? 'yes' : 'no-hide-descendants'}
98
97
  style={iconStyle}
99
- onPress={onPress} // When provided, makes the icon tappable
100
98
  >
101
99
  {name}
102
100
  </Text>
@@ -1,18 +1,17 @@
1
1
  import type { Text as RNText, TextStyle } from 'react-native';
2
- import type { ElevationLevel } from '../../theme/types';
3
2
 
4
3
  /**
5
4
  * Props for Icon — Material Symbols icon with variable font support.
6
5
  *
7
- * Uses the icon variant, weight, and grade from the theme config.
8
- * Per-instance control over size, fill, and color.
6
+ * Icon is a pure presentation primitive. It inherits elevation from the
7
+ * nearest parent Frame via FrameContext. For interactive icons, wrap in
8
+ * `<Button icon="..." />` instead.
9
9
  *
10
10
  * @example
11
11
  * ```tsx
12
12
  * <Icon name="home" />
13
13
  * <Icon name="settings" size={24} fill={1} />
14
14
  * <Icon name="check" color="#00ff00" />
15
- * <Icon name="delete" size={20} onPress={() => handleDelete()} />
16
15
  * ```
17
16
  *
18
17
  * @see {@link https://fonts.google.com/icons Material Symbols icon reference}
@@ -35,23 +34,17 @@ export interface IconProps {
35
34
  /**
36
35
  * Fill state for the icon.
37
36
  *
38
- * - `0` — Outlined (default)
39
- * - `1` — Filled (solid)
37
+ * - `false` / `0` — Outlined (default)
38
+ * - `true` / `1` — Filled (solid)
40
39
  */
41
- readonly fill?: 0 | 1;
40
+ readonly fill?: boolean | 0 | 1;
42
41
 
43
42
  /** Icon color as hex string. @default tokens.textPrimary */
44
43
  readonly color?: string;
45
44
 
46
- /** Elevation level for token computation. @default 1 */
47
- readonly elevation?: ElevationLevel;
48
-
49
- /** Additional styles applied to the icon Text element. */
45
+ /** Additional styles applied to the icon Text element (positioning, transforms). */
50
46
  readonly style?: TextStyle;
51
47
 
52
- /** Press handler — makes the icon tappable. */
53
- readonly onPress?: () => void;
54
-
55
48
  // ── Accessibility ──
56
49
 
57
50
  /** Label read by screen readers. When omitted, the icon is treated as decorative (hidden from assistive technology). */
@@ -3,15 +3,25 @@ import { Text as RNText } from 'react-native';
3
3
  import type { TextStyle } from 'react-native';
4
4
  import { srgbToHex } from 'newtone';
5
5
  import { useTokens } from '../../tokens/useTokens';
6
+ import type { UseTokensResult } from '../../tokens/useTokens';
6
7
  import type { TextProps, TextColor } from './Text.types';
7
8
 
8
- // Maps user-friendly color names to the actual token keys in the theme.
9
- // Example: color="primary" looks up tokens.textPrimary to get the hex color.
10
- const COLOR_MAP: Record<TextColor, 'textPrimary' | 'textSecondary' | 'interactive'> = {
11
- primary: 'textPrimary',
12
- secondary: 'textSecondary',
13
- interactive: 'interactive',
14
- };
9
+ /**
10
+ * Resolve a semantic text color to a hex string from the current tokens.
11
+ * Neutral text colors are top-level; palette colors use nested PaletteTokens.fill.
12
+ */
13
+ function resolveTextColor(color: TextColor, tokens: UseTokensResult): string {
14
+ switch (color) {
15
+ case 'primary': return srgbToHex(tokens.textPrimary.srgb);
16
+ case 'secondary': return srgbToHex(tokens.textSecondary.srgb);
17
+ case 'tertiary': return srgbToHex(tokens.textTertiary.srgb);
18
+ case 'disabled': return srgbToHex(tokens.textDisabled.srgb);
19
+ case 'accent': return srgbToHex(tokens.accent.fill.srgb);
20
+ case 'success': return srgbToHex(tokens.success.fill.srgb);
21
+ case 'warning': return srgbToHex(tokens.warning.fill.srgb);
22
+ case 'error': return srgbToHex(tokens.error.fill.srgb);
23
+ }
24
+ }
15
25
 
16
26
  /**
17
27
  * Token-aware text primitive.
@@ -61,7 +71,7 @@ export function Text({
61
71
  // Font weight is stored as a number (e.g. 400, 600) but React Native expects a string.
62
72
  fontWeight: String(tokens.typography.weight[weight]) as TextStyle['fontWeight'],
63
73
  // Convert the theme color from internal sRGB format to a hex string (e.g. '#1a1a1a').
64
- color: srgbToHex(tokens[COLOR_MAP[color]].srgb),
74
+ color: resolveTextColor(color, tokens),
65
75
  // Line height = font size × multiplier (e.g. 16px × 1.5 = 24px line height).
66
76
  lineHeight: fontSize * tokens.typography.lineHeight[lineHeight],
67
77
  textAlign: align,
@@ -8,7 +8,7 @@ export type TextSize = 'xs' | 'sm' | 'base' | 'md' | 'lg' | 'xl' | 'xxl';
8
8
  export type TextWeight = 'regular' | 'medium' | 'semibold' | 'bold';
9
9
 
10
10
  /** Semantic text color tokens — resolved from the current theme's token palette. */
11
- export type TextColor = 'primary' | 'secondary' | 'interactive';
11
+ export type TextColor = 'primary' | 'secondary' | 'tertiary' | 'disabled' | 'accent' | 'success' | 'warning' | 'error';
12
12
 
13
13
  /** Font family tokens — maps to `tokens.typography.fonts.*` font stacks. */
14
14
  export type TextFont = 'default' | 'display' | 'mono';
@@ -58,9 +58,14 @@ export interface TextProps {
58
58
  /**
59
59
  * Semantic color token.
60
60
  *
61
- * - `'primary'` — Main text color (default)
62
- * - `'secondary'` — Subdued/muted text
63
- * - `'interactive'` — Accent color for links/actions
61
+ * - `'primary'` — Main text color, high contrast
62
+ * - `'secondary'` — Subdued/muted text (captions, labels)
63
+ * - `'tertiary'` — Hints, placeholders
64
+ * - `'disabled'` — Disabled text elements
65
+ * - `'accent'` — Accent palette color for links/actions
66
+ * - `'success'` — Success palette color for positive indicators
67
+ * - `'warning'` — Warning palette color for caution indicators
68
+ * - `'error'` — Error palette color for destructive/error indicators
64
69
  *
65
70
  * @default 'primary'
66
71
  */
@@ -18,7 +18,7 @@ import { useTokens } from '../../tokens/useTokens';
18
18
  * ```tsx
19
19
  * <Wrapper direction="horizontal" gap="md" align="center">
20
20
  * <Button onPress={() => {}}>Save</Button>
21
- * <Button variant="ghost" onPress={() => {}}>Cancel</Button>
21
+ * <Button variant="tertiary" onPress={() => {}}>Cancel</Button>
22
22
  * </Wrapper>
23
23
  * ```
24
24
  *
@@ -23,7 +23,7 @@ import type {
23
23
  * // Horizontal row with spacing
24
24
  * <Wrapper direction="horizontal" gap="md" align="center">
25
25
  * <Button onPress={() => {}}>Save</Button>
26
- * <Button variant="ghost" onPress={() => {}}>Cancel</Button>
26
+ * <Button variant="tertiary" onPress={() => {}}>Cancel</Button>
27
27
  * </Wrapper>
28
28
  * ```
29
29
  *
@@ -0,0 +1,111 @@
1
+ export interface IconCatalogCategory {
2
+ readonly id: string;
3
+ readonly label: string;
4
+ readonly icons: readonly string[];
5
+ }
6
+
7
+ export const ICON_CATALOG: readonly IconCatalogCategory[] = [
8
+ {
9
+ id: 'navigation',
10
+ label: 'Navigation',
11
+ icons: [
12
+ 'home', 'menu', 'close', 'arrow_back', 'arrow_forward',
13
+ 'arrow_upward', 'arrow_downward', 'chevron_left', 'chevron_right',
14
+ 'expand_more', 'expand_less', 'first_page', 'last_page',
15
+ 'more_vert', 'more_horiz', 'unfold_more', 'unfold_less',
16
+ 'subdirectory_arrow_right',
17
+ ],
18
+ },
19
+ {
20
+ id: 'actions',
21
+ label: 'Actions',
22
+ icons: [
23
+ 'search', 'settings', 'done', 'add', 'remove',
24
+ 'delete', 'edit', 'save', 'refresh', 'undo',
25
+ 'redo', 'download', 'upload', 'share', 'print',
26
+ 'content_copy', 'content_paste', 'open_in_new', 'launch',
27
+ 'drag_indicator', 'tune', 'sort', 'filter_list',
28
+ ],
29
+ },
30
+ {
31
+ id: 'communication',
32
+ label: 'Communication',
33
+ icons: [
34
+ 'mail', 'chat', 'call', 'notifications', 'forum',
35
+ 'send', 'inbox', 'drafts', 'mark_email_read',
36
+ 'contact_mail', 'alternate_email', 'comment',
37
+ 'chat_bubble', 'sms', 'voicemail',
38
+ ],
39
+ },
40
+ {
41
+ id: 'content',
42
+ label: 'Content',
43
+ icons: [
44
+ 'add_circle', 'remove_circle', 'check_circle', 'cancel',
45
+ 'flag', 'bookmark', 'star', 'favorite',
46
+ 'thumb_up', 'thumb_down', 'push_pin', 'link',
47
+ 'link_off', 'bolt', 'label', 'inventory_2',
48
+ 'visibility', 'visibility_off',
49
+ ],
50
+ },
51
+ {
52
+ id: 'media',
53
+ label: 'Media',
54
+ icons: [
55
+ 'play_arrow', 'pause', 'stop', 'skip_next', 'skip_previous',
56
+ 'fast_forward', 'fast_rewind', 'replay', 'shuffle',
57
+ 'repeat', 'volume_up', 'volume_off', 'music_note',
58
+ 'image', 'photo_camera', 'videocam', 'mic',
59
+ ],
60
+ },
61
+ {
62
+ id: 'file',
63
+ label: 'File & Folder',
64
+ icons: [
65
+ 'folder', 'folder_open', 'create_new_folder', 'description',
66
+ 'file_copy', 'attach_file', 'cloud', 'cloud_upload',
67
+ 'cloud_download', 'cloud_off', 'storage', 'snippet_folder',
68
+ ],
69
+ },
70
+ {
71
+ id: 'social',
72
+ label: 'Social & People',
73
+ icons: [
74
+ 'person', 'group', 'person_add', 'person_remove',
75
+ 'people', 'face', 'sentiment_satisfied', 'sentiment_dissatisfied',
76
+ 'public', 'workspace_premium', 'emoji_events', 'military_tech',
77
+ ],
78
+ },
79
+ {
80
+ id: 'alerts',
81
+ label: 'Alerts & Status',
82
+ icons: [
83
+ 'error', 'warning', 'info', 'help',
84
+ 'check_circle', 'report', 'new_releases',
85
+ 'notification_important', 'priority_high',
86
+ 'verified', 'shield', 'security',
87
+ 'lock', 'lock_open',
88
+ ],
89
+ },
90
+ {
91
+ id: 'device',
92
+ label: 'Device & Hardware',
93
+ icons: [
94
+ 'phone_android', 'computer', 'tablet_mac', 'watch',
95
+ 'keyboard', 'mouse', 'headphones', 'speaker',
96
+ 'monitor', 'devices', 'memory', 'battery_full',
97
+ 'wifi', 'bluetooth', 'usb', 'dark_mode', 'light_mode',
98
+ ],
99
+ },
100
+ {
101
+ id: 'editor',
102
+ label: 'Editor & Formatting',
103
+ icons: [
104
+ 'format_bold', 'format_italic', 'format_underlined',
105
+ 'format_list_bulleted', 'format_list_numbered', 'format_quote',
106
+ 'format_align_left', 'format_align_center', 'format_align_right',
107
+ 'title', 'text_fields', 'code', 'palette',
108
+ 'color_lens', 'brush', 'auto_fix_high',
109
+ ],
110
+ },
111
+ ];
@@ -15,3 +15,6 @@ export {
15
15
  } from './registry';
16
16
 
17
17
  export { generateComponentCode } from './codegen';
18
+
19
+ export type { IconCatalogCategory } from './icons';
20
+ export { ICON_CATALOG } from './icons';
@@ -20,13 +20,17 @@ export const COMPONENTS: readonly ComponentMeta[] = [
20
20
  variants: [
21
21
  { id: 'primary-md', label: 'Primary', props: { variant: 'primary', size: 'md' } },
22
22
  { id: 'secondary-md', label: 'Secondary', props: { variant: 'secondary', size: 'md' } },
23
- { id: 'ghost-md', label: 'Ghost', props: { variant: 'ghost', size: 'md' } },
24
- { id: 'outline-md', label: 'Outline', props: { variant: 'outline', size: 'md' } },
23
+ { id: 'tertiary-md', label: 'Tertiary', props: { variant: 'tertiary', size: 'md' } },
25
24
  { id: 'primary-sm', label: 'Primary Small', props: { variant: 'primary', size: 'sm' } },
26
25
  { id: 'primary-lg', label: 'Primary Large', props: { variant: 'primary', size: 'lg' } },
26
+ { id: 'accent-primary', label: 'Accent Primary', props: { variant: 'primary', size: 'md', semantic: 'accent' } },
27
+ { id: 'accent-secondary', label: 'Accent Secondary', props: { variant: 'secondary', size: 'md', semantic: 'accent' } },
28
+ { id: 'success-primary', label: 'Success Primary', props: { variant: 'primary', size: 'md', semantic: 'success' } },
29
+ { id: 'error-primary', label: 'Error Primary', props: { variant: 'primary', size: 'md', semantic: 'error' } },
30
+ { id: 'warning-primary', label: 'Warning Primary', props: { variant: 'primary', size: 'md', semantic: 'warning' } },
27
31
  { id: 'icon-left', label: 'Icon Left', props: { variant: 'primary', size: 'md', icon: 'add' } },
28
32
  { id: 'icon-right', label: 'Icon Right', props: { variant: 'primary', size: 'md', icon: 'arrow_forward', iconPosition: 'right' } },
29
- { id: 'icon-only', label: 'Icon Only', props: { variant: 'ghost', size: 'md', icon: 'settings' } },
33
+ { id: 'icon-only', label: 'Icon Only', props: { variant: 'tertiary', size: 'md', icon: 'settings' } },
30
34
  ],
31
35
  editableProps: [
32
36
  {
@@ -36,11 +40,23 @@ export const COMPONENTS: readonly ComponentMeta[] = [
36
40
  options: [
37
41
  { label: 'Primary', value: 'primary' },
38
42
  { label: 'Secondary', value: 'secondary' },
39
- { label: 'Ghost', value: 'ghost' },
40
- { label: 'Outline', value: 'outline' },
43
+ { label: 'Tertiary', value: 'tertiary' },
41
44
  ],
42
45
  defaultValue: 'primary',
43
46
  },
47
+ {
48
+ name: 'semantic',
49
+ label: 'Semantic',
50
+ control: 'select',
51
+ options: [
52
+ { label: 'Neutral', value: 'neutral' },
53
+ { label: 'Accent', value: 'accent' },
54
+ { label: 'Success', value: 'success' },
55
+ { label: 'Warning', value: 'warning' },
56
+ { label: 'Error', value: 'error' },
57
+ ],
58
+ defaultValue: 'neutral',
59
+ },
44
60
  {
45
61
  name: 'size',
46
62
  label: 'Size',
@@ -403,7 +419,7 @@ export const COMPONENTS: readonly ComponentMeta[] = [
403
419
  { id: 'subheading', label: 'Subheading', props: { size: 'lg', weight: 'semibold' } },
404
420
  { id: 'body', label: 'Body', props: { size: 'base' } },
405
421
  { id: 'caption', label: 'Caption', props: { size: 'sm', color: 'secondary' } },
406
- { id: 'link', label: 'Link', props: { color: 'interactive', weight: 'medium' } },
422
+ { id: 'accent', label: 'Accent', props: { color: 'accent', weight: 'medium' } },
407
423
  { id: 'mono', label: 'Monospace', props: { font: 'mono', size: 'sm' } },
408
424
  ],
409
425
  editableProps: [
@@ -441,7 +457,12 @@ export const COMPONENTS: readonly ComponentMeta[] = [
441
457
  options: [
442
458
  { label: 'Primary', value: 'primary' },
443
459
  { label: 'Secondary', value: 'secondary' },
444
- { label: 'Interactive', value: 'interactive' },
460
+ { label: 'Tertiary', value: 'tertiary' },
461
+ { label: 'Disabled', value: 'disabled' },
462
+ { label: 'Accent', value: 'accent' },
463
+ { label: 'Success', value: 'success' },
464
+ { label: 'Warning', value: 'warning' },
465
+ { label: 'Error', value: 'error' },
445
466
  ],
446
467
  defaultValue: 'primary',
447
468
  },
@@ -463,40 +484,35 @@ export const COMPONENTS: readonly ComponentMeta[] = [
463
484
  name: 'Icon',
464
485
  importName: 'Icon',
465
486
  categoryId: 'primitives',
466
- description: 'Material Symbols icon with size, fill, and color',
487
+ description: 'Material Symbols icon with size and fill',
467
488
  hasChildren: false,
468
489
  variants: [
469
- { id: 'home', label: 'Home', props: { name: 'home' } },
470
- { id: 'settings', label: 'Settings', props: { name: 'settings' } },
471
- { id: 'check', label: 'Check', props: { name: 'check' } },
472
- { id: 'add', label: 'Add', props: { name: 'add' } },
473
- { id: 'delete', label: 'Delete', props: { name: 'delete' } },
474
- { id: 'search', label: 'Search', props: { name: 'search' } },
475
- { id: 'filled', label: 'Filled Icon', props: { name: 'favorite', fill: 1 } },
476
- { id: 'large', label: 'Large Icon', props: { name: 'star', size: 32 } },
490
+ { id: 'default', label: 'Default', props: { name: 'add' } },
477
491
  ],
478
492
  editableProps: [
479
493
  {
480
494
  name: 'name',
481
495
  label: 'Icon Name',
482
496
  control: 'text',
483
- defaultValue: 'home',
497
+ defaultValue: 'add',
484
498
  },
485
499
  {
486
500
  name: 'size',
487
501
  label: 'Size',
488
- control: 'number',
502
+ control: 'discrete-slider',
503
+ options: [
504
+ { label: '20', value: 20 },
505
+ { label: '24', value: 24 },
506
+ { label: '40', value: 40 },
507
+ { label: '48', value: 48 },
508
+ ],
489
509
  defaultValue: 24,
490
510
  },
491
511
  {
492
512
  name: 'fill',
493
513
  label: 'Fill',
494
- control: 'select',
495
- options: [
496
- { label: 'Outlined', value: 0 },
497
- { label: 'Filled', value: 1 },
498
- ],
499
- defaultValue: 0,
514
+ control: 'toggle',
515
+ defaultValue: false,
500
516
  },
501
517
  ],
502
518
  },
@@ -18,7 +18,7 @@ export interface EditablePropOption {
18
18
  export interface EditableProp {
19
19
  readonly name: string;
20
20
  readonly label: string;
21
- readonly control: 'select' | 'text' | 'toggle' | 'number';
21
+ readonly control: 'select' | 'text' | 'toggle' | 'number' | 'discrete-slider';
22
22
  readonly options?: readonly EditablePropOption[];
23
23
  readonly defaultValue: string | number | boolean;
24
24
  }
@@ -1,19 +1,21 @@
1
1
  import { createContext, useContext } from 'react';
2
- import type { ThemeName, ElevationLevel } from './types';
2
+ import type { ElevationLevel } from './types';
3
+ import type { ResolvedTokens } from '../tokens/types';
3
4
 
4
5
  /**
5
6
  * Context value provided by Frame to its descendants.
6
- * Contains the resolved theme and elevation that children should use.
7
+ * Contains the resolved elevation and pre-computed tokens.
8
+ * Tokens are included to avoid redundant computeTokens calls in child components.
7
9
  */
8
10
  export interface FrameContextValue {
9
- readonly theme: ThemeName;
10
11
  readonly elevation: ElevationLevel;
12
+ readonly tokens: ResolvedTokens;
11
13
  }
12
14
 
13
15
  /**
14
- * FrameContext - Propagates theme and elevation overrides from Frame to descendants.
16
+ * FrameContext - Propagates elevation overrides from Frame to descendants.
15
17
  *
16
- * When null, components fall back to NewtoneProvider's theme and default elevation (1).
18
+ * When null, components fall back to default elevation (1).
17
19
  * When present, useTokens() reads from this context instead.
18
20
  */
19
21
  export const FrameContext = createContext<FrameContextValue | null>(null);
@@ -1,5 +1,5 @@
1
1
  import React, { createContext, useState, useMemo, useContext } from 'react';
2
- import type { NewtoneThemeConfig, NewtoneThemeContext, ColorMode, ThemeName } from './types';
2
+ import type { NewtoneThemeConfig, NewtoneThemeContext, ColorMode } from './types';
3
3
  import { DEFAULT_THEME_CONFIG } from './defaults';
4
4
  import { GoogleFontLoader } from '../fonts/GoogleFontLoader';
5
5
  import { IconFontLoader } from '../fonts/IconFontLoader';
@@ -9,18 +9,17 @@ const ThemeContext = createContext<NewtoneThemeContext | null>(null);
9
9
  export interface NewtoneProviderProps {
10
10
  readonly config?: NewtoneThemeConfig;
11
11
  readonly initialMode?: ColorMode;
12
- readonly initialTheme?: ThemeName;
13
12
  readonly children: React.ReactNode;
14
13
  }
15
14
 
16
15
  /**
17
16
  * NewtoneProvider - Provides theme context to all Newtone components
18
17
  *
19
- * Wrap your app with this provider to enable theme and mode switching.
18
+ * Wrap your app with this provider to enable mode switching.
20
19
  *
21
20
  * @example
22
21
  * ```tsx
23
- * <NewtoneProvider initialMode="light" initialTheme="neutral">
22
+ * <NewtoneProvider initialMode="light">
24
23
  * <App />
25
24
  * </NewtoneProvider>
26
25
  * ```
@@ -28,21 +27,17 @@ export interface NewtoneProviderProps {
28
27
  export function NewtoneProvider({
29
28
  config = DEFAULT_THEME_CONFIG,
30
29
  initialMode = 'light',
31
- initialTheme = 'neutral',
32
30
  children,
33
31
  }: NewtoneProviderProps) {
34
32
  const [mode, setMode] = useState<ColorMode>(initialMode);
35
- const [theme, setTheme] = useState<ThemeName>(initialTheme);
36
33
 
37
34
  const value = useMemo(
38
35
  () => ({
39
36
  config,
40
37
  mode,
41
- theme,
42
38
  setMode,
43
- setTheme,
44
39
  }),
45
- [config, mode, theme]
40
+ [config, mode]
46
41
  );
47
42
 
48
43
  return (
@@ -61,7 +56,7 @@ export function NewtoneProvider({
61
56
  *
62
57
  * @example
63
58
  * ```tsx
64
- * const { mode, theme, setMode, setTheme } = useNewtoneTheme();
59
+ * const { mode, setMode } = useNewtoneTheme();
65
60
  * ```
66
61
  */
67
62
  export function useNewtoneTheme(): NewtoneThemeContext {
@@ -30,15 +30,6 @@ export const DEFAULT_THEME_CONFIG: NewtoneThemeConfig = {
30
30
  { hue: DEFAULT_ERROR_HUE, saturation: DEFAULT_ERROR_SATURATION },
31
31
  ],
32
32
  },
33
- themes: {
34
- neutral: { paletteIndex: 0, lightModeNv: 0.95, darkModeNv: 0.1 },
35
- primary: { paletteIndex: 1, lightModeNv: 0.95, darkModeNv: 0.1 },
36
- secondary: { paletteIndex: 1, lightModeNv: 0.85, darkModeNv: 0.15 },
37
- strong: { paletteIndex: 0, lightModeNv: 0.1, darkModeNv: 0.95 },
38
- },
39
- elevation: {
40
- offsets: [-0.02, 0, 0.04], // [sunken, default, elevated]
41
- },
42
33
  spacing: {
43
34
  '00': 0, // base * 0
44
35
  '02': 2, // base * 0.25