@newtonedev/components 0.1.13 → 0.1.15

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 (173) hide show
  1. package/dist/composites/actions/Button/Button.d.ts +17 -20
  2. package/dist/composites/actions/Button/Button.d.ts.map +1 -1
  3. package/dist/composites/actions/Button/Button.styles.d.ts +12 -24
  4. package/dist/composites/actions/Button/Button.styles.d.ts.map +1 -1
  5. package/dist/composites/actions/Button/Button.types.d.ts +14 -13
  6. package/dist/composites/actions/Button/Button.types.d.ts.map +1 -1
  7. package/dist/composites/actions/Button/index.d.ts +1 -1
  8. package/dist/composites/actions/Button/index.d.ts.map +1 -1
  9. package/dist/composites/branding/LogoMonogram/LogoMonogram.d.ts +10 -0
  10. package/dist/composites/branding/LogoMonogram/LogoMonogram.d.ts.map +1 -0
  11. package/dist/composites/branding/LogoMonogram/LogoMonogram.types.d.ts +35 -0
  12. package/dist/composites/branding/LogoMonogram/LogoMonogram.types.d.ts.map +1 -0
  13. package/dist/composites/branding/LogoMonogram/index.d.ts +3 -0
  14. package/dist/composites/branding/LogoMonogram/index.d.ts.map +1 -0
  15. package/dist/composites/branding/LogoWordmark/LogoWordmark.d.ts +9 -0
  16. package/dist/composites/branding/LogoWordmark/LogoWordmark.d.ts.map +1 -0
  17. package/dist/composites/branding/LogoWordmark/LogoWordmark.types.d.ts +26 -0
  18. package/dist/composites/branding/LogoWordmark/LogoWordmark.types.d.ts.map +1 -0
  19. package/dist/composites/branding/LogoWordmark/index.d.ts +3 -0
  20. package/dist/composites/branding/LogoWordmark/index.d.ts.map +1 -0
  21. package/dist/composites/display/Chip/Chip.d.ts +25 -0
  22. package/dist/composites/display/Chip/Chip.d.ts.map +1 -0
  23. package/dist/composites/display/Chip/Chip.styles.d.ts +29 -0
  24. package/dist/composites/display/Chip/Chip.styles.d.ts.map +1 -0
  25. package/dist/composites/display/Chip/Chip.types.d.ts +63 -0
  26. package/dist/composites/display/Chip/Chip.types.d.ts.map +1 -0
  27. package/dist/composites/display/Chip/index.d.ts +3 -0
  28. package/dist/composites/display/Chip/index.d.ts.map +1 -0
  29. package/dist/composites/form-controls/Select/Select.d.ts.map +1 -1
  30. package/dist/composites/form-controls/Select/Select.styles.d.ts +2 -2
  31. package/dist/composites/form-controls/Select/Select.styles.d.ts.map +1 -1
  32. package/dist/composites/form-controls/Select/SelectOption.d.ts.map +1 -1
  33. package/dist/composites/form-controls/TextInput/TextInput.d.ts.map +1 -1
  34. package/dist/composites/form-controls/TextInput/TextInput.styles.d.ts +2 -2
  35. package/dist/composites/form-controls/TextInput/TextInput.styles.d.ts.map +1 -1
  36. package/dist/composites/form-controls/Toggle/Toggle.d.ts.map +1 -1
  37. package/dist/composites/form-controls/Toggle/Toggle.styles.d.ts +2 -2
  38. package/dist/composites/form-controls/Toggle/Toggle.styles.d.ts.map +1 -1
  39. package/dist/composites/layout/AppShell/AppShell.d.ts.map +1 -1
  40. package/dist/composites/layout/AppShell/AppShell.styles.d.ts +2 -2
  41. package/dist/composites/layout/AppShell/AppShell.styles.d.ts.map +1 -1
  42. package/dist/composites/layout/Card/Card.d.ts.map +1 -1
  43. package/dist/composites/layout/Card/Card.styles.d.ts +2 -2
  44. package/dist/composites/layout/Card/Card.styles.d.ts.map +1 -1
  45. package/dist/composites/layout/ContentCard/ContentCard.d.ts +33 -0
  46. package/dist/composites/layout/ContentCard/ContentCard.d.ts.map +1 -0
  47. package/dist/composites/layout/ContentCard/ContentCard.styles.d.ts +10 -0
  48. package/dist/composites/layout/ContentCard/ContentCard.styles.d.ts.map +1 -0
  49. package/dist/composites/layout/ContentCard/ContentCard.types.d.ts +52 -0
  50. package/dist/composites/layout/ContentCard/ContentCard.types.d.ts.map +1 -0
  51. package/dist/composites/layout/ContentCard/index.d.ts +3 -0
  52. package/dist/composites/layout/ContentCard/index.d.ts.map +1 -0
  53. package/dist/composites/layout/Divider/Divider.d.ts +25 -0
  54. package/dist/composites/layout/Divider/Divider.d.ts.map +1 -0
  55. package/dist/composites/layout/Divider/Divider.styles.d.ts +8 -0
  56. package/dist/composites/layout/Divider/Divider.styles.d.ts.map +1 -0
  57. package/dist/composites/layout/Divider/Divider.types.d.ts +25 -0
  58. package/dist/composites/layout/Divider/Divider.types.d.ts.map +1 -0
  59. package/dist/composites/layout/Divider/index.d.ts +3 -0
  60. package/dist/composites/layout/Divider/index.d.ts.map +1 -0
  61. package/dist/composites/layout/Navbar/Navbar.d.ts.map +1 -1
  62. package/dist/composites/layout/Navbar/Navbar.styles.d.ts +4 -3
  63. package/dist/composites/layout/Navbar/Navbar.styles.d.ts.map +1 -1
  64. package/dist/composites/layout/Sidebar/Sidebar.d.ts.map +1 -1
  65. package/dist/composites/layout/Sidebar/Sidebar.styles.d.ts +4 -3
  66. package/dist/composites/layout/Sidebar/Sidebar.styles.d.ts.map +1 -1
  67. package/dist/composites/overlays/Popover/Popover.d.ts.map +1 -1
  68. package/dist/composites/overlays/Popover/Popover.styles.d.ts +2 -2
  69. package/dist/composites/overlays/Popover/Popover.styles.d.ts.map +1 -1
  70. package/dist/composites/range-inputs/ColorScaleSlider/ColorScaleSlider.d.ts.map +1 -1
  71. package/dist/composites/range-inputs/ColorScaleSlider/ColorScaleSlider.styles.d.ts +2 -2
  72. package/dist/composites/range-inputs/ColorScaleSlider/ColorScaleSlider.styles.d.ts.map +1 -1
  73. package/dist/composites/range-inputs/HueSlider/HueSlider.styles.d.ts +2 -2
  74. package/dist/composites/range-inputs/HueSlider/HueSlider.styles.d.ts.map +1 -1
  75. package/dist/composites/range-inputs/Slider/Slider.styles.d.ts +2 -2
  76. package/dist/composites/range-inputs/Slider/Slider.styles.d.ts.map +1 -1
  77. package/dist/index.cjs +935 -523
  78. package/dist/index.cjs.map +1 -1
  79. package/dist/index.d.ts +13 -3
  80. package/dist/index.d.ts.map +1 -1
  81. package/dist/index.js +860 -473
  82. package/dist/index.js.map +1 -1
  83. package/dist/primitives/Frame/Frame.d.ts +1 -35
  84. package/dist/primitives/Frame/Frame.d.ts.map +1 -1
  85. package/dist/primitives/Frame/Frame.styles.d.ts +13 -4
  86. package/dist/primitives/Frame/Frame.styles.d.ts.map +1 -1
  87. package/dist/primitives/Frame/Frame.types.d.ts +99 -1
  88. package/dist/primitives/Frame/Frame.types.d.ts.map +1 -1
  89. package/dist/primitives/Frame/index.d.ts +1 -1
  90. package/dist/primitives/Frame/index.d.ts.map +1 -1
  91. package/dist/primitives/Icon/Icon.d.ts.map +1 -1
  92. package/dist/primitives/Icon/Icon.types.d.ts +2 -2
  93. package/dist/primitives/Icon/Icon.types.d.ts.map +1 -1
  94. package/dist/primitives/Text/Text.d.ts +5 -3
  95. package/dist/primitives/Text/Text.d.ts.map +1 -1
  96. package/dist/primitives/Text/Text.spans.d.ts.map +1 -1
  97. package/dist/primitives/Text/Text.types.d.ts +3 -6
  98. package/dist/primitives/Text/Text.types.d.ts.map +1 -1
  99. package/dist/primitives/Wrapper/Wrapper.d.ts +1 -1
  100. package/dist/primitives/Wrapper/Wrapper.d.ts.map +1 -1
  101. package/dist/primitives/Wrapper/Wrapper.styles.d.ts +9 -1
  102. package/dist/primitives/Wrapper/Wrapper.styles.d.ts.map +1 -1
  103. package/dist/primitives/Wrapper/Wrapper.types.d.ts +31 -1
  104. package/dist/primitives/Wrapper/Wrapper.types.d.ts.map +1 -1
  105. package/dist/registry/registry.d.ts.map +1 -1
  106. package/package.json +1 -1
  107. package/src/composites/actions/Button/Button.styles.ts +90 -182
  108. package/src/composites/actions/Button/Button.tsx +37 -33
  109. package/src/composites/actions/Button/Button.types.ts +16 -15
  110. package/src/composites/actions/Button/index.ts +1 -1
  111. package/src/composites/branding/LogoMonogram/LogoMonogram.tsx +29 -0
  112. package/src/composites/branding/LogoMonogram/LogoMonogram.types.ts +35 -0
  113. package/src/composites/branding/LogoMonogram/index.ts +2 -0
  114. package/src/composites/branding/LogoWordmark/LogoWordmark.tsx +29 -0
  115. package/src/composites/branding/LogoWordmark/LogoWordmark.types.ts +25 -0
  116. package/src/composites/branding/LogoWordmark/index.ts +2 -0
  117. package/src/composites/display/Chip/Chip.styles.ts +189 -0
  118. package/src/composites/display/Chip/Chip.tsx +97 -0
  119. package/src/composites/display/Chip/Chip.types.ts +74 -0
  120. package/src/composites/display/Chip/index.ts +2 -0
  121. package/src/composites/form-controls/Select/Select.styles.ts +10 -10
  122. package/src/composites/form-controls/Select/Select.tsx +9 -6
  123. package/src/composites/form-controls/Select/SelectOption.tsx +10 -7
  124. package/src/composites/form-controls/TextInput/TextInput.styles.ts +12 -8
  125. package/src/composites/form-controls/TextInput/TextInput.tsx +7 -4
  126. package/src/composites/form-controls/Toggle/Toggle.styles.ts +10 -7
  127. package/src/composites/form-controls/Toggle/Toggle.tsx +4 -3
  128. package/src/composites/layout/AppShell/AppShell.styles.ts +8 -3
  129. package/src/composites/layout/AppShell/AppShell.tsx +6 -2
  130. package/src/composites/layout/Card/Card.styles.ts +10 -4
  131. package/src/composites/layout/Card/Card.tsx +4 -3
  132. package/src/composites/layout/ContentCard/ContentCard.styles.ts +44 -0
  133. package/src/composites/layout/ContentCard/ContentCard.tsx +71 -0
  134. package/src/composites/layout/ContentCard/ContentCard.types.ts +60 -0
  135. package/src/composites/layout/ContentCard/index.ts +2 -0
  136. package/src/composites/layout/Divider/Divider.styles.ts +30 -0
  137. package/src/composites/layout/Divider/Divider.tsx +46 -0
  138. package/src/composites/layout/Divider/Divider.types.ts +28 -0
  139. package/src/composites/layout/Divider/index.ts +2 -0
  140. package/src/composites/layout/Navbar/Navbar.styles.ts +7 -5
  141. package/src/composites/layout/Navbar/Navbar.tsx +4 -3
  142. package/src/composites/layout/Sidebar/Sidebar.styles.ts +7 -5
  143. package/src/composites/layout/Sidebar/Sidebar.tsx +4 -3
  144. package/src/composites/overlays/Popover/Popover.styles.ts +7 -5
  145. package/src/composites/overlays/Popover/Popover.tsx +4 -3
  146. package/src/composites/range-inputs/ColorScaleSlider/ColorScaleSlider.styles.ts +5 -5
  147. package/src/composites/range-inputs/ColorScaleSlider/ColorScaleSlider.tsx +4 -3
  148. package/src/composites/range-inputs/HueSlider/HueSlider.styles.ts +7 -7
  149. package/src/composites/range-inputs/HueSlider/HueSlider.tsx +1 -1
  150. package/src/composites/range-inputs/Slider/Slider.styles.ts +9 -9
  151. package/src/composites/range-inputs/Slider/Slider.tsx +1 -1
  152. package/src/index.ts +49 -10
  153. package/src/primitives/Frame/Frame.styles.ts +55 -12
  154. package/src/primitives/Frame/Frame.tsx +139 -140
  155. package/src/primitives/Frame/Frame.types.ts +119 -1
  156. package/src/primitives/Frame/index.ts +4 -0
  157. package/src/primitives/Icon/Icon.tsx +9 -6
  158. package/src/primitives/Icon/Icon.types.ts +2 -2
  159. package/src/primitives/Text/Text.spans.ts +9 -5
  160. package/src/primitives/Text/Text.tsx +26 -15
  161. package/src/primitives/Text/Text.types.ts +3 -6
  162. package/src/primitives/Wrapper/Wrapper.styles.ts +32 -0
  163. package/src/primitives/Wrapper/Wrapper.tsx +22 -3
  164. package/src/primitives/Wrapper/Wrapper.types.ts +45 -0
  165. package/src/registry/registry.ts +5 -21
  166. package/dist/_COMPONENT_TEMPLATE/ComponentName.d.ts +0 -70
  167. package/dist/_COMPONENT_TEMPLATE/ComponentName.d.ts.map +0 -1
  168. package/dist/_COMPONENT_TEMPLATE/ComponentName.styles.d.ts +0 -23
  169. package/dist/_COMPONENT_TEMPLATE/ComponentName.styles.d.ts.map +0 -1
  170. package/dist/_COMPONENT_TEMPLATE/ComponentName.types.d.ts +0 -45
  171. package/dist/_COMPONENT_TEMPLATE/ComponentName.types.d.ts.map +0 -1
  172. package/dist/_COMPONENT_TEMPLATE/index.d.ts +0 -3
  173. package/dist/_COMPONENT_TEMPLATE/index.d.ts.map +0 -1
@@ -1,8 +1,18 @@
1
1
  import type { UseTokensResult } from 'newtone-api';
2
- import type { PaletteTokens } from 'newtone-api';
3
- import type { ButtonVariant, ButtonSemantic, ButtonSize } from './Button.types';
2
+ import type { ThemeName } from 'newtone-api';
3
+ import type { ButtonVariant, ButtonSize } from './Button.types';
4
4
  import type { TextSize } from '../../../primitives/Text/Text.types';
5
5
 
6
+ /**
7
+ * Fixed heights per size (px)
8
+ */
9
+ export const BUTTON_HEIGHTS: Record<ButtonSize, number> = {
10
+ sm: 40,
11
+ md: 48,
12
+ lg: 56,
13
+ xl: 64,
14
+ };
15
+
6
16
  /**
7
17
  * Configuration returned by getButtonConfig
8
18
  */
@@ -17,6 +27,7 @@ export interface ButtonConfig {
17
27
  borderColor?: string;
18
28
  };
19
29
  sizeTokens: {
30
+ height: number;
20
31
  padding: number;
21
32
  gap: number;
22
33
  borderRadius: number;
@@ -31,8 +42,6 @@ export interface ButtonConfig {
31
42
  export interface ButtonPadding {
32
43
  paddingLeft: number;
33
44
  paddingRight: number;
34
- paddingTop: number;
35
- paddingBottom: number;
36
45
  }
37
46
 
38
47
  /**
@@ -40,12 +49,6 @@ export interface ButtonPadding {
40
49
  *
41
50
  * Text appears visually closer to borders than icons, so we add 8px extra
42
51
  * horizontal padding on the text side for optical balance.
43
- *
44
- * @param size - Button size (determines base padding)
45
- * @param hasIcon - Whether button has an icon
46
- * @param hasText - Whether button has text
47
- * @param iconPosition - Icon position relative to text
48
- * @returns Padding object with all four sides
49
52
  */
50
53
  export function computeButtonPadding(
51
54
  size: ButtonSize,
@@ -53,111 +56,59 @@ export function computeButtonPadding(
53
56
  hasText: boolean,
54
57
  iconPosition: 'left' | 'right'
55
58
  ): ButtonPadding {
56
- // Size-specific base padding
57
59
  const basePadding: Record<ButtonSize, number> = {
58
- sm: 8,
59
- md: 12,
60
- lg: 16,
60
+ sm: 12,
61
+ md: 16,
62
+ lg: 20,
63
+ xl: 24,
61
64
  };
62
65
 
63
66
  const base = basePadding[size];
64
- const textExtra = 8; // Optical correction for text vs icon
67
+ const textExtra = 8;
65
68
 
66
- // Icon-only: square button
69
+ // Icon-only: symmetric
67
70
  if (!hasText && hasIcon) {
68
- return {
69
- paddingLeft: base,
70
- paddingRight: base,
71
- paddingTop: base,
72
- paddingBottom: base,
73
- };
71
+ return { paddingLeft: base, paddingRight: base };
74
72
  }
75
73
 
76
74
  // Text-only: extra padding on both sides
77
75
  if (hasText && !hasIcon) {
78
- return {
79
- paddingLeft: base + textExtra,
80
- paddingRight: base + textExtra,
81
- paddingTop: base,
82
- paddingBottom: base,
83
- };
76
+ return { paddingLeft: base + textExtra, paddingRight: base + textExtra };
84
77
  }
85
78
 
86
79
  // Icon + text: extra padding on text side only
87
80
  if (hasText && hasIcon) {
88
81
  if (iconPosition === 'left') {
89
- return {
90
- paddingLeft: base,
91
- paddingRight: base + textExtra,
92
- paddingTop: base,
93
- paddingBottom: base,
94
- };
82
+ return { paddingLeft: base, paddingRight: base + textExtra };
95
83
  } else {
96
- return {
97
- paddingLeft: base + textExtra,
98
- paddingRight: base,
99
- paddingTop: base,
100
- paddingBottom: base,
101
- };
84
+ return { paddingLeft: base + textExtra, paddingRight: base };
102
85
  }
103
86
  }
104
87
 
105
- // Fallback: symmetric base padding
106
- return {
107
- paddingLeft: base,
108
- paddingRight: base,
109
- paddingTop: base,
110
- paddingBottom: base,
111
- };
112
- }
113
-
114
- /**
115
- * Resolve the PaletteTokens for a given semantic.
116
- * Returns undefined for 'neutral' (handled separately).
117
- */
118
- function getPaletteTokens(semantic: ButtonSemantic, tokens: UseTokensResult): PaletteTokens | undefined {
119
- switch (semantic) {
120
- case 'accent': return tokens.accent;
121
- case 'success': return tokens.success;
122
- case 'error': return tokens.error;
123
- case 'warning': return tokens.warning;
124
- default: return undefined;
125
- }
88
+ return { paddingLeft: base, paddingRight: base };
126
89
  }
127
90
 
128
91
  /**
129
- * Compute button configuration based on variant, semantic, size, and state.
130
- *
131
- * This function ONLY computes variant colors + size tokens.
132
- * Layout concerns (flexbox, spacing, alignment) are handled by Wrapper primitive.
133
- * Typography concerns (font, size, weight) are handled by Text primitive.
134
- *
135
- * Elevation-aware: neutral primary uses backgroundInteractive for consistent contrast.
136
- * Semantic variants use proper PaletteTokens — no opacity hacks.
92
+ * Compute button configuration based on variant, size, and state.
137
93
  *
138
- * @param variant - Button type (primary, secondary, tertiary)
139
- * @param semantic - Button semantic meaning (neutral, accent, success, error, warning)
140
- * @param size - Button size (sm, md, lg)
141
- * @param disabled - Whether button is disabled
142
- * @param tokens - Resolved tokens for current elevation
143
- * @returns ButtonConfig with variantColors and sizeTokens
94
+ * Colors are derived from the inherited theme (via FrameContext) rather than
95
+ * hardcoded to the primary palette. The `theme` parameter comes from the
96
+ * nearest Frame's `theme` prop, defaulting to 'primary'.
144
97
  */
145
98
  export function getButtonConfig(
146
99
  variant: ButtonVariant,
147
- semantic: ButtonSemantic,
148
100
  size: ButtonSize,
149
101
  disabled: boolean,
150
- tokens: UseTokensResult
102
+ tokens: UseTokensResult,
103
+ theme: ThemeName,
151
104
  ): ButtonConfig {
152
- // Get size configuration
153
105
  const sizeConfig = getSizeConfig(size, tokens);
154
-
155
- // Get variant-specific colors
156
- const variantColors = getVariantColors(variant, semantic, disabled, tokens);
106
+ const variantColors = getVariantColors(variant, disabled, tokens, theme);
157
107
 
158
108
  return {
159
109
  variantColors,
160
110
  sizeTokens: {
111
+ height: sizeConfig.height,
161
112
  padding: sizeConfig.padding,
162
113
  gap: sizeConfig.gap,
163
114
  borderRadius: sizeConfig.borderRadius,
@@ -168,11 +119,12 @@ export function getButtonConfig(
168
119
  }
169
120
 
170
121
  /**
171
- * Get size configuration with unified icon/text sizes across all sizes.
122
+ * Get size configuration. Heights are fixed per size.
172
123
  * Only padding and radius scale with size.
173
124
  */
174
125
  function getSizeConfig(size: ButtonSize, tokens: UseTokensResult) {
175
126
  const configs: Record<ButtonSize, {
127
+ height: number;
176
128
  padding: number;
177
129
  gap: number;
178
130
  borderRadius: number;
@@ -180,24 +132,35 @@ function getSizeConfig(size: ButtonSize, tokens: UseTokensResult) {
180
132
  iconSize: number;
181
133
  }> = {
182
134
  sm: {
183
- padding: 8,
135
+ height: BUTTON_HEIGHTS.sm,
136
+ padding: 12,
184
137
  gap: tokens.spacing['08'],
185
138
  borderRadius: 8,
186
- textSize: 'md', // 16px
187
- iconSize: 24,
139
+ textSize: 'md',
140
+ iconSize: 20,
188
141
  },
189
142
  md: {
190
- padding: 12,
143
+ height: BUTTON_HEIGHTS.md,
144
+ padding: 16,
191
145
  gap: tokens.spacing['08'],
192
146
  borderRadius: 12,
193
- textSize: 'md', // 16px
147
+ textSize: 'md',
194
148
  iconSize: 24,
195
149
  },
196
150
  lg: {
197
- padding: 16,
151
+ height: BUTTON_HEIGHTS.lg,
152
+ padding: 20,
153
+ gap: tokens.spacing['08'],
154
+ borderRadius: 14,
155
+ textSize: 'md',
156
+ iconSize: 24,
157
+ },
158
+ xl: {
159
+ height: BUTTON_HEIGHTS.xl,
160
+ padding: 24,
198
161
  gap: tokens.spacing['08'],
199
162
  borderRadius: 16,
200
- textSize: 'md', // 16px
163
+ textSize: 'lg',
201
164
  iconSize: 24,
202
165
  },
203
166
  };
@@ -206,134 +169,79 @@ function getSizeConfig(size: ButtonSize, tokens: UseTokensResult) {
206
169
  }
207
170
 
208
171
  /**
209
- * Get variant-specific colors from theme tokens.
172
+ * Get variant-specific colors from the inherited theme's tokens.
210
173
  * Handles disabled state override for all variants.
211
174
  */
212
175
  function getVariantColors(
213
176
  variant: ButtonVariant,
214
- semantic: ButtonSemantic,
215
177
  disabled: boolean,
216
- tokens: UseTokensResult
178
+ tokens: UseTokensResult,
179
+ theme: ThemeName,
217
180
  ) {
218
- // Disabled state overrides for all variants
181
+ const t = tokens.colors[theme];
182
+
219
183
  if (disabled) {
220
- const baseColors = getVariantColorsForState(variant, semantic, tokens);
221
- const { gamut } = tokens;
222
- const disabledBg = tokens.backgroundSunken[gamut];
184
+ const baseColors = getVariantColorsForState(variant, tokens, theme);
223
185
  return {
224
186
  ...baseColors,
225
- bg: disabledBg,
226
- hoveredBg: disabledBg,
227
- pressedBg: disabledBg,
228
- textColor: tokens.textSecondary[gamut],
229
- iconColor: tokens.textSecondary[gamut],
187
+ bg: 'transparent',
188
+ hoveredBg: 'transparent',
189
+ pressedBg: 'transparent',
190
+ textColor: t.main.fontDisabled,
191
+ iconColor: t.main.fontDisabled,
192
+ borderWidth: 1,
193
+ borderColor: t.main.divider,
230
194
  };
231
195
  }
232
196
 
233
- return getVariantColorsForState(variant, semantic, tokens);
197
+ return getVariantColorsForState(variant, tokens, theme);
234
198
  }
235
199
 
236
200
  /**
237
201
  * Get variant colors for non-disabled state.
238
- * Implements 3 types × 5 semantics = 15 combinations.
239
- * Uses proper PaletteTokens for all semantic variants — no opacity hacks.
202
+ * Uses the inherited theme's tokens no semantic prop needed.
240
203
  */
241
204
  function getVariantColorsForState(
242
205
  variant: ButtonVariant,
243
- semantic: ButtonSemantic,
244
- tokens: UseTokensResult
206
+ tokens: UseTokensResult,
207
+ theme: ThemeName,
245
208
  ) {
246
- const { gamut } = tokens;
247
- const paletteTokens = getPaletteTokens(semantic, tokens);
209
+ const t = tokens.colors[theme];
248
210
 
249
- // PRIMARY VARIANT: Filled background
211
+ // PRIMARY: Filled background using emphasis appearance
250
212
  if (variant === 'primary') {
251
- if (semantic === 'neutral') {
252
- // Neutral primary - uses backgroundInteractive tokens for consistent contrast across elevations
253
- return {
254
- bg: tokens.backgroundInteractive[gamut],
255
- hoveredBg: tokens.backgroundInteractiveHover[gamut],
256
- pressedBg: tokens.backgroundInteractiveActive[gamut],
257
- textColor: tokens.textPrimary[gamut],
258
- iconColor: tokens.textPrimary[gamut],
259
- borderWidth: 1,
260
- borderColor: 'transparent',
261
- };
262
- }
263
-
264
- // Semantic primary (accent, success, error, warning) — uses palette fill tokens
265
213
  return {
266
- bg: paletteTokens!.fill[gamut],
267
- hoveredBg: paletteTokens!.fillHover[gamut],
268
- pressedBg: paletteTokens!.fillActive[gamut],
269
- textColor: paletteTokens!.onFill[gamut],
270
- iconColor: paletteTokens!.onFill[gamut],
271
- borderWidth: 1,
214
+ bg: t.emphasis.actionEnabled,
215
+ hoveredBg: t.emphasis.actionHovered,
216
+ pressedBg: t.emphasis.actionPressed,
217
+ textColor: t.emphasis.accentSmall,
218
+ iconColor: t.emphasis.accentSmall,
219
+ borderWidth: 0,
272
220
  borderColor: 'transparent',
273
221
  };
274
222
  }
275
223
 
276
- // SECONDARY VARIANT: Outlined (border + subtle background for non-neutral)
224
+ // SECONDARY: Outlined with subtle hover
277
225
  if (variant === 'secondary') {
278
- if (semantic === 'neutral') {
279
- // Shifted action scale: transparent → 01 (hover) → 02 (pressed)
280
- return {
281
- bg: 'transparent',
282
- hoveredBg: tokens.backgroundInteractive[gamut],
283
- pressedBg: tokens.backgroundInteractiveHover[gamut],
284
- textColor: tokens.textPrimary[gamut],
285
- iconColor: tokens.textPrimary[gamut],
286
- borderWidth: 1,
287
- borderColor: tokens.border[gamut],
288
- };
289
- }
290
-
291
- // Semantic secondary — uses palette surface tokens for subtle bg
292
226
  return {
293
- bg: paletteTokens!.background[gamut],
294
- hoveredBg: paletteTokens!.backgroundInteractive[gamut],
295
- pressedBg: paletteTokens!.backgroundInteractiveHover[gamut],
296
- textColor: paletteTokens!.fill[gamut],
297
- iconColor: paletteTokens!.fill[gamut],
298
- borderWidth: 1,
299
- borderColor: 'transparent',
300
- };
301
- }
302
-
303
- // TERTIARY VARIANT: Ghost (text-only, no visible border)
304
- if (variant === 'tertiary') {
305
- if (semantic === 'neutral') {
306
- // Shifted action scale: transparent → 01 (hover) → 02 (pressed)
307
- return {
308
- bg: 'transparent',
309
- hoveredBg: tokens.backgroundInteractive[gamut],
310
- pressedBg: tokens.backgroundInteractiveHover[gamut],
311
- textColor: tokens.textPrimary[gamut],
312
- iconColor: tokens.textPrimary[gamut],
313
- borderWidth: 1,
314
- borderColor: 'transparent',
315
- };
316
- }
317
-
318
- // Semantic tertiary — uses palette surface tokens for hover/pressed
319
- return {
320
- bg: 'transparent',
321
- hoveredBg: paletteTokens!.background[gamut],
322
- pressedBg: paletteTokens!.backgroundInteractive[gamut],
323
- textColor: paletteTokens!.fill[gamut],
324
- iconColor: paletteTokens!.fill[gamut],
325
- borderWidth: 1,
227
+ bg: t.main.actionEnabled,
228
+ hoveredBg: t.main.actionHovered,
229
+ pressedBg: t.main.actionPressed,
230
+ textColor: t.main.fontPrimary,
231
+ iconColor: t.main.fontPrimary,
232
+ borderWidth: 0,
326
233
  borderColor: 'transparent',
327
234
  };
328
235
  }
329
236
 
330
- // Fallback (should never reach here with proper types)
237
+ // GHOST: No border, transparent background, subtle hover
331
238
  return {
332
239
  bg: 'transparent',
333
- hoveredBg: 'transparent',
334
- pressedBg: 'transparent',
335
- textColor: tokens.textPrimary[gamut],
336
- iconColor: tokens.textPrimary[gamut],
240
+ hoveredBg: t.main.actionEnabled,
241
+ pressedBg: t.main.actionHovered,
242
+ textColor: t.main.fontSecondary,
243
+ iconColor: t.main.fontSecondary,
337
244
  borderWidth: 0,
245
+ borderColor: 'transparent',
338
246
  };
339
247
  }
@@ -3,41 +3,39 @@ import { Pressable } from 'react-native';
3
3
  import type { ButtonProps } from './Button.types';
4
4
  import { getButtonConfig, computeButtonPadding } from './Button.styles';
5
5
  import { useTokens } from 'newtone-api';
6
+ import { useFrameContext } from 'newtone-api';
6
7
  import { Icon } from '../../../primitives/Icon/Icon';
7
8
  import { Text } from '../../../primitives/Text';
8
9
  import { Wrapper } from '../../../primitives/Wrapper/Wrapper';
9
10
 
10
11
  /**
11
- * Button — A composite component demonstrating the primitive composition pattern.
12
+ * Button — A composite component that inherits its color theme from the
13
+ * nearest parent Frame's `theme` prop (defaults to 'primary').
12
14
  *
13
- * **Composition Architecture:**
14
- * - Pressable (RN primitive) handles interaction
15
- * - Wrapper (primitive) handles layout (direction, gap, padding, alignment)
16
- * - Icon (primitive) handles icon rendering with theme tokens
17
- * - Text (primitive) — handles typography with theme tokens
15
+ * **Variants:**
16
+ * - `primary` Filled background (highest visual weight)
17
+ * - `secondary`Outlined with subtle hover
18
+ * - `ghost`No border, transparent, subtle hover
18
19
  *
19
- * This component does NOT manually compute flexbox, spacing, or typography styles.
20
- * Instead, it delegates to primitives which already handle these concerns.
21
- *
22
- * Automatically adapts to the current theme and mode from NewtoneProvider.
20
+ * **Sizes (fixed heights):**
21
+ * - `sm` 40px
22
+ * - `md` — 48px
23
+ * - `lg` 56px
24
+ * - `xl` — 64px
23
25
  *
24
26
  * @example
25
27
  * ```tsx
26
- * <Button variant="primary" semantic="accent" size="md" onPress={() => console.log('Pressed')}>
28
+ * <Button variant="primary" size="md" onPress={() => console.log('Pressed')}>
27
29
  * Click me
28
30
  * </Button>
29
31
  * ```
30
32
  *
31
33
  * @example
32
34
  * ```tsx
33
- * <Button icon="add" variant="primary" semantic="accent" onPress={handleAdd}>
34
- * New Item
35
- * </Button>
36
- * ```
37
- *
38
- * @example
39
- * ```tsx
40
- * <Button icon="delete" variant="tertiary" semantic="neutral" size="sm" onPress={handleDelete} />
35
+ * // Inherits theme from parent Frame
36
+ * <Frame theme="error">
37
+ * <Button variant="primary">Delete</Button>
38
+ * </Frame>
41
39
  * ```
42
40
  */
43
41
  export function Button({
@@ -45,33 +43,37 @@ export function Button({
45
43
  icon,
46
44
  iconPosition = 'left',
47
45
  variant = 'primary',
48
- semantic = 'neutral',
49
46
  size = 'md',
50
47
  disabled = false,
48
+ loading = false,
49
+ fullWidth = false,
51
50
  style,
52
51
  textStyle,
53
52
  ...pressableProps
54
53
  }: ButtonProps) {
55
- // Read tokens from current elevation context
56
- // backgroundInteractive provides consistent contrast across all elevations
57
54
  const tokens = useTokens();
55
+ const frameCtx = useFrameContext();
56
+
57
+ // Inherit theme from nearest Frame, default to 'primary'
58
+ const theme = frameCtx?.theme ?? 'primary';
59
+
60
+ const isDisabled = disabled || loading;
58
61
 
59
- // Get color tokens + size config (now using backgroundInteractive for consistent contrast)
60
62
  const { variantColors, sizeTokens } = React.useMemo(
61
- () => getButtonConfig(variant, semantic, size, disabled, tokens),
62
- [variant, semantic, size, disabled, tokens]
63
+ () => getButtonConfig(variant, size, isDisabled, tokens, theme),
64
+ [variant, size, isDisabled, tokens, theme]
63
65
  );
64
66
 
65
67
  // Compute asymmetric padding (+8px on text side for optical balance)
68
+ // Ghost variant with no icon uses standard padding
66
69
  const padding = React.useMemo(
67
70
  () => computeButtonPadding(size, !!icon, !!children, iconPosition),
68
71
  [size, icon, children, iconPosition]
69
72
  );
70
73
 
71
74
  return (
72
- <Pressable disabled={disabled} {...pressableProps}>
75
+ <Pressable disabled={isDisabled} {...pressableProps}>
73
76
  {({ pressed, hovered }: { pressed: boolean; hovered?: boolean }) => (
74
- // Wrapper handles layout: direction, gap, alignment (padding via style)
75
77
  <Wrapper
76
78
  direction="horizontal"
77
79
  align="center"
@@ -79,16 +81,19 @@ export function Button({
79
81
  gap={sizeTokens.gap}
80
82
  style={[
81
83
  {
82
- ...padding, // Asymmetric horizontal padding for text optical balance
83
- backgroundColor: pressed && !disabled
84
+ height: sizeTokens.height,
85
+ paddingLeft: padding.paddingLeft,
86
+ paddingRight: padding.paddingRight,
87
+ backgroundColor: pressed && !isDisabled
84
88
  ? variantColors.pressedBg
85
- : hovered && !disabled
89
+ : hovered && !isDisabled
86
90
  ? variantColors.hoveredBg
87
91
  : variantColors.bg,
88
92
  borderRadius: sizeTokens.borderRadius,
89
- borderWidth: variantColors.borderWidth, // Always 1 for consistent sizing
93
+ borderWidth: variantColors.borderWidth,
90
94
  borderColor: variantColors.borderColor || 'transparent',
91
- opacity: disabled ? 0.4 : 1,
95
+ opacity: isDisabled ? (loading ? 0.6 : 0.4) : 1,
96
+ ...(fullWidth && { width: '100%' as any, alignSelf: 'stretch' as any }),
92
97
  },
93
98
  ...(Array.isArray(style) ? style : style ? [style] : []),
94
99
  ]}
@@ -97,7 +102,6 @@ export function Button({
97
102
  <Icon name={icon} size={sizeTokens.iconSize} color={variantColors.iconColor} />
98
103
  )}
99
104
  {children && (
100
- // Text primitive with semantic props + color style override
101
105
  <Text
102
106
  role="label"
103
107
  size={sizeTokens.textSize}
@@ -1,19 +1,14 @@
1
1
  import type { PressableProps, ViewStyle, TextStyle } from 'react-native';
2
2
 
3
3
  /**
4
- * Visual type for the Button component (describes visual weight)
4
+ * Visual variant for the Button component (describes visual weight)
5
5
  */
6
- export type ButtonVariant = 'primary' | 'secondary' | 'tertiary';
7
-
8
- /**
9
- * Semantic meaning for the Button component (describes color purpose)
10
- */
11
- export type ButtonSemantic = 'neutral' | 'accent' | 'success' | 'error' | 'warning';
6
+ export type ButtonVariant = 'primary' | 'secondary' | 'ghost';
12
7
 
13
8
  /**
14
9
  * Size presets for the Button component
15
10
  */
16
- export type ButtonSize = 'sm' | 'md' | 'lg';
11
+ export type ButtonSize = 'sm' | 'md' | 'lg' | 'xl';
17
12
 
18
13
  /**
19
14
  * Icon position relative to button text
@@ -43,17 +38,11 @@ export interface ButtonProps extends Omit<PressableProps, 'children' | 'style'>
43
38
  readonly iconPosition?: ButtonIconPosition;
44
39
 
45
40
  /**
46
- * Visual type (describes visual weight: filled, outlined, ghost)
41
+ * Visual variant (describes visual weight: filled, outlined, ghost)
47
42
  * @default 'primary'
48
43
  */
49
44
  readonly variant?: ButtonVariant;
50
45
 
51
- /**
52
- * Semantic meaning (describes color purpose: neutral, accent, success, error, warning)
53
- * @default 'neutral'
54
- */
55
- readonly semantic?: ButtonSemantic;
56
-
57
46
  /**
58
47
  * Size preset
59
48
  * @default 'md'
@@ -66,6 +55,18 @@ export interface ButtonProps extends Omit<PressableProps, 'children' | 'style'>
66
55
  */
67
56
  readonly disabled?: boolean;
68
57
 
58
+ /**
59
+ * Loading state — disables interaction and dims content.
60
+ * @default false
61
+ */
62
+ readonly loading?: boolean;
63
+
64
+ /**
65
+ * Full-width mode — stretches button to fill container width.
66
+ * @default false
67
+ */
68
+ readonly fullWidth?: boolean;
69
+
69
70
  /**
70
71
  * Custom style overrides for container
71
72
  */
@@ -1,2 +1,2 @@
1
1
  export { Button } from './Button';
2
- export type { ButtonProps, ButtonVariant, ButtonSize } from './Button.types';
2
+ export type { ButtonProps, ButtonVariant, ButtonSize, ButtonIconPosition } from './Button.types';
@@ -0,0 +1,29 @@
1
+ import React from 'react';
2
+ import type { LogoMonogramProps } from './LogoMonogram.types';
3
+
4
+ /**
5
+ * Newtone monogram — the "N" symbol mark rendered as an SVG.
6
+ *
7
+ * The fill color is derived from `colorValue` (0 = black, 255 = white),
8
+ * allowing smooth animated transitions between light and dark contexts.
9
+ */
10
+ export function LogoMonogram({ colorValue = 0, size = 32 }: LogoMonogramProps) {
11
+ // Convert the 0–255 grayscale value to an rgb() string.
12
+ const fg = `rgb(${colorValue}, ${colorValue}, ${colorValue})`;
13
+
14
+ return (
15
+ <svg
16
+ width={size}
17
+ height={size}
18
+ viewBox="0 0 168 168"
19
+ fill="none"
20
+ xmlns="http://www.w3.org/2000/svg"
21
+ >
22
+ <path d="M39.3574 70H12L20 84H36L30.2681 94.0309C28.8627 96.4903 28.8627 99.5096 30.2681 101.969L36 112L49.7319 87.9691C51.1373 85.5097 51.1373 82.4903 49.7319 80.0309L46.3034 74.0309C44.879 71.5383 42.2283 70 39.3574 70Z" fill={fg} />
23
+ <path d="M84 112H36L41.6966 121.969C43.121 124.462 45.7717 126 48.6426 126H79.3574C82.2283 126 84.879 124.462 86.3034 121.969L93.7319 108.969C95.1373 106.51 95.1373 103.49 93.7319 101.031L84 84L76 98L84 112Z" fill={fg} />
24
+ <path d="M76 70L84 84H64.6426C61.7717 84 59.121 82.4617 57.6966 79.9691L50.268 66.9691C48.8626 64.5097 48.8626 61.4903 50.268 59.0309L65.6966 32.0309C67.121 29.5383 69.7717 28 72.6426 28H84L60 70H76Z" fill={fg} />
25
+ <path d="M95.3574 28H84L132 112L137.732 101.969C139.137 99.5097 139.137 96.4903 137.732 94.0309L102.303 32.0309C100.879 29.5383 98.2283 28 95.3574 28Z" fill={fg} />
26
+ <path d="M104.643 112H132L126.303 121.969C124.879 124.462 122.228 126 119.357 126H108L116 140L108 154L94.268 129.969C92.8626 127.51 92.8626 124.49 94.268 122.031L97.6966 116.031C99.121 113.538 101.772 112 104.643 112Z" fill={fg} />
27
+ </svg>
28
+ );
29
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Props for LogoMonogram — the Newtone "N" symbol mark.
3
+ *
4
+ * Renders a single-color SVG monogram that scales to the given size.
5
+ * The `colorValue` prop controls the grayscale fill, enabling smooth
6
+ * transitions between light and dark backgrounds.
7
+ *
8
+ * @example
9
+ * ```tsx
10
+ * <LogoMonogram colorValue={0} />
11
+ * ```
12
+ *
13
+ * @example
14
+ * ```tsx
15
+ * <LogoMonogram colorValue={255} size={48} />
16
+ * ```
17
+ */
18
+ export interface LogoMonogramProps {
19
+ /**
20
+ * Grayscale fill value (0–255).
21
+ *
22
+ * - `0` produces black (for light backgrounds)
23
+ * - `255` produces white (for dark backgrounds)
24
+ *
25
+ * @default 0
26
+ */
27
+ readonly colorValue?: number;
28
+
29
+ /**
30
+ * Width and height of the SVG in pixels.
31
+ *
32
+ * @default 32
33
+ */
34
+ readonly size?: number;
35
+ }
@@ -0,0 +1,2 @@
1
+ export { LogoMonogram } from './LogoMonogram';
2
+ export type { LogoMonogramProps } from './LogoMonogram.types';