@newtonedev/components 0.1.7 → 0.1.8

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 (94) hide show
  1. package/dist/composites/actions/Button/Button.d.ts.map +1 -1
  2. package/dist/composites/form-controls/Select/Select.styles.d.ts.map +1 -1
  3. package/dist/composites/form-controls/TextInput/TextInput.styles.d.ts.map +1 -1
  4. package/dist/composites/form-controls/Toggle/Toggle.styles.d.ts.map +1 -1
  5. package/dist/composites/range-inputs/ColorScaleSlider/ColorScaleSlider.styles.d.ts.map +1 -1
  6. package/dist/composites/range-inputs/HueSlider/HueSlider.styles.d.ts.map +1 -1
  7. package/dist/composites/range-inputs/Slider/Slider.styles.d.ts.map +1 -1
  8. package/dist/fonts/GoogleFontLoader.d.ts +5 -4
  9. package/dist/fonts/GoogleFontLoader.d.ts.map +1 -1
  10. package/dist/fonts/SelfHostedFontLoader.d.ts +14 -0
  11. package/dist/fonts/SelfHostedFontLoader.d.ts.map +1 -0
  12. package/dist/fonts/buildGoogleFontsUrl.d.ts +1 -16
  13. package/dist/fonts/buildGoogleFontsUrl.d.ts.map +1 -1
  14. package/dist/fonts/measureFont.d.ts +18 -0
  15. package/dist/fonts/measureFont.d.ts.map +1 -0
  16. package/dist/fonts/reportQueue.d.ts +7 -0
  17. package/dist/fonts/reportQueue.d.ts.map +1 -0
  18. package/dist/fonts/useLocalCalibration.d.ts +19 -0
  19. package/dist/fonts/useLocalCalibration.d.ts.map +1 -0
  20. package/dist/fonts/useTypographyCalibrations.d.ts +11 -0
  21. package/dist/fonts/useTypographyCalibrations.d.ts.map +1 -0
  22. package/dist/index.cjs +628 -422
  23. package/dist/index.cjs.map +1 -1
  24. package/dist/index.d.ts +7 -6
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +567 -376
  27. package/dist/index.js.map +1 -1
  28. package/dist/primitives/Icon/Icon.types.d.ts +1 -1
  29. package/dist/primitives/Icon/Icon.types.d.ts.map +1 -1
  30. package/dist/primitives/Text/Text.d.ts +33 -8
  31. package/dist/primitives/Text/Text.d.ts.map +1 -1
  32. package/dist/primitives/Text/Text.spans.d.ts +22 -0
  33. package/dist/primitives/Text/Text.spans.d.ts.map +1 -0
  34. package/dist/primitives/Text/Text.types.d.ts +75 -27
  35. package/dist/primitives/Text/Text.types.d.ts.map +1 -1
  36. package/dist/primitives/Text/index.d.ts +23 -2
  37. package/dist/primitives/Text/index.d.ts.map +1 -1
  38. package/dist/primitives/index.d.ts +1 -1
  39. package/dist/primitives/index.d.ts.map +1 -1
  40. package/dist/registry/codegen.d.ts.map +1 -1
  41. package/dist/registry/registry.d.ts.map +1 -1
  42. package/dist/registry/types.d.ts +2 -0
  43. package/dist/registry/types.d.ts.map +1 -1
  44. package/dist/theme/NewtoneProvider.d.ts +9 -1
  45. package/dist/theme/NewtoneProvider.d.ts.map +1 -1
  46. package/dist/theme/defaults.d.ts +1 -0
  47. package/dist/theme/defaults.d.ts.map +1 -1
  48. package/dist/theme/types.d.ts +48 -32
  49. package/dist/theme/types.d.ts.map +1 -1
  50. package/dist/theme/useBreakpoint.d.ts +9 -0
  51. package/dist/theme/useBreakpoint.d.ts.map +1 -0
  52. package/dist/tokens/computeTokens.d.ts +9 -22
  53. package/dist/tokens/computeTokens.d.ts.map +1 -1
  54. package/dist/tokens/types.d.ts +40 -22
  55. package/dist/tokens/types.d.ts.map +1 -1
  56. package/package.json +2 -1
  57. package/src/composites/actions/Button/Button.styles.ts +3 -3
  58. package/src/composites/actions/Button/Button.tsx +3 -2
  59. package/src/composites/form-controls/Select/Select.styles.ts +8 -8
  60. package/src/composites/form-controls/Select/Select.tsx +1 -1
  61. package/src/composites/form-controls/Select/SelectOption.tsx +3 -3
  62. package/src/composites/form-controls/TextInput/TextInput.styles.ts +5 -5
  63. package/src/composites/form-controls/Toggle/Toggle.styles.ts +3 -3
  64. package/src/composites/range-inputs/ColorScaleSlider/ColorScaleSlider.styles.ts +6 -6
  65. package/src/composites/range-inputs/HueSlider/HueSlider.styles.ts +9 -9
  66. package/src/composites/range-inputs/Slider/Slider.styles.ts +9 -9
  67. package/src/fonts/GoogleFontLoader.tsx +25 -10
  68. package/src/fonts/SelfHostedFontLoader.tsx +44 -0
  69. package/src/fonts/buildGoogleFontsUrl.ts +2 -31
  70. package/src/fonts/measureFont.ts +42 -0
  71. package/src/fonts/reportQueue.ts +54 -0
  72. package/src/fonts/useLocalCalibration.ts +97 -0
  73. package/src/fonts/useTypographyCalibrations.ts +15 -0
  74. package/src/index.ts +16 -7
  75. package/src/primitives/Frame/Frame.tsx +3 -3
  76. package/src/primitives/Icon/Icon.tsx +1 -1
  77. package/src/primitives/Icon/Icon.types.ts +1 -1
  78. package/src/primitives/Text/Text.spans.ts +57 -0
  79. package/src/primitives/Text/Text.tsx +205 -53
  80. package/src/primitives/Text/Text.types.ts +80 -27
  81. package/src/primitives/Text/index.ts +27 -3
  82. package/src/primitives/index.ts +3 -2
  83. package/src/registry/codegen.ts +1 -0
  84. package/src/registry/registry.ts +55 -53
  85. package/src/registry/types.ts +2 -0
  86. package/src/theme/NewtoneProvider.tsx +18 -2
  87. package/src/theme/defaults.ts +8 -28
  88. package/src/theme/types.ts +63 -33
  89. package/src/theme/useBreakpoint.ts +14 -0
  90. package/src/tokens/computeTokens.ts +23 -19
  91. package/src/tokens/types.ts +10 -24
  92. package/dist/fonts/googleFonts.d.ts +0 -20
  93. package/dist/fonts/googleFonts.d.ts.map +0 -1
  94. package/src/fonts/googleFonts.ts +0 -87
@@ -1,20 +1,25 @@
1
1
  import type { Text as RNText, TextStyle } from 'react-native';
2
2
  import type { ElevationLevel } from '../../theme/types';
3
3
 
4
- /** Typography size tokensmaps to `tokens.typography.size.*` pixel values. */
5
- export type TextSize = 'xs' | 'sm' | 'base' | 'md' | 'lg' | 'xl' | 'xxl';
4
+ /** Typography size stepselects within the current role's 3-step scale. @default 'md' */
5
+ export type TextSize = 'sm' | 'md' | 'lg';
6
6
 
7
- /** Typography weight tokens — maps to `tokens.typography.weight.*` numeric values. */
8
- export type TextWeight = 'regular' | 'medium' | 'semibold' | 'bold';
7
+ /** Font weight slots — maps to per-scope `tokens.typography.fonts[scope].weights.*` values. */
8
+ export type TextWeight = 'regular' | 'medium' | 'bold';
9
9
 
10
10
  /** Semantic text color tokens — resolved from the current theme's token palette. */
11
11
  export type TextColor = 'primary' | 'secondary' | 'tertiary' | 'disabled' | 'accent' | 'success' | 'warning' | 'error';
12
12
 
13
- /** Font family tokens maps to `tokens.typography.fonts.*` font stacks. */
14
- export type TextFont = 'default' | 'display' | 'mono';
13
+ /** Font scopeselects which font family slot to use. @default 'main' */
14
+ export type TextScope = 'main' | 'display' | 'mono' | 'currency';
15
15
 
16
- /** Line height multiplier tokens — maps to `tokens.typography.lineHeight.*` values. */
17
- export type TextLineHeight = 'tight' | 'normal' | 'relaxed';
16
+ /**
17
+ * Semantic text role determines default size, weight, and line height.
18
+ * Provides purpose-driven typography presets. Individual size/weight/lineHeight
19
+ * props can override the role's defaults when needed (e.g., in composites).
20
+ * @default 'body'
21
+ */
22
+ export type TextRole = 'headline' | 'title' | 'heading' | 'subheading' | 'body' | 'label' | 'caption';
18
23
 
19
24
  /** Text alignment. */
20
25
  export type TextAlign = 'left' | 'center' | 'right';
@@ -22,38 +27,37 @@ export type TextAlign = 'left' | 'center' | 'right';
22
27
  /**
23
28
  * Props for Text — themed typography component.
24
29
  *
25
- * All visual properties are token-driven: size, weight, color, font,
26
- * and line height resolve from the current theme context. Use semantic
27
- * tokens rather than raw values for consistency.
30
+ * Primary API surface: `scope` (font family) + `role` (semantic preset) + `color`.
31
+ * Individual size/weight/lineHeight props are available as overrides for composites.
28
32
  *
29
33
  * @example
30
34
  * ```tsx
31
35
  * <Text>Default body text</Text>
32
- * <Text size="lg" weight="bold">Heading</Text>
33
- * <Text color="secondary" size="sm">Caption text</Text>
34
- * <Text font="mono" size="sm">const x = 42;</Text>
35
- * <Text numberOfLines={2}>Long text that truncates after two lines...</Text>
36
+ * <Text role="headline" scope="display">Hero title</Text>
37
+ * <Text role="label">Form label</Text>
38
+ * <Text scope="mono" role="caption">const x = 42;</Text>
39
+ * <Text role="body">Regular text with <Text.Bold>bold emphasis</Text.Bold></Text>
36
40
  * ```
37
41
  */
38
42
  export interface TextProps {
39
43
  /**
40
- * Text content. Accepts strings, numbers, and nested `<Text>` elements
41
- * for inline formatting (e.g., bold spans within a paragraph).
44
+ * Text content. Accepts strings, numbers, and span sub-components
45
+ * for inline formatting (e.g., `<Text.Bold>`, `<Text.Italic>`).
42
46
  *
43
47
  * @example
44
48
  * ```tsx
45
49
  * <Text>Plain string</Text>
46
50
  * <Text>Count: {42}</Text>
47
- * <Text>Normal <Text weight="bold">bold</Text> normal</Text>
51
+ * <Text role="body">Normal <Text.Bold>bold</Text.Bold> normal</Text>
48
52
  * ```
49
53
  */
50
54
  readonly children: React.ReactNode;
51
55
 
52
- /** Typography size token. @default 'base' */
53
- readonly size?: TextSize;
56
+ /** Font scope — selects which font family to use. @default 'main' */
57
+ readonly scope?: TextScope;
54
58
 
55
- /** Font weight token. @default 'regular' */
56
- readonly weight?: TextWeight;
59
+ /** Semantic role — sets default size/weight/lineHeight. @default 'body' */
60
+ readonly role?: TextRole;
57
61
 
58
62
  /**
59
63
  * Semantic color token.
@@ -71,11 +75,10 @@ export interface TextProps {
71
75
  */
72
76
  readonly color?: TextColor;
73
77
 
74
- /** Font family token. @default 'default' */
75
- readonly font?: TextFont;
78
+ // ── Overrides (used by composites, not exposed in editor UI) ──
76
79
 
77
- /** Line height multiplier token. @default 'normal' */
78
- readonly lineHeight?: TextLineHeight;
80
+ /** Override the role's default size. */
81
+ readonly size?: TextSize;
79
82
 
80
83
  /** Text alignment. */
81
84
  readonly align?: TextAlign;
@@ -92,7 +95,8 @@ export interface TextProps {
92
95
  // ── Accessibility ──
93
96
 
94
97
  /**
95
- * Semantic role for screen readers.
98
+ * Semantic role for screen readers. Auto-inferred from typography role
99
+ * (headline/title/heading → 'header') unless explicitly set.
96
100
  *
97
101
  * - `'header'` — Marks this as a heading (e.g. page title, section header)
98
102
  * - `'text'` — Default text role (usually omitted)
@@ -110,4 +114,53 @@ export interface TextProps {
110
114
 
111
115
  /** Ref to the underlying React Native Text element. */
112
116
  readonly ref?: React.Ref<RNText>;
117
+
118
+ /**
119
+ * Enable responsive font sizing for adaptive roles (headline, title, heading, subheading).
120
+ * When true, the Text component measures its container width and uses `resolveResponsiveSize`
121
+ * to pick the best fontSize within the role's natural size, improving line-break quality.
122
+ * Requires the text to be wrapped in a container that defines a width.
123
+ * No-op for non-adaptive roles (body, label, caption).
124
+ * @default false
125
+ */
126
+ readonly responsive?: boolean;
127
+
128
+ /**
129
+ * Apply vertical centering correction to compensate for font metrics.
130
+ * Shifts text down by the font's verticalCenterOffset so that cap-height
131
+ * text appears visually centered within flex containers (buttons, chips).
132
+ * No-op for multi-line body text — only enable in single-line centering contexts.
133
+ * Requires fontMetrics in the theme config; ignored when metrics are unavailable.
134
+ * @default false
135
+ */
136
+ readonly centerVertically?: boolean;
137
+
138
+ /**
139
+ * OpenType feature tags to enable (e.g., `['tnum', 'kern']`).
140
+ * When fontMetrics are available, requested tags are filtered against the font's
141
+ * supported features. When metrics are unavailable, all tags are applied optimistically.
142
+ * Maps to the CSS `font-feature-settings` property via react-native-web.
143
+ */
144
+ readonly features?: readonly string[];
145
+ }
146
+
147
+ /**
148
+ * Constrained props for inline span sub-components (Text.Bold, Text.Italic, etc.).
149
+ * Spans inherit font family, size, and line height from the parent Text.
150
+ * Only inline formatting properties are exposed.
151
+ */
152
+ export interface TextSpanProps {
153
+ readonly children: React.ReactNode;
154
+ /** Override text color within the span. */
155
+ readonly color?: TextColor;
156
+ /** Override font weight within the span. */
157
+ readonly weight?: TextWeight;
158
+ /** Apply italic style. */
159
+ readonly italic?: boolean;
160
+ /** Apply underline decoration. */
161
+ readonly underline?: boolean;
162
+ /** Apply background highlight using a semantic color token. */
163
+ readonly highlight?: TextColor;
164
+ /** Style overrides (applied last). */
165
+ readonly style?: TextStyle | readonly TextStyle[];
113
166
  }
@@ -1,10 +1,34 @@
1
- export { Text } from './Text';
1
+ import { TextBase } from './Text';
2
+ import { TextSpan, TextBold, TextMedium, TextItalic, TextUnderline, TextHighlight } from './Text.spans';
3
+
4
+ /**
5
+ * Token-aware text primitive with span sub-components.
6
+ *
7
+ * Primary API: `scope` (font family) + `role` (semantic preset) + `color`.
8
+ *
9
+ * @example
10
+ * ```tsx
11
+ * <Text role="headline" scope="display">Hero</Text>
12
+ * <Text role="body">Regular <Text.Bold>bold</Text.Bold> text</Text>
13
+ * <Text role="label" color="secondary">Field label</Text>
14
+ * ```
15
+ */
16
+ export const Text = Object.assign(TextBase, {
17
+ Span: TextSpan,
18
+ Bold: TextBold,
19
+ Medium: TextMedium,
20
+ Italic: TextItalic,
21
+ Underline: TextUnderline,
22
+ Highlight: TextHighlight,
23
+ });
24
+
2
25
  export type {
3
26
  TextProps,
4
27
  TextSize,
5
28
  TextWeight,
6
29
  TextColor,
7
- TextFont,
8
- TextLineHeight,
30
+ TextScope,
31
+ TextRole,
9
32
  TextAlign,
33
+ TextSpanProps,
10
34
  } from './Text.types';
@@ -40,7 +40,8 @@ export type {
40
40
  TextSize,
41
41
  TextWeight,
42
42
  TextColor,
43
- TextFont,
44
- TextLineHeight,
43
+ TextScope,
44
+ TextRole,
45
+ TextSpanProps,
45
46
  TextAlign,
46
47
  } from './Text';
@@ -18,6 +18,7 @@ const HANDLER_PROPS: Record<string, readonly string[]> = {
18
18
  */
19
19
  const CHILDREN_CONTENT: Record<string, string> = {
20
20
  button: 'Button',
21
+ text: 'The quick brown fox',
21
22
  card: '{/* content */}',
22
23
  frame: '{/* content */}',
23
24
  };
@@ -1,12 +1,11 @@
1
1
  import type { CategoryMeta, ComponentMeta } from './types';
2
2
 
3
3
  export const CATEGORIES: readonly CategoryMeta[] = [
4
- { id: 'primitives', name: 'Design Primitives', description: 'Core building blocks for theme-aware layouts and typography' },
5
- { id: 'actions', name: 'Actions', description: 'Interactive elements that trigger actions' },
6
- { id: 'form-controls', name: 'Form Controls', description: 'Input elements for user data entry' },
7
- { id: 'range-inputs', name: 'Range Inputs', description: 'Slider controls for numeric and continuous values' },
8
- { id: 'layout', name: 'Layout', description: 'Structural and container components' },
9
- { id: 'overlays', name: 'Overlays', description: 'Floating and portal-based UI elements' },
4
+ { id: 'colors', name: 'Colors', description: 'Color palettes and token visualization', icon: 'palette' },
5
+ { id: 'typography', name: 'Typography', description: 'Text roles, scopes, and weight control', icon: 'text_fields' },
6
+ { id: 'symbols', name: 'Symbols', description: 'Material Symbols icon browser', icon: 'grid_view' },
7
+ { id: 'layout', name: 'Layout', description: 'Structural containers Frame and Wrapper', icon: 'grid_on' },
8
+ { id: 'components', name: 'Components', description: 'Interactive UI components', icon: 'widgets' },
10
9
  ];
11
10
 
12
11
  export const COMPONENTS: readonly ComponentMeta[] = [
@@ -14,7 +13,7 @@ export const COMPONENTS: readonly ComponentMeta[] = [
14
13
  id: 'button',
15
14
  name: 'Button',
16
15
  importName: 'Button',
17
- categoryId: 'actions',
16
+ categoryId: 'components',
18
17
  description: 'Interactive button with multiple variants, sizes, and optional icon',
19
18
  hasChildren: true,
20
19
  variants: [
@@ -96,7 +95,7 @@ export const COMPONENTS: readonly ComponentMeta[] = [
96
95
  id: 'text-input',
97
96
  name: 'TextInput',
98
97
  importName: 'TextInput',
99
- categoryId: 'form-controls',
98
+ categoryId: 'components',
100
99
  description: 'Text input field with label support',
101
100
  hasChildren: false,
102
101
  variants: [
@@ -123,7 +122,7 @@ export const COMPONENTS: readonly ComponentMeta[] = [
123
122
  id: 'select',
124
123
  name: 'Select',
125
124
  importName: 'Select',
126
- categoryId: 'form-controls',
125
+ categoryId: 'components',
127
126
  description: 'Dropdown selector with options',
128
127
  hasChildren: false,
129
128
  variants: [
@@ -170,7 +169,7 @@ export const COMPONENTS: readonly ComponentMeta[] = [
170
169
  id: 'toggle',
171
170
  name: 'Toggle',
172
171
  importName: 'Toggle',
173
- categoryId: 'form-controls',
172
+ categoryId: 'components',
174
173
  description: 'Binary switch component',
175
174
  hasChildren: false,
176
175
  variants: [
@@ -197,7 +196,7 @@ export const COMPONENTS: readonly ComponentMeta[] = [
197
196
  id: 'slider',
198
197
  name: 'Slider',
199
198
  importName: 'Slider',
200
- categoryId: 'range-inputs',
199
+ categoryId: 'components',
201
200
  description: 'Numeric range slider',
202
201
  hasChildren: false,
203
202
  variants: [
@@ -241,7 +240,7 @@ export const COMPONENTS: readonly ComponentMeta[] = [
241
240
  id: 'hue-slider',
242
241
  name: 'HueSlider',
243
242
  importName: 'HueSlider',
244
- categoryId: 'range-inputs',
243
+ categoryId: 'components',
245
244
  description: 'Specialized slider for hue selection (0-360\u00b0)',
246
245
  hasChildren: false,
247
246
  variants: [
@@ -377,7 +376,7 @@ export const COMPONENTS: readonly ComponentMeta[] = [
377
376
  id: 'card',
378
377
  name: 'Card',
379
378
  importName: 'Card',
380
- categoryId: 'layout',
379
+ categoryId: 'components',
381
380
  description: 'Surface container with elevation levels',
382
381
  hasChildren: true,
383
382
  variants: [
@@ -410,45 +409,59 @@ export const COMPONENTS: readonly ComponentMeta[] = [
410
409
  id: 'text',
411
410
  name: 'Text',
412
411
  importName: 'Text',
413
- categoryId: 'primitives',
414
- description: 'Typography primitive with semantic size, weight, color, font, and lineHeight',
412
+ categoryId: 'typography',
413
+ description: 'Typography primitive with semantic scope (font family) and role (purpose)',
415
414
  hasChildren: true,
415
+ previewLayout: 'list',
416
416
  variants: [
417
- { id: 'default', label: 'Default', props: {} },
418
- { id: 'heading', label: 'Heading', props: { size: 'xl', weight: 'bold' } },
419
- { id: 'subheading', label: 'Subheading', props: { size: 'lg', weight: 'semibold' } },
420
- { id: 'body', label: 'Body', props: { size: 'base' } },
421
- { id: 'caption', label: 'Caption', props: { size: 'sm', color: 'secondary' } },
422
- { id: 'accent', label: 'Accent', props: { color: 'accent', weight: 'medium' } },
423
- { id: 'mono', label: 'Monospace', props: { font: 'mono', size: 'sm' } },
417
+ { id: 'body', label: 'Body', props: { role: 'body' } },
418
+ { id: 'headline', label: 'Headline', props: { role: 'headline', scope: 'display' } },
419
+ { id: 'title', label: 'Title', props: { role: 'title', scope: 'display' } },
420
+ { id: 'heading', label: 'Heading', props: { role: 'heading' } },
421
+ { id: 'subheading', label: 'Subheading', props: { role: 'subheading' } },
422
+ { id: 'label', label: 'Label', props: { role: 'label' } },
423
+ { id: 'caption', label: 'Caption', props: { role: 'caption', color: 'secondary' } },
424
+ { id: 'mono', label: 'Monospace', props: { scope: 'mono', role: 'body' } },
425
+ { id: 'currency', label: 'Currency', props: { scope: 'currency', role: 'body' } },
424
426
  ],
425
427
  editableProps: [
426
428
  {
427
- name: 'size',
428
- label: 'Size',
429
+ name: 'scope',
430
+ label: 'Scope',
429
431
  control: 'select',
430
432
  options: [
431
- { label: 'Extra Small', value: 'xs' },
432
- { label: 'Small', value: 'sm' },
433
- { label: 'Base', value: 'base' },
434
- { label: 'Medium', value: 'md' },
435
- { label: 'Large', value: 'lg' },
436
- { label: 'Extra Large', value: 'xl' },
437
- { label: 'XXL', value: 'xxl' },
433
+ { label: 'Main', value: 'main' },
434
+ { label: 'Display', value: 'display' },
435
+ { label: 'Mono', value: 'mono' },
436
+ { label: 'Currency', value: 'currency' },
438
437
  ],
439
- defaultValue: 'base',
438
+ defaultValue: 'main',
440
439
  },
441
440
  {
442
- name: 'weight',
443
- label: 'Weight',
441
+ name: 'role',
442
+ label: 'Role',
444
443
  control: 'select',
445
444
  options: [
446
- { label: 'Regular', value: 'regular' },
447
- { label: 'Medium', value: 'medium' },
448
- { label: 'Semibold', value: 'semibold' },
449
- { label: 'Bold', value: 'bold' },
445
+ { label: 'Headline', value: 'headline' },
446
+ { label: 'Title', value: 'title' },
447
+ { label: 'Heading', value: 'heading' },
448
+ { label: 'Subheading', value: 'subheading' },
449
+ { label: 'Body', value: 'body' },
450
+ { label: 'Label', value: 'label' },
451
+ { label: 'Caption', value: 'caption' },
450
452
  ],
451
- defaultValue: 'regular',
453
+ defaultValue: 'body',
454
+ },
455
+ {
456
+ name: 'size',
457
+ label: 'Size',
458
+ control: 'select',
459
+ options: [
460
+ { label: 'Small', value: 'sm' },
461
+ { label: 'Medium', value: 'md' },
462
+ { label: 'Large', value: 'lg' },
463
+ ],
464
+ defaultValue: 'md',
452
465
  },
453
466
  {
454
467
  name: 'color',
@@ -466,24 +479,13 @@ export const COMPONENTS: readonly ComponentMeta[] = [
466
479
  ],
467
480
  defaultValue: 'primary',
468
481
  },
469
- {
470
- name: 'font',
471
- label: 'Font',
472
- control: 'select',
473
- options: [
474
- { label: 'Default', value: 'default' },
475
- { label: 'Display', value: 'display' },
476
- { label: 'Mono', value: 'mono' },
477
- ],
478
- defaultValue: 'default',
479
- },
480
482
  ],
481
483
  },
482
484
  {
483
485
  id: 'icon',
484
486
  name: 'Icon',
485
487
  importName: 'Icon',
486
- categoryId: 'primitives',
488
+ categoryId: 'symbols',
487
489
  description: 'Material Symbols icon with size and fill',
488
490
  hasChildren: false,
489
491
  variants: [
@@ -520,7 +522,7 @@ export const COMPONENTS: readonly ComponentMeta[] = [
520
522
  id: 'wrapper',
521
523
  name: 'Wrapper',
522
524
  importName: 'Wrapper',
523
- categoryId: 'primitives',
525
+ categoryId: 'layout',
524
526
  description: 'Lightweight layout container with direction, spacing, and alignment (no theming)',
525
527
  hasChildren: true,
526
528
  variants: [
@@ -594,7 +596,7 @@ export const COMPONENTS: readonly ComponentMeta[] = [
594
596
  id: 'color-scale-slider',
595
597
  name: 'ColorScaleSlider',
596
598
  importName: 'ColorScaleSlider',
597
- categoryId: 'range-inputs',
599
+ categoryId: 'components',
598
600
  description: 'Interactive palette preview slider with color segments',
599
601
  hasChildren: false,
600
602
  variants: [
@@ -2,6 +2,7 @@ export interface CategoryMeta {
2
2
  readonly id: string;
3
3
  readonly name: string;
4
4
  readonly description: string;
5
+ readonly icon?: string;
5
6
  }
6
7
 
7
8
  export interface VariantMeta {
@@ -30,6 +31,7 @@ export interface ComponentMeta {
30
31
  readonly categoryId: string;
31
32
  readonly description: string;
32
33
  readonly hasChildren: boolean;
34
+ readonly previewLayout?: 'grid' | 'list';
33
35
  readonly variants: readonly VariantMeta[];
34
36
  readonly editableProps: readonly EditableProp[];
35
37
  }
@@ -2,6 +2,7 @@ import React, { createContext, useState, useMemo, useContext } from 'react';
2
2
  import type { NewtoneThemeConfig, NewtoneThemeContext, ColorMode } from './types';
3
3
  import { DEFAULT_THEME_CONFIG } from './defaults';
4
4
  import { GoogleFontLoader } from '../fonts/GoogleFontLoader';
5
+ import { SelfHostedFontLoader } from '../fonts/SelfHostedFontLoader';
5
6
  import { IconFontLoader } from '../fonts/IconFontLoader';
6
7
 
7
8
  const ThemeContext = createContext<NewtoneThemeContext | null>(null);
@@ -10,6 +11,14 @@ export interface NewtoneProviderProps {
10
11
  readonly config?: NewtoneThemeConfig;
11
12
  readonly initialMode?: ColorMode;
12
13
  readonly children: React.ReactNode;
14
+ /** Optional URL for typography telemetry. When set, adaptive Text instances report observations. */
15
+ readonly reportingEndpoint?: string;
16
+ /**
17
+ * Self-hosted @font-face CSS from the theme API.
18
+ * When provided, injects this CSS instead of loading from Google CDN.
19
+ * Falls back to Google CDN when absent or null.
20
+ */
21
+ readonly fontFaceCss?: string | null;
13
22
  }
14
23
 
15
24
  /**
@@ -28,6 +37,8 @@ export function NewtoneProvider({
28
37
  config = DEFAULT_THEME_CONFIG,
29
38
  initialMode = 'light',
30
39
  children,
40
+ reportingEndpoint,
41
+ fontFaceCss,
31
42
  }: NewtoneProviderProps) {
32
43
  const [mode, setMode] = useState<ColorMode>(initialMode);
33
44
 
@@ -36,13 +47,18 @@ export function NewtoneProvider({
36
47
  config,
37
48
  mode,
38
49
  setMode,
50
+ reportingEndpoint,
39
51
  }),
40
- [config, mode]
52
+ [config, mode, reportingEndpoint]
41
53
  );
42
54
 
43
55
  return (
44
56
  <ThemeContext.Provider value={value}>
45
- <GoogleFontLoader fonts={config.typography.fonts} />
57
+ {fontFaceCss ? (
58
+ <SelfHostedFontLoader fontFaceCss={fontFaceCss} />
59
+ ) : (
60
+ <GoogleFontLoader fonts={config.typography.fonts} />
61
+ )}
46
62
  <IconFontLoader icons={config.icons} />
47
63
  {children}
48
64
  </ThemeContext.Provider>
@@ -11,6 +11,10 @@ import {
11
11
  DEFAULT_ERROR_HUE,
12
12
  DEFAULT_ERROR_SATURATION,
13
13
  } from 'newtone';
14
+ import { DEFAULT_FONT_SLOTS, DEFAULT_FONT_SIZES, DEFAULT_LINE_HEIGHTS, DEFAULT_ROLE_SCALES } from '@newtonedev/fonts';
15
+
16
+ // Re-export typography defaults from @newtonedev/fonts (canonical source)
17
+ export { DEFAULT_FONT_SIZES, DEFAULT_LINE_HEIGHTS, DEFAULT_ROLE_SCALES } from '@newtonedev/fonts';
14
18
 
15
19
  /**
16
20
  * Default theme configuration matching playground defaults.
@@ -54,34 +58,10 @@ export const DEFAULT_THEME_CONFIG: NewtoneThemeConfig = {
54
58
  pill: 999,
55
59
  },
56
60
  typography: {
57
- fonts: {
58
- mono: {
59
- type: 'system',
60
- family: 'ui-monospace',
61
- fallback: 'SFMono-Regular, Menlo, Monaco, Consolas, monospace',
62
- },
63
- display: {
64
- type: 'system',
65
- family: 'system-ui',
66
- fallback: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
67
- },
68
- default: {
69
- type: 'system',
70
- family: 'system-ui',
71
- fallback: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
72
- },
73
- },
74
- scale: {
75
- xs: 10, // 16 / 1.25^2
76
- sm: 13, // 16 / 1.25
77
- base: 16,
78
- md: 20, // 16 * 1.25
79
- lg: 25, // 16 * 1.25^2
80
- xl: 31, // 16 * 1.25^3
81
- xxl: 39, // 16 * 1.25^4
82
- },
83
- lineHeight: { tight: 1.25, normal: 1.5, relaxed: 1.75 },
84
- fontWeight: { regular: 400, medium: 500, semibold: 600, bold: 700 },
61
+ fonts: DEFAULT_FONT_SLOTS,
62
+ fontSizes: DEFAULT_FONT_SIZES,
63
+ lineHeights: DEFAULT_LINE_HEIGHTS,
64
+ roles: DEFAULT_ROLE_SCALES,
85
65
  },
86
66
  icons: {
87
67
  variant: 'rounded', // Material Design 3 aesthetic