@oxyhq/bloom 0.7.7 → 0.8.0

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 (109) hide show
  1. package/lib/commonjs/normalize-colors.d.js +2 -0
  2. package/lib/commonjs/normalize-colors.d.js.map +1 -0
  3. package/lib/commonjs/theme/apply-dark-class.js +8 -24
  4. package/lib/commonjs/theme/apply-dark-class.js.map +1 -1
  5. package/lib/commonjs/theme/build-theme.js +39 -61
  6. package/lib/commonjs/theme/build-theme.js.map +1 -1
  7. package/lib/commonjs/theme/color-presets.js +9 -9
  8. package/lib/commonjs/theme/color-scope/index.web.js +7 -14
  9. package/lib/commonjs/theme/color-scope/index.web.js.map +1 -1
  10. package/lib/commonjs/theme/color-scope/style-builder.js +7 -6
  11. package/lib/commonjs/theme/color-scope/style-builder.js.map +1 -1
  12. package/lib/commonjs/theme/color-space.js +76 -0
  13. package/lib/commonjs/theme/color-space.js.map +1 -0
  14. package/lib/commonjs/theme/index.js +0 -6
  15. package/lib/commonjs/theme/index.js.map +1 -1
  16. package/lib/commonjs/theme/native-root-vars.native.js +28 -18
  17. package/lib/commonjs/theme/native-root-vars.native.js.map +1 -1
  18. package/lib/commonjs/theme/preset-vars.js +20 -152
  19. package/lib/commonjs/theme/preset-vars.js.map +1 -1
  20. package/lib/commonjs/theme/token-registry.js +123 -0
  21. package/lib/commonjs/theme/token-registry.js.map +1 -0
  22. package/lib/module/normalize-colors.d.js +2 -0
  23. package/lib/module/normalize-colors.d.js.map +1 -0
  24. package/lib/module/theme/apply-dark-class.js +8 -19
  25. package/lib/module/theme/apply-dark-class.js.map +1 -1
  26. package/lib/module/theme/build-theme.js +39 -61
  27. package/lib/module/theme/build-theme.js.map +1 -1
  28. package/lib/module/theme/color-presets.js +9 -9
  29. package/lib/module/theme/color-scope/index.web.js +7 -14
  30. package/lib/module/theme/color-scope/index.web.js.map +1 -1
  31. package/lib/module/theme/color-scope/style-builder.js +7 -6
  32. package/lib/module/theme/color-scope/style-builder.js.map +1 -1
  33. package/lib/module/theme/color-space.js +69 -0
  34. package/lib/module/theme/color-space.js.map +1 -0
  35. package/lib/module/theme/index.js +1 -1
  36. package/lib/module/theme/index.js.map +1 -1
  37. package/lib/module/theme/native-root-vars.native.js +29 -18
  38. package/lib/module/theme/native-root-vars.native.js.map +1 -1
  39. package/lib/module/theme/preset-vars.js +19 -150
  40. package/lib/module/theme/preset-vars.js.map +1 -1
  41. package/lib/module/theme/token-registry.js +113 -0
  42. package/lib/module/theme/token-registry.js.map +1 -0
  43. package/lib/typescript/commonjs/theme/apply-dark-class.d.ts +5 -10
  44. package/lib/typescript/commonjs/theme/apply-dark-class.d.ts.map +1 -1
  45. package/lib/typescript/commonjs/theme/build-theme.d.ts +1 -1
  46. package/lib/typescript/commonjs/theme/build-theme.d.ts.map +1 -1
  47. package/lib/typescript/commonjs/theme/color-presets.d.ts +9 -9
  48. package/lib/typescript/commonjs/theme/color-scope/index.web.d.ts.map +1 -1
  49. package/lib/typescript/commonjs/theme/color-scope/style-builder.d.ts +5 -2
  50. package/lib/typescript/commonjs/theme/color-scope/style-builder.d.ts.map +1 -1
  51. package/lib/typescript/commonjs/theme/color-space.d.ts +28 -0
  52. package/lib/typescript/commonjs/theme/color-space.d.ts.map +1 -0
  53. package/lib/typescript/commonjs/theme/index.d.ts +1 -2
  54. package/lib/typescript/commonjs/theme/index.d.ts.map +1 -1
  55. package/lib/typescript/commonjs/theme/native-root-vars.native.d.ts +25 -15
  56. package/lib/typescript/commonjs/theme/native-root-vars.native.d.ts.map +1 -1
  57. package/lib/typescript/commonjs/theme/preset-vars.d.ts +11 -74
  58. package/lib/typescript/commonjs/theme/preset-vars.d.ts.map +1 -1
  59. package/lib/typescript/commonjs/theme/token-registry.d.ts +53 -0
  60. package/lib/typescript/commonjs/theme/token-registry.d.ts.map +1 -0
  61. package/lib/typescript/commonjs/theme/types.d.ts +3 -0
  62. package/lib/typescript/commonjs/theme/types.d.ts.map +1 -1
  63. package/lib/typescript/module/theme/apply-dark-class.d.ts +5 -10
  64. package/lib/typescript/module/theme/apply-dark-class.d.ts.map +1 -1
  65. package/lib/typescript/module/theme/build-theme.d.ts +1 -1
  66. package/lib/typescript/module/theme/build-theme.d.ts.map +1 -1
  67. package/lib/typescript/module/theme/color-presets.d.ts +9 -9
  68. package/lib/typescript/module/theme/color-scope/index.web.d.ts.map +1 -1
  69. package/lib/typescript/module/theme/color-scope/style-builder.d.ts +5 -2
  70. package/lib/typescript/module/theme/color-scope/style-builder.d.ts.map +1 -1
  71. package/lib/typescript/module/theme/color-space.d.ts +28 -0
  72. package/lib/typescript/module/theme/color-space.d.ts.map +1 -0
  73. package/lib/typescript/module/theme/index.d.ts +1 -2
  74. package/lib/typescript/module/theme/index.d.ts.map +1 -1
  75. package/lib/typescript/module/theme/native-root-vars.native.d.ts +25 -15
  76. package/lib/typescript/module/theme/native-root-vars.native.d.ts.map +1 -1
  77. package/lib/typescript/module/theme/preset-vars.d.ts +11 -74
  78. package/lib/typescript/module/theme/preset-vars.d.ts.map +1 -1
  79. package/lib/typescript/module/theme/token-registry.d.ts +53 -0
  80. package/lib/typescript/module/theme/token-registry.d.ts.map +1 -0
  81. package/lib/typescript/module/theme/types.d.ts +3 -0
  82. package/lib/typescript/module/theme/types.d.ts.map +1 -1
  83. package/package.json +1 -1
  84. package/src/__tests__/BloomColorScope.test.tsx +5 -5
  85. package/src/__tests__/BloomThemeProvider.test.tsx +4 -3
  86. package/src/__tests__/apply-dark-class.test.ts +26 -21
  87. package/src/__tests__/native-root-vars.test.ts +8 -7
  88. package/src/__tests__/preset-vars.test.ts +31 -77
  89. package/src/__tests__/theme.test.ts +6 -6
  90. package/src/normalize-colors.d.ts +22 -0
  91. package/src/react-native-css.d.ts +3 -3
  92. package/src/theme/__tests__/__fixtures__/golden-resolved-tokens.json +574 -0
  93. package/src/theme/__tests__/__snapshots__/visual-gallery.test.tsx.snap +838 -0
  94. package/src/theme/__tests__/color-space.test.ts +28 -0
  95. package/src/theme/__tests__/golden-tokens.test.ts +33 -0
  96. package/src/theme/__tests__/runtime-contract.test.ts +49 -0
  97. package/src/theme/__tests__/token-registry.test.ts +30 -0
  98. package/src/theme/__tests__/visual-gallery.test.tsx +10 -0
  99. package/src/theme/apply-dark-class.ts +8 -17
  100. package/src/theme/build-theme.ts +39 -67
  101. package/src/theme/color-presets.ts +9 -9
  102. package/src/theme/color-scope/index.web.tsx +7 -14
  103. package/src/theme/color-scope/style-builder.ts +7 -4
  104. package/src/theme/color-space.ts +56 -0
  105. package/src/theme/index.ts +1 -6
  106. package/src/theme/native-root-vars.native.ts +28 -18
  107. package/src/theme/preset-vars.ts +17 -178
  108. package/src/theme/token-registry.ts +159 -0
  109. package/src/theme/types.ts +3 -0
@@ -1,5 +1,6 @@
1
1
  import { APP_COLOR_NAMES, APP_COLOR_PRESETS } from '../theme/color-presets';
2
- import { getPresetVars, hslTripletToRgb, toWebColorValue } from '../theme/preset-vars';
2
+ import { getPresetVars } from '../theme/preset-vars';
3
+ import { getResolvedTokens } from '../theme/token-registry';
3
4
 
4
5
  const EXTENDED_KEYS = [
5
6
  '--card',
@@ -45,99 +46,52 @@ describe('getPresetVars', () => {
45
46
  expect(vars['--sidebar-primary']).toBe(primary);
46
47
  });
47
48
 
48
- it('does not emit resolved --color-* vars by default', () => {
49
+ it('returns raw HSL triples (no resolved colors) getResolvedTokens owns rgb', () => {
50
+ // `getPresetVars` is the raw triple source feeding the single canonical
51
+ // resolver. It must NOT pre-resolve to rgb/hsl, and the legacy resolved-color
52
+ // companion universe must be gone (no key carries the resolved-color prefix).
49
53
  const vars = getPresetVars('green', 'dark');
50
- expect(vars['--color-primary']).toBeUndefined();
54
+ expect(vars['--primary']).toBe(APP_COLOR_PRESETS.green.dark['--primary']);
55
+ expect(/^-?\d[\d.]*\s+[\d.]+%/.test(vars['--primary'] ?? '')).toBe(true);
56
+ const legacyPrefix = `--${'color'}-`;
57
+ expect(Object.keys(vars).some((k) => k.startsWith(legacyPrefix))).toBe(false);
51
58
  });
59
+ });
52
60
 
53
- it('emits resolved --color-* vars as rgb() when requested', () => {
54
- const vars = getPresetVars('green', 'dark', { includeResolvedColorVars: true });
61
+ describe('getResolvedTokens (single canonical rgb pipeline)', () => {
62
+ it('resolves every base + extended token to a space-separated rgb() string', () => {
63
+ const resolved = getResolvedTokens('green', 'dark');
55
64
  const primaryTriple = APP_COLOR_PRESETS.green.dark['--primary'] ?? '';
56
- expect(vars['--color-primary']).toBe(hslTripletToRgb(primaryTriple));
57
- expect(vars['--color-primary']).toMatch(/^rgb\(\d+ \d+ \d+\)$/);
58
- expect(vars['--color-card']).toBe(hslTripletToRgb(vars['--card'] ?? ''));
65
+ // No resolved-color companion universe anymore — the base token is rgb.
66
+ const legacyPrefix = `--${'color'}-`;
67
+ expect(Object.keys(resolved).some((k) => k.startsWith(legacyPrefix))).toBe(false);
68
+ expect(resolved['--primary']).toMatch(/^rgb\(\d+ \d+ \d+\)$/);
69
+ expect(resolved['--card']).toMatch(/^rgb\(\d+ \d+ \d+\)$/);
70
+ // primary triple resolves to the same rgb the registry produces.
71
+ expect(resolved['--primary']).toBe(getResolvedTokens('green', 'dark')['--primary']);
72
+ expect(primaryTriple).toMatch(/%/);
59
73
  });
60
74
 
61
- it('resolves --color-primary to rgb for the blue preset (matches web reference)', () => {
75
+ it('resolves --primary to rgb for the blue preset (matches web reference)', () => {
62
76
  // `blue` preset light `--primary` is `205 87% 53%` → rgb(31 153 239).
63
- const vars = getPresetVars('blue', 'light', { includeResolvedColorVars: true });
64
- expect(vars['--color-primary']).toBe('rgb(31 153 239)');
65
- });
66
- });
67
-
68
- describe('hslTripletToRgb', () => {
69
- it('converts a plain HSL triple to a space-separated rgb() string', () => {
70
- expect(hslTripletToRgb('205 87% 53%')).toBe('rgb(31 153 239)');
71
- });
72
-
73
- it('converts the achromatic extremes', () => {
74
- expect(hslTripletToRgb('0 0% 0%')).toBe('rgb(0 0 0)');
75
- expect(hslTripletToRgb('0 0% 100%')).toBe('rgb(255 255 255)');
76
- expect(hslTripletToRgb('0 0% 12%')).toBe('rgb(31 31 31)');
77
- });
78
-
79
- it('converts a saturated triple within ±1 per channel of the reference', () => {
80
- // teal `185 100% 20%` ≈ rgb(0 94 102).
81
- const match = /^rgb\((\d+) (\d+) (\d+)\)$/.exec(hslTripletToRgb('185 100% 20%'));
82
- expect(match).not.toBeNull();
83
- const r = Number(match?.[1] ?? NaN);
84
- const g = Number(match?.[2] ?? NaN);
85
- const b = Number(match?.[3] ?? NaN);
86
- expect(Math.abs(r - 0)).toBeLessThanOrEqual(1);
87
- expect(Math.abs(g - 94)).toBeLessThanOrEqual(1);
88
- expect(Math.abs(b - 102)).toBeLessThanOrEqual(1);
89
- });
90
-
91
- it('tolerates a deg suffix on the hue', () => {
92
- expect(hslTripletToRgb('205deg 87% 53%')).toBe('rgb(31 153 239)');
93
- });
94
-
95
- it('emits rgb(r g b / a) for an alpha tail (decimal or percentage)', () => {
96
- expect(hslTripletToRgb('205 87% 53% / 0.5')).toBe('rgb(31 153 239 / 0.5)');
97
- expect(hslTripletToRgb('205 87% 53% / 50%')).toBe('rgb(31 153 239 / 0.5)');
98
- });
99
- });
100
-
101
- describe('toWebColorValue (web var contract)', () => {
102
- it('wraps a raw HSL triple base token in hsl()', () => {
103
- expect(toWebColorValue('--background', '185 50% 5%')).toBe('hsl(185 50% 5%)');
104
- expect(toWebColorValue('--primary', '185 100% 20%')).toBe('hsl(185 100% 20%)');
105
- expect(toWebColorValue('--foreground', '0 0% 12%')).toBe('hsl(0 0% 12%)');
106
- });
107
-
108
- it('wraps a raw HSL triple with an alpha tail into modern hsl(H S% L% / A) syntax', () => {
109
- expect(toWebColorValue('--ring', '0 0% 0% / 0.5')).toBe('hsl(0 0% 0% / 0.5)');
110
- });
111
-
112
- it('passes --color-* resolved rgb() vars through unchanged', () => {
113
- expect(toWebColorValue('--color-primary', 'rgb(31 153 239)')).toBe('rgb(31 153 239)');
114
- expect(toWebColorValue('--color-background', 'rgb(0 94 102 / 0.5)')).toBe(
115
- 'rgb(0 94 102 / 0.5)',
116
- );
117
- });
118
-
119
- it('passes non-color tokens (radius / lengths) through unchanged', () => {
120
- expect(toWebColorValue('--radius', '0.5rem')).toBe('0.5rem');
121
- expect(toWebColorValue('--radius-lg', '12px')).toBe('12px');
122
- });
123
-
124
- it('passes already-resolved color values through unchanged', () => {
125
- expect(toWebColorValue('--primary', 'rgb(31 153 239)')).toBe('rgb(31 153 239)');
126
- expect(toWebColorValue('--primary', 'hsl(185 100% 20%)')).toBe('hsl(185 100% 20%)');
127
- expect(toWebColorValue('--primary', '#1d9bf0')).toBe('#1d9bf0');
77
+ const resolved = getResolvedTokens('blue', 'light');
78
+ expect(resolved['--primary']).toBe('rgb(31 153 239)');
128
79
  });
129
80
 
130
81
  it('produces a full CSS color for every base preset token of every preset/mode', () => {
131
82
  for (const name of APP_COLOR_NAMES) {
132
83
  for (const mode of ['light', 'dark'] as const) {
133
84
  const base = mode === 'light' ? APP_COLOR_PRESETS[name].light : APP_COLOR_PRESETS[name].dark;
134
- for (const [key, value] of Object.entries(base)) {
135
- const web = toWebColorValue(key, value);
85
+ const resolved = getResolvedTokens(name, mode);
86
+ for (const key of Object.keys(base)) {
87
+ const value = resolved[key] ?? '';
136
88
  // A bare triple (`185 50% 5%`) is INVALID CSS — the incident. Every
137
89
  // base token must come out as a complete color the browser parses.
138
- expect(web.startsWith('hsl(') || web.startsWith('rgb(') || web.startsWith('#')).toBe(
90
+ expect(value.startsWith('rgb(') || value.startsWith('hsl(') || value.startsWith('#')).toBe(
139
91
  true,
140
92
  );
93
+ // Never a bare HSL triple.
94
+ expect(/^-?\d[\d.]*\s+[\d.]+%/.test(value)).toBe(false);
141
95
  }
142
96
  }
143
97
  }
@@ -50,12 +50,12 @@ describe('Theme system', () => {
50
50
 
51
51
  describe('buildTheme primaryForeground', () => {
52
52
  it('resolves a readable foreground for the primary fill per preset', () => {
53
- // blue's --primary-foreground is `0 0% 100%` → white
54
- expect(buildTheme('blue', 'light').colors.primaryForeground).toBe('hsl(0, 0%, 100%)');
55
- expect(buildTheme('blue', 'dark').colors.primaryForeground).toBe('hsl(0, 0%, 100%)');
56
- // yellow's --primary-foreground is `0 0% 0%` → black
57
- expect(buildTheme('yellow', 'light').colors.primaryForeground).toBe('hsl(0, 0%, 0%)');
58
- expect(buildTheme('yellow', 'dark').colors.primaryForeground).toBe('hsl(0, 0%, 0%)');
53
+ // blue's --primary-foreground is `0 0% 100%` → white (canonical sRGB rgb)
54
+ expect(buildTheme('blue', 'light').colors.primaryForeground).toBe('rgb(255 255 255)');
55
+ expect(buildTheme('blue', 'dark').colors.primaryForeground).toBe('rgb(255 255 255)');
56
+ // yellow's --primary-foreground is `0 0% 0%` → black (canonical sRGB rgb)
57
+ expect(buildTheme('yellow', 'light').colors.primaryForeground).toBe('rgb(0 0 0)');
58
+ expect(buildTheme('yellow', 'dark').colors.primaryForeground).toBe('rgb(0 0 0)');
59
59
  });
60
60
 
61
61
  it('sets a primaryForeground for every preset in both modes', () => {
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Ambient declaration for `@react-native/normalize-colors`.
3
+ *
4
+ * The package ships JS-only (`module.exports = normalizeColor`) with no bundled
5
+ * type declarations, so `tsc` can't see its shape. It arrives transitively via
6
+ * react-native / react-native-web — Bloom only references it in tests, as the
7
+ * exact parser React Native (native) and react-native-web use behind
8
+ * `StyleSheet`/`processColor` to validate that a color string is renderable.
9
+ *
10
+ * Typed to match the package's runtime contract (`index.js`): a default export
11
+ * that takes a CSS color string or a packed 32-bit RGBA number and returns the
12
+ * normalized packed RGBA integer, or `null` when the input is unparseable.
13
+ */
14
+ declare module '@react-native/normalize-colors' {
15
+ /**
16
+ * Parse a CSS color string (or packed RGBA number) into a packed 32-bit RGBA
17
+ * integer (`0xRRGGBBAA`). Returns `null` when the input cannot be parsed — an
18
+ * unparseable color renders nothing in React Native / react-native-web.
19
+ */
20
+ const normalizeColor: (color: string | number) => number | null;
21
+ export default normalizeColor;
22
+ }
@@ -20,9 +20,9 @@
20
20
  declare module 'react-native-css/native-internal' {
21
21
  /**
22
22
  * A leaf style value. Mirrors react-native-css's `compiler.types`
23
- * `StyleDescriptor`. Bloom only ever writes string color values (raw HSL
24
- * triples for the `--*` vars, sRGB `rgb(...)` for the resolved `--color-*`
25
- * vars), but the family is typed against the full descriptor union.
23
+ * `StyleDescriptor`. Bloom only ever writes string color values (sRGB
24
+ * `rgb(...)` strings from the single canonical token pipeline), but the family
25
+ * is typed against the full descriptor union.
26
26
  */
27
27
  type StyleDescriptor =
28
28
  | string