@newtonedev/components 0.1.6 → 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 (102) 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 +863 -437
  23. package/dist/index.cjs.map +1 -1
  24. package/dist/index.d.ts +9 -8
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +801 -391
  27. package/dist/index.js.map +1 -1
  28. package/dist/primitives/Icon/Icon.d.ts +1 -1
  29. package/dist/primitives/Icon/Icon.d.ts.map +1 -1
  30. package/dist/primitives/Icon/Icon.types.d.ts +8 -13
  31. package/dist/primitives/Icon/Icon.types.d.ts.map +1 -1
  32. package/dist/primitives/Text/Text.d.ts +33 -8
  33. package/dist/primitives/Text/Text.d.ts.map +1 -1
  34. package/dist/primitives/Text/Text.spans.d.ts +22 -0
  35. package/dist/primitives/Text/Text.spans.d.ts.map +1 -0
  36. package/dist/primitives/Text/Text.types.d.ts +75 -27
  37. package/dist/primitives/Text/Text.types.d.ts.map +1 -1
  38. package/dist/primitives/Text/index.d.ts +23 -2
  39. package/dist/primitives/Text/index.d.ts.map +1 -1
  40. package/dist/primitives/index.d.ts +1 -1
  41. package/dist/primitives/index.d.ts.map +1 -1
  42. package/dist/registry/codegen.d.ts.map +1 -1
  43. package/dist/registry/icons.d.ts +7 -0
  44. package/dist/registry/icons.d.ts.map +1 -0
  45. package/dist/registry/index.d.ts +2 -0
  46. package/dist/registry/index.d.ts.map +1 -1
  47. package/dist/registry/registry.d.ts.map +1 -1
  48. package/dist/registry/types.d.ts +3 -1
  49. package/dist/registry/types.d.ts.map +1 -1
  50. package/dist/theme/NewtoneProvider.d.ts +9 -1
  51. package/dist/theme/NewtoneProvider.d.ts.map +1 -1
  52. package/dist/theme/defaults.d.ts +1 -0
  53. package/dist/theme/defaults.d.ts.map +1 -1
  54. package/dist/theme/types.d.ts +48 -32
  55. package/dist/theme/types.d.ts.map +1 -1
  56. package/dist/theme/useBreakpoint.d.ts +9 -0
  57. package/dist/theme/useBreakpoint.d.ts.map +1 -0
  58. package/dist/tokens/computeTokens.d.ts +9 -22
  59. package/dist/tokens/computeTokens.d.ts.map +1 -1
  60. package/dist/tokens/types.d.ts +40 -22
  61. package/dist/tokens/types.d.ts.map +1 -1
  62. package/package.json +2 -1
  63. package/src/composites/actions/Button/Button.styles.ts +3 -3
  64. package/src/composites/actions/Button/Button.tsx +3 -2
  65. package/src/composites/form-controls/Select/Select.styles.ts +8 -8
  66. package/src/composites/form-controls/Select/Select.tsx +1 -1
  67. package/src/composites/form-controls/Select/SelectOption.tsx +3 -3
  68. package/src/composites/form-controls/TextInput/TextInput.styles.ts +5 -5
  69. package/src/composites/form-controls/Toggle/Toggle.styles.ts +3 -3
  70. package/src/composites/range-inputs/ColorScaleSlider/ColorScaleSlider.styles.ts +6 -6
  71. package/src/composites/range-inputs/HueSlider/HueSlider.styles.ts +9 -9
  72. package/src/composites/range-inputs/Slider/Slider.styles.ts +9 -9
  73. package/src/fonts/GoogleFontLoader.tsx +25 -10
  74. package/src/fonts/SelfHostedFontLoader.tsx +44 -0
  75. package/src/fonts/buildGoogleFontsUrl.ts +2 -31
  76. package/src/fonts/measureFont.ts +42 -0
  77. package/src/fonts/reportQueue.ts +54 -0
  78. package/src/fonts/useLocalCalibration.ts +97 -0
  79. package/src/fonts/useTypographyCalibrations.ts +15 -0
  80. package/src/index.ts +18 -7
  81. package/src/primitives/Frame/Frame.tsx +3 -3
  82. package/src/primitives/Icon/Icon.tsx +5 -7
  83. package/src/primitives/Icon/Icon.types.ts +8 -15
  84. package/src/primitives/Text/Text.spans.ts +57 -0
  85. package/src/primitives/Text/Text.tsx +205 -53
  86. package/src/primitives/Text/Text.types.ts +80 -27
  87. package/src/primitives/Text/index.ts +27 -3
  88. package/src/primitives/index.ts +3 -2
  89. package/src/registry/codegen.ts +1 -0
  90. package/src/registry/icons.ts +111 -0
  91. package/src/registry/index.ts +3 -0
  92. package/src/registry/registry.ts +67 -70
  93. package/src/registry/types.ts +3 -1
  94. package/src/theme/NewtoneProvider.tsx +18 -2
  95. package/src/theme/defaults.ts +8 -28
  96. package/src/theme/types.ts +63 -33
  97. package/src/theme/useBreakpoint.ts +14 -0
  98. package/src/tokens/computeTokens.ts +23 -19
  99. package/src/tokens/types.ts +10 -24
  100. package/dist/fonts/googleFonts.d.ts +0 -20
  101. package/dist/fonts/googleFonts.d.ts.map +0 -1
  102. package/src/fonts/googleFonts.ts +0 -87
@@ -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' },
452
+ ],
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' },
450
463
  ],
451
- defaultValue: 'regular',
464
+ defaultValue: 'md',
452
465
  },
453
466
  {
454
467
  name: 'color',
@@ -466,58 +479,42 @@ 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',
487
- description: 'Material Symbols icon with size, fill, and color',
488
+ categoryId: 'symbols',
489
+ description: 'Material Symbols icon with size and fill',
488
490
  hasChildren: false,
489
491
  variants: [
490
- { id: 'home', label: 'Home', props: { name: 'home' } },
491
- { id: 'settings', label: 'Settings', props: { name: 'settings' } },
492
- { id: 'check', label: 'Check', props: { name: 'check' } },
493
- { id: 'add', label: 'Add', props: { name: 'add' } },
494
- { id: 'delete', label: 'Delete', props: { name: 'delete' } },
495
- { id: 'search', label: 'Search', props: { name: 'search' } },
496
- { id: 'filled', label: 'Filled Icon', props: { name: 'favorite', fill: 1 } },
497
- { id: 'large', label: 'Large Icon', props: { name: 'star', size: 32 } },
492
+ { id: 'default', label: 'Default', props: { name: 'add' } },
498
493
  ],
499
494
  editableProps: [
500
495
  {
501
496
  name: 'name',
502
497
  label: 'Icon Name',
503
498
  control: 'text',
504
- defaultValue: 'home',
499
+ defaultValue: 'add',
505
500
  },
506
501
  {
507
502
  name: 'size',
508
503
  label: 'Size',
509
- control: 'number',
504
+ control: 'discrete-slider',
505
+ options: [
506
+ { label: '20', value: 20 },
507
+ { label: '24', value: 24 },
508
+ { label: '40', value: 40 },
509
+ { label: '48', value: 48 },
510
+ ],
510
511
  defaultValue: 24,
511
512
  },
512
513
  {
513
514
  name: 'fill',
514
515
  label: 'Fill',
515
- control: 'select',
516
- options: [
517
- { label: 'Outlined', value: 0 },
518
- { label: 'Filled', value: 1 },
519
- ],
520
- defaultValue: 0,
516
+ control: 'toggle',
517
+ defaultValue: false,
521
518
  },
522
519
  ],
523
520
  },
@@ -525,7 +522,7 @@ export const COMPONENTS: readonly ComponentMeta[] = [
525
522
  id: 'wrapper',
526
523
  name: 'Wrapper',
527
524
  importName: 'Wrapper',
528
- categoryId: 'primitives',
525
+ categoryId: 'layout',
529
526
  description: 'Lightweight layout container with direction, spacing, and alignment (no theming)',
530
527
  hasChildren: true,
531
528
  variants: [
@@ -599,7 +596,7 @@ export const COMPONENTS: readonly ComponentMeta[] = [
599
596
  id: 'color-scale-slider',
600
597
  name: 'ColorScaleSlider',
601
598
  importName: 'ColorScaleSlider',
602
- categoryId: 'range-inputs',
599
+ categoryId: 'components',
603
600
  description: 'Interactive palette preview slider with color segments',
604
601
  hasChildren: false,
605
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 {
@@ -18,7 +19,7 @@ export interface EditablePropOption {
18
19
  export interface EditableProp {
19
20
  readonly name: string;
20
21
  readonly label: string;
21
- readonly control: 'select' | 'text' | 'toggle' | 'number';
22
+ readonly control: 'select' | 'text' | 'toggle' | 'number' | 'discrete-slider';
22
23
  readonly options?: readonly EditablePropOption[];
23
24
  readonly defaultValue: string | number | boolean;
24
25
  }
@@ -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
@@ -1,20 +1,27 @@
1
1
  import type { DynamicRange, PaletteConfig } from 'newtone';
2
2
 
3
+ // Re-export font/typography types from @newtonedev/fonts (canonical source)
4
+ export type {
5
+ FontConfig,
6
+ FontWeights,
7
+ FontSlot,
8
+ FontSizeScale,
9
+ LineHeightScale,
10
+ RoleSizeStep,
11
+ RoleScale,
12
+ RoleScales,
13
+ BreakpointKey,
14
+ Breakpoints,
15
+ BreakpointRoleScales,
16
+ FontRuntimeMetrics,
17
+ } from '@newtonedev/fonts';
18
+ import type { FontSlot, FontSizeScale, LineHeightScale, RoleScales, BreakpointRoleScales, FontRuntimeMetrics, TextRole } from '@newtonedev/fonts';
19
+
3
20
  /**
4
21
  * Color mode: light or dark
5
22
  */
6
23
  export type ColorMode = 'light' | 'dark';
7
24
 
8
- /**
9
- * Font configuration for a single font slot
10
- */
11
- export interface FontConfig {
12
- readonly type: 'system' | 'google' | 'custom';
13
- readonly family: string; // 'ui-monospace' | 'Roboto' | custom name
14
- readonly customUrl?: string; // Supabase Storage URL for custom fonts
15
- readonly fallback: string; // CSS fallback stack
16
- }
17
-
18
25
  /**
19
26
  * Elevation levels for surfaces (internal)
20
27
  * 0 = sunken, 1 = default, 2 = elevated
@@ -118,30 +125,46 @@ export interface NewtoneThemeConfig {
118
125
  };
119
126
  readonly typography: {
120
127
  readonly fonts: {
121
- readonly mono: FontConfig;
122
- readonly display: FontConfig;
123
- readonly default: FontConfig;
124
- };
125
- readonly scale: {
126
- readonly xs: number; // baseSize / ratio^2
127
- readonly sm: number; // baseSize / ratio
128
- readonly base: number; // baseSize
129
- readonly md: number; // baseSize * ratio
130
- readonly lg: number; // baseSize * ratio^2
131
- readonly xl: number; // baseSize * ratio^3
132
- readonly xxl: number; // baseSize * ratio^4
133
- };
134
- readonly lineHeight: {
135
- readonly tight: number; // 1.25
136
- readonly normal: number; // 1.5
137
- readonly relaxed: number; // 1.75
138
- };
139
- readonly fontWeight: {
140
- readonly regular: number; // 400
141
- readonly medium: number; // 500
142
- readonly semibold: number; // 600
143
- readonly bold: number; // 700
128
+ readonly main: FontSlot; // Default/body font (fallback for all scopes)
129
+ readonly display: FontSlot; // Headlines, titles, large display text
130
+ readonly mono: FontSlot; // Code snippets, technical content
131
+ readonly currency: FontSlot; // Monetary amounts, financial data
144
132
  };
133
+ /** Primitive font size scale — numbered tokens ('01'-'16'), values in px. */
134
+ readonly fontSizes: FontSizeScale;
135
+ /** Primitive line height scale — numbered tokens ('01'-'16'), values in px. */
136
+ readonly lineHeights: LineHeightScale;
137
+ /** Role mapping — pairs fontSizes and lineHeights for each role x size combination. */
138
+ readonly roles: RoleScales;
139
+ /**
140
+ * Per-font avgCharWidth ratios, keyed by font family name.
141
+ * Measured with canvas at editor publish time (Layer 1 calibration).
142
+ * Used by resolveResponsiveSize to improve line break estimation accuracy.
143
+ * Falls back to 0.55 when absent.
144
+ */
145
+ readonly calibrations?: Record<string, number>;
146
+ /**
147
+ * Pre-computed role scales for each viewport breakpoint (sm/md/lg).
148
+ * Enables CSS-only consumers to generate @media rules from the theme JSON
149
+ * without importing newtone-fonts. Runtime components use useBreakpoint
150
+ * instead and compute scaling on the fly.
151
+ */
152
+ readonly breakpointRoles?: BreakpointRoleScales;
153
+ /**
154
+ * Per-font OpenType-derived metrics, keyed by font family name.
155
+ * Extracted from font binaries at upload time, looked up at publish time.
156
+ * Used by Text for line height correction (adjustLineHeight),
157
+ * vertical centering (verticalCenterOffset), and font feature validation.
158
+ * Falls back to Inter-equivalent defaults when absent.
159
+ */
160
+ readonly fontMetrics?: Readonly<Record<string, FontRuntimeMetrics>>;
161
+ /**
162
+ * Per-role font weight (CSS font-weight 100–900).
163
+ * Primary weight path — the Text component uses these values directly.
164
+ * When populated via toThemeConfig, defaults are merged with user overrides.
165
+ * Per-instance weight prop still takes priority.
166
+ */
167
+ readonly roleWeights?: Partial<Record<TextRole, number>>;
145
168
  };
146
169
  readonly icons: {
147
170
  readonly variant: 'outlined' | 'rounded' | 'sharp';
@@ -158,4 +181,11 @@ export interface NewtoneThemeContext {
158
181
  readonly config: NewtoneThemeConfig;
159
182
  readonly mode: ColorMode;
160
183
  readonly setMode: (mode: ColorMode) => void;
184
+ /**
185
+ * Optional URL for the typography telemetry reporting endpoint.
186
+ * When set, the Text component (in responsive mode) will batch and POST
187
+ * ObserverPayload observations to this URL for cross-site aggregation.
188
+ * When absent, no telemetry is sent (opt-in).
189
+ */
190
+ readonly reportingEndpoint?: string;
161
191
  }
@@ -0,0 +1,14 @@
1
+ import { useWindowDimensions } from 'react-native';
2
+ import { getBreakpointForWidth } from '@newtonedev/fonts';
3
+ import type { BreakpointKey } from '@newtonedev/fonts';
4
+
5
+ /**
6
+ * Returns the current viewport breakpoint key ('sm' | 'md' | 'lg').
7
+ *
8
+ * Uses `useWindowDimensions` from react-native (cross-platform — works
9
+ * on web via react-native-web) and re-evaluates on window resize.
10
+ */
11
+ export function useBreakpoint(): BreakpointKey {
12
+ const { width } = useWindowDimensions();
13
+ return getBreakpointForWidth(width);
14
+ }
@@ -1,6 +1,7 @@
1
1
  import { getColor } from 'newtone';
2
2
  import type { PaletteConfig } from 'newtone';
3
- import type { ColorSystemConfig, ColorMode, ElevationLevel, FontConfig, TokenOverrides } from '../theme/types';
3
+ import { fontConfigToFamily } from '@newtonedev/fonts';
4
+ import type { ColorSystemConfig, ColorMode, ElevationLevel, FontSlot, TokenOverrides } from '../theme/types';
4
5
  import type { ResolvedTokens, PaletteTokens } from './types';
5
6
 
6
7
  /**
@@ -115,14 +116,6 @@ export const ERROR_DEFAULTS: PaletteDefaults = {
115
116
  },
116
117
  };
117
118
 
118
- /**
119
- * Convert FontConfig to CSS font-family string
120
- */
121
- function fontConfigToFamily(font: FontConfig): string {
122
- const family = font.family.includes(' ') ? `"${font.family}"` : font.family;
123
- return `${family}, ${font.fallback}`;
124
- }
125
-
126
119
  const clamp = (n: number) => Math.max(0, Math.min(1, n));
127
120
 
128
121
  /**
@@ -262,10 +255,9 @@ export function computeTokens(
262
255
  spacing: { readonly '00': number; readonly '02': number; readonly '04': number; readonly '06': number; readonly '08': number; readonly '10': number; readonly '12': number; readonly '16': number; readonly '20': number; readonly '24': number; readonly '32': number; readonly '40': number; readonly '48': number },
263
256
  radius: { readonly none: number; readonly sm: number; readonly md: number; readonly lg: number; readonly xl: number; readonly pill: 999 },
264
257
  typography: {
265
- readonly fonts: { readonly mono: FontConfig; readonly display: FontConfig; readonly default: FontConfig };
266
- readonly scale: { readonly xs: number; readonly sm: number; readonly base: number; readonly md: number; readonly lg: number; readonly xl: number; readonly xxl: number };
267
- readonly lineHeight: { readonly tight: number; readonly normal: number; readonly relaxed: number };
268
- readonly fontWeight: { readonly regular: number; readonly medium: number; readonly semibold: number; readonly bold: number };
258
+ readonly fonts: { readonly main: FontSlot; readonly display: FontSlot; readonly mono: FontSlot; readonly currency: FontSlot };
259
+ readonly fontSizes: { readonly [key: string]: number };
260
+ readonly lineHeights: { readonly [key: string]: number };
269
261
  },
270
262
  icons: {
271
263
  readonly variant: 'outlined' | 'rounded' | 'sharp';
@@ -493,13 +485,25 @@ export function computeTokens(
493
485
  radius,
494
486
  typography: {
495
487
  fonts: {
496
- mono: fontConfigToFamily(typography.fonts.mono),
497
- display: fontConfigToFamily(typography.fonts.display),
498
- default: fontConfigToFamily(typography.fonts.default),
488
+ main: {
489
+ family: fontConfigToFamily(typography.fonts.main.config),
490
+ weights: typography.fonts.main.weights,
491
+ },
492
+ display: {
493
+ family: fontConfigToFamily(typography.fonts.display.config),
494
+ weights: typography.fonts.display.weights,
495
+ },
496
+ mono: {
497
+ family: fontConfigToFamily(typography.fonts.mono.config),
498
+ weights: typography.fonts.mono.weights,
499
+ },
500
+ currency: {
501
+ family: fontConfigToFamily(typography.fonts.currency.config),
502
+ weights: typography.fonts.currency.weights,
503
+ },
499
504
  },
500
- size: typography.scale,
501
- lineHeight: typography.lineHeight,
502
- weight: typography.fontWeight,
505
+ fontSizes: typography.fontSizes,
506
+ lineHeights: typography.lineHeights,
503
507
  },
504
508
  icons: {
505
509
  variant: icons.variant,
@@ -123,33 +123,19 @@ export interface ResolvedTokens {
123
123
  readonly xl: number;
124
124
  readonly pill: 999;
125
125
  };
126
- /** Typography tokens for fonts, sizes, line heights, and weights */
126
+ /** Typography tokens for fonts (per-scope) and primitive size/lineHeight scales */
127
127
  readonly typography: {
128
+ /** Per-scope font family + weight mapping */
128
129
  readonly fonts: {
129
- readonly mono: string; // CSS font-family string
130
- readonly display: string; // CSS font-family string
131
- readonly default: string; // CSS font-family string
132
- };
133
- readonly size: {
134
- readonly xs: number;
135
- readonly sm: number;
136
- readonly base: number;
137
- readonly md: number;
138
- readonly lg: number;
139
- readonly xl: number;
140
- readonly xxl: number;
141
- };
142
- readonly lineHeight: {
143
- readonly tight: number;
144
- readonly normal: number;
145
- readonly relaxed: number;
146
- };
147
- readonly weight: {
148
- readonly regular: number;
149
- readonly medium: number;
150
- readonly semibold: number;
151
- readonly bold: number;
130
+ readonly main: { readonly family: string; readonly weights: { readonly regular: number; readonly medium: number; readonly bold: number; } };
131
+ readonly display: { readonly family: string; readonly weights: { readonly regular: number; readonly medium: number; readonly bold: number; } };
132
+ readonly mono: { readonly family: string; readonly weights: { readonly regular: number; readonly medium: number; readonly bold: number; } };
133
+ readonly currency: { readonly family: string; readonly weights: { readonly regular: number; readonly medium: number; readonly bold: number; } };
152
134
  };
135
+ /** Primitive font size scale — numbered tokens ('01'–'16'), values in px */
136
+ readonly fontSizes: { readonly [key: string]: number };
137
+ /** Primitive line height scale — numbered tokens ('01'–'16'), values in px */
138
+ readonly lineHeights: { readonly [key: string]: number };
153
139
  };
154
140
  /** Icon tokens for Material Symbols configuration */
155
141
  readonly icons: {
@@ -1,20 +0,0 @@
1
- export interface GoogleFontEntry {
2
- readonly family: string;
3
- readonly category: 'sans-serif' | 'serif' | 'monospace' | 'display';
4
- readonly fallback: string;
5
- }
6
- /**
7
- * Curated list of popular Google Fonts organized by category.
8
- * Used by the font picker UI and the GoogleFontLoader.
9
- */
10
- export declare const GOOGLE_FONTS: readonly GoogleFontEntry[];
11
- export interface SystemFontEntry {
12
- readonly family: string;
13
- readonly category: 'sans-serif' | 'serif' | 'monospace';
14
- readonly fallback: string;
15
- }
16
- /**
17
- * System font options that don't require external loading.
18
- */
19
- export declare const SYSTEM_FONTS: readonly SystemFontEntry[];
20
- //# sourceMappingURL=googleFonts.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"googleFonts.d.ts","sourceRoot":"","sources":["../../src/fonts/googleFonts.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,YAAY,GAAG,OAAO,GAAG,WAAW,GAAG,SAAS,CAAC;IACpE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED;;;GAGG;AACH,eAAO,MAAM,YAAY,EAAE,SAAS,eAAe,EAiDlD,CAAC;AAEF,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,YAAY,GAAG,OAAO,GAAG,WAAW,CAAC;IACxD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED;;GAEG;AACH,eAAO,MAAM,YAAY,EAAE,SAAS,eAAe,EAgBlD,CAAC"}