@wordpress/theme 0.1.0 → 0.1.1-next.2f1c7c01b.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 (142) hide show
  1. package/README.md +69 -9
  2. package/bin/generate-default-ramps/index.ts +49 -0
  3. package/bin/generate-primitive-tokens/index.ts +14 -9
  4. package/bin/terrazzo-plugin-figma-ds-token-manager/index.ts +11 -4
  5. package/build/color-ramps/index.js +21 -39
  6. package/build/color-ramps/index.js.map +3 -3
  7. package/build/color-ramps/lib/color-utils.js +39 -0
  8. package/build/color-ramps/lib/color-utils.js.map +7 -0
  9. package/build/color-ramps/lib/constants.js +18 -25
  10. package/build/color-ramps/lib/constants.js.map +3 -3
  11. package/build/color-ramps/lib/default-ramps.js +220 -0
  12. package/build/color-ramps/lib/default-ramps.js.map +7 -0
  13. package/build/color-ramps/lib/find-color-with-constraints.js +57 -71
  14. package/build/color-ramps/lib/find-color-with-constraints.js.map +3 -3
  15. package/build/color-ramps/lib/index.js +25 -68
  16. package/build/color-ramps/lib/index.js.map +3 -3
  17. package/build/color-ramps/lib/ramp-configs.js +12 -11
  18. package/build/color-ramps/lib/ramp-configs.js.map +1 -1
  19. package/build/color-ramps/lib/register-color-spaces.js +7 -0
  20. package/build/color-ramps/lib/register-color-spaces.js.map +7 -0
  21. package/build/color-ramps/lib/taper-chroma.js +35 -27
  22. package/build/color-ramps/lib/taper-chroma.js.map +3 -3
  23. package/build/color-ramps/lib/types.js +2 -1
  24. package/build/color-ramps/lib/types.js.map +1 -1
  25. package/build/color-ramps/lib/utils.js +12 -9
  26. package/build/color-ramps/lib/utils.js.map +2 -2
  27. package/build/context.js +3 -2
  28. package/build/context.js.map +1 -1
  29. package/build/index.js +2 -1
  30. package/build/index.js.map +1 -1
  31. package/build/lock-unlock.js +3 -2
  32. package/build/lock-unlock.js.map +1 -1
  33. package/build/prebuilt/js/design-tokens.js +14 -1
  34. package/build/prebuilt/js/design-tokens.js.map +2 -2
  35. package/build/prebuilt/json/figma.json +214 -32
  36. package/build/prebuilt/ts/design-tokens.js +38 -1
  37. package/build/prebuilt/ts/design-tokens.js.map +2 -2
  38. package/build/private-apis.js +3 -2
  39. package/build/private-apis.js.map +1 -1
  40. package/build/theme-provider.js +19 -17
  41. package/build/theme-provider.js.map +4 -4
  42. package/build/types/css-modules.d.js +0 -1
  43. package/build/types.js +2 -1
  44. package/build/types.js.map +1 -1
  45. package/build/use-theme-provider-styles.js +50 -36
  46. package/build/use-theme-provider-styles.js.map +3 -3
  47. package/build-module/color-ramps/index.js +20 -28
  48. package/build-module/color-ramps/index.js.map +2 -2
  49. package/build-module/color-ramps/lib/color-utils.js +19 -0
  50. package/build-module/color-ramps/lib/color-utils.js.map +7 -0
  51. package/build-module/color-ramps/lib/constants.js +13 -10
  52. package/build-module/color-ramps/lib/constants.js.map +2 -2
  53. package/build-module/color-ramps/lib/default-ramps.js +196 -0
  54. package/build-module/color-ramps/lib/default-ramps.js.map +7 -0
  55. package/build-module/color-ramps/lib/find-color-with-constraints.js +56 -60
  56. package/build-module/color-ramps/lib/find-color-with-constraints.js.map +2 -2
  57. package/build-module/color-ramps/lib/index.js +28 -55
  58. package/build-module/color-ramps/lib/index.js.map +3 -3
  59. package/build-module/color-ramps/lib/ramp-configs.js +11 -10
  60. package/build-module/color-ramps/lib/ramp-configs.js.map +1 -1
  61. package/build-module/color-ramps/lib/register-color-spaces.js +7 -0
  62. package/build-module/color-ramps/lib/register-color-spaces.js.map +7 -0
  63. package/build-module/color-ramps/lib/taper-chroma.js +40 -16
  64. package/build-module/color-ramps/lib/taper-chroma.js.map +2 -2
  65. package/build-module/color-ramps/lib/utils.js +7 -4
  66. package/build-module/color-ramps/lib/utils.js.map +2 -2
  67. package/build-module/context.js +2 -1
  68. package/build-module/context.js.map +1 -1
  69. package/build-module/index.js +1 -0
  70. package/build-module/index.js.map +1 -1
  71. package/build-module/lock-unlock.js +2 -1
  72. package/build-module/lock-unlock.js.map +1 -1
  73. package/build-module/prebuilt/js/design-tokens.js +13 -0
  74. package/build-module/prebuilt/js/design-tokens.js.map +2 -2
  75. package/build-module/prebuilt/json/figma.json +214 -32
  76. package/build-module/prebuilt/ts/design-tokens.js +37 -0
  77. package/build-module/prebuilt/ts/design-tokens.js.map +2 -2
  78. package/build-module/private-apis.js +2 -1
  79. package/build-module/private-apis.js.map +1 -1
  80. package/build-module/theme-provider.js +18 -6
  81. package/build-module/theme-provider.js.map +3 -3
  82. package/build-module/use-theme-provider-styles.js +52 -31
  83. package/build-module/use-theme-provider-styles.js.map +2 -2
  84. package/build-types/color-ramps/index.d.ts +9 -16
  85. package/build-types/color-ramps/index.d.ts.map +1 -1
  86. package/build-types/color-ramps/lib/color-utils.d.ts +22 -0
  87. package/build-types/color-ramps/lib/color-utils.d.ts.map +1 -0
  88. package/build-types/color-ramps/lib/constants.d.ts +6 -8
  89. package/build-types/color-ramps/lib/constants.d.ts.map +1 -1
  90. package/build-types/color-ramps/lib/default-ramps.d.ts +7 -0
  91. package/build-types/color-ramps/lib/default-ramps.d.ts.map +1 -0
  92. package/build-types/color-ramps/lib/find-color-with-constraints.d.ts +7 -5
  93. package/build-types/color-ramps/lib/find-color-with-constraints.d.ts.map +1 -1
  94. package/build-types/color-ramps/lib/index.d.ts +5 -2
  95. package/build-types/color-ramps/lib/index.d.ts.map +1 -1
  96. package/build-types/color-ramps/lib/register-color-spaces.d.ts +2 -0
  97. package/build-types/color-ramps/lib/register-color-spaces.d.ts.map +1 -0
  98. package/build-types/color-ramps/lib/taper-chroma.d.ts +7 -3
  99. package/build-types/color-ramps/lib/taper-chroma.d.ts.map +1 -1
  100. package/build-types/color-ramps/lib/utils.d.ts +7 -3
  101. package/build-types/color-ramps/lib/utils.d.ts.map +1 -1
  102. package/build-types/color-ramps/stories/index.story.d.ts.map +1 -1
  103. package/build-types/prebuilt/ts/design-tokens.d.ts.map +1 -1
  104. package/build-types/use-theme-provider-styles.d.ts +4 -0
  105. package/build-types/use-theme-provider-styles.d.ts.map +1 -1
  106. package/docs/ds-tokens.md +34 -0
  107. package/package.json +19 -9
  108. package/src/color-ramps/index.ts +24 -41
  109. package/src/color-ramps/lib/color-utils.ts +34 -0
  110. package/src/color-ramps/lib/constants.ts +8 -6
  111. package/src/color-ramps/lib/default-ramps.ts +200 -0
  112. package/src/color-ramps/lib/find-color-with-constraints.ts +70 -79
  113. package/src/color-ramps/lib/index.ts +27 -61
  114. package/src/color-ramps/lib/register-color-spaces.ts +13 -0
  115. package/src/color-ramps/lib/taper-chroma.ts +47 -19
  116. package/src/color-ramps/lib/utils.ts +8 -9
  117. package/src/color-ramps/stories/index.story.tsx +16 -22
  118. package/src/color-ramps/test/__snapshots__/index.test.ts.snap +96 -96
  119. package/src/color-ramps/test/index.test.ts +27 -15
  120. package/src/prebuilt/css/design-tokens.css +90 -32
  121. package/src/prebuilt/js/design-tokens.js +12 -0
  122. package/src/prebuilt/json/figma.json +214 -32
  123. package/src/prebuilt/ts/design-tokens.ts +36 -0
  124. package/src/use-theme-provider-styles.ts +47 -25
  125. package/tokens/color.json +184 -32
  126. package/tsconfig.bin.json +13 -0
  127. package/tsconfig.bin.tsbuildinfo +1 -0
  128. package/tsconfig.json +6 -4
  129. package/tsconfig.src.json +9 -0
  130. package/tsconfig.src.tsbuildinfo +1 -0
  131. package/bin/build-tokens.js +0 -83
  132. package/build/color-ramps/lib/cache-utils.js +0 -57
  133. package/build/color-ramps/lib/cache-utils.js.map +0 -7
  134. package/build/style.module.css.js +0 -2
  135. package/build-module/color-ramps/lib/cache-utils.js +0 -31
  136. package/build-module/color-ramps/lib/cache-utils.js.map +0 -7
  137. package/build-module/style.module.css.js +0 -1
  138. package/build-style/style.css +0 -3
  139. package/build-types/color-ramps/lib/cache-utils.d.ts +0 -22
  140. package/build-types/color-ramps/lib/cache-utils.d.ts.map +0 -1
  141. package/src/color-ramps/lib/cache-utils.ts +0 -56
  142. package/tsconfig.tsbuildinfo +0 -1
@@ -1,11 +1,12 @@
1
1
  /**
2
2
  * External dependencies
3
3
  */
4
- import Color from 'colorjs.io';
4
+ import { get, OKLCH, type ColorTypes } from 'colorjs.io/fn';
5
5
 
6
6
  /**
7
7
  * Internal dependencies
8
8
  */
9
+ import './register-color-spaces';
9
10
  import { clampToGamut } from './utils';
10
11
  import {
11
12
  WHITE,
@@ -13,7 +14,7 @@ import {
13
14
  LIGHTNESS_EPSILON,
14
15
  MAX_BISECTION_ITERATIONS,
15
16
  } from './constants';
16
- import { getCachedContrast } from './cache-utils';
17
+ import { getContrast } from './color-utils';
17
18
  import { type TaperChromaOptions, taperChroma } from './taper-chroma';
18
19
 
19
20
  /**
@@ -28,22 +29,20 @@ import { type TaperChromaOptions, taperChroma } from './taper-chroma';
28
29
  * @param direction
29
30
  * @param options
30
31
  * @param options.strict
31
- * @param options.debug
32
32
  * @param options.lightnessConstraint
33
33
  * @param options.lightnessConstraint.type
34
34
  * @param options.lightnessConstraint.value
35
35
  * @param options.taperChromaOptions
36
36
  */
37
37
  export function findColorMeetingRequirements(
38
- reference: Color,
39
- seed: Color,
38
+ reference: ColorTypes,
39
+ seed: ColorTypes,
40
40
  target: number,
41
41
  direction: 'lighter' | 'darker',
42
42
  {
43
43
  lightnessConstraint,
44
44
  taperChromaOptions,
45
45
  strict = true,
46
- debug = false,
47
46
  }: {
48
47
  lightnessConstraint?: {
49
48
  type: 'force' | 'onlyIfSucceeds';
@@ -51,43 +50,42 @@ export function findColorMeetingRequirements(
51
50
  };
52
51
  taperChromaOptions?: TaperChromaOptions;
53
52
  strict?: boolean;
54
- debug?: boolean;
55
53
  } = {}
56
- ): { color: Color; reached: boolean; achieved: number } {
54
+ ): { color: ColorTypes; reached: boolean; achieved: number } {
57
55
  // A target of 1 means same color.
58
56
  // A target lower than 1 doesn't make sense.
59
57
  if ( target <= 1 ) {
60
- return { color: seed.clone(), reached: true, achieved: 1 };
58
+ return { color: seed, reached: true, achieved: 1 };
61
59
  }
62
60
 
63
- if ( lightnessConstraint ) {
64
- // Apply a specific L value.
65
- // Useful when pinning a step to a specific lightness, of to specify
66
- // min/max L values.
67
- let newL = lightnessConstraint.value;
68
- let newC = seed.oklch.c;
61
+ function getColorForL( l: number ): ColorTypes {
62
+ let newL = l;
63
+ let newC = get( seed, [ OKLCH, 'c' ] );
69
64
 
70
65
  if ( taperChromaOptions ) {
71
- ( { l: newL, c: newC } = taperChroma(
72
- seed,
73
- newL,
74
- taperChromaOptions
75
- ) );
66
+ const tapered = taperChroma( seed, newL, taperChromaOptions );
67
+ // taperChroma returns either { l, c } or a ColorObject
68
+ if ( 'l' in tapered && 'c' in tapered ) {
69
+ newL = tapered.l;
70
+ newC = tapered.c;
71
+ } else {
72
+ // It's already a ColorObject, return it directly
73
+ return tapered;
74
+ }
76
75
  }
77
76
 
78
- const colorWithExactL = clampToGamut(
79
- new Color( 'oklch', [ newL, newC, seed.oklch.h ] )
80
- );
81
- const exactLContrast = getCachedContrast( reference, colorWithExactL );
82
-
83
- if ( debug ) {
84
- // eslint-disable-next-line no-console
85
- console.log(
86
- `Succeeded with ${ lightnessConstraint.type } lightness`,
87
- lightnessConstraint.value,
88
- colorWithExactL.oklch.l
89
- );
90
- }
77
+ return clampToGamut( {
78
+ spaceId: 'oklch',
79
+ coords: [ newL, newC, get( seed, [ OKLCH, 'h' ] ) ],
80
+ } );
81
+ }
82
+
83
+ if ( lightnessConstraint ) {
84
+ // Apply a specific L value.
85
+ // Useful when pinning a step to a specific lightness, of to specify
86
+ // min/max L values.
87
+ const colorWithExactL = getColorForL( lightnessConstraint.value );
88
+ const exactLContrast = getContrast( reference, colorWithExactL );
91
89
 
92
90
  // If the L constraint is of "force" type, apply it even when it doesn't
93
91
  // meet the contrast target.
@@ -106,36 +104,24 @@ export function findColorMeetingRequirements(
106
104
  // Set the boundary based on the direction.
107
105
  const mostContrastingL = direction === 'lighter' ? 1 : 0;
108
106
  const mostContrastingColor = direction === 'lighter' ? WHITE : BLACK;
109
- const highestPossibleContrast = getCachedContrast(
110
- reference,
111
- mostContrastingColor
112
- );
107
+ const highestContrast = getContrast( reference, mostContrastingColor );
113
108
 
114
109
  // If even the most contrasting color can't reach the target,
115
110
  // the target is unreachable.
116
- if ( highestPossibleContrast < target ) {
111
+ if ( highestContrast < target ) {
117
112
  if ( strict ) {
118
113
  throw new Error(
119
114
  `Contrast target ${ target.toFixed(
120
115
  2
121
- ) }:1 unreachable in ${ direction } direction against ${ mostContrastingColor.toString() }` +
122
- `(boundary achieves ${ highestPossibleContrast.toFixed(
123
- 3
124
- ) }:1).`
116
+ ) }:1 unreachable in ${ direction } direction` +
117
+ `(boundary achieves ${ highestContrast.toFixed( 3 ) }:1).`
125
118
  );
126
119
  }
127
120
 
128
- if ( debug ) {
129
- // eslint-disable-next-line no-console
130
- console.log(
131
- 'Did not succeeded because it reached the limit',
132
- mostContrastingL
133
- );
134
- }
135
121
  return {
136
122
  color: mostContrastingColor,
137
123
  reached: false,
138
- achieved: highestPossibleContrast,
124
+ achieved: highestContrast,
139
125
  };
140
126
  }
141
127
 
@@ -143,48 +129,53 @@ export function findColorMeetingRequirements(
143
129
  // Originally this was seed.oklch.l — although it's an assumption that works
144
130
  // only when we know for sure the direction of the search.
145
131
  // TODO: can we bring this back to seed.oklch.l ?
146
- let worseL = reference.oklch.l;
132
+ let worseL = get( reference, [ OKLCH, 'l' ] );
133
+ let worseContrast = 1;
134
+ let replacedWorse = false;
147
135
  let betterL = mostContrastingL;
136
+ let betterContrast = highestContrast;
137
+ let replacedBetter = false;
148
138
 
149
- let bestContrastFound = highestPossibleContrast;
150
- let resultingColor = mostContrastingColor;
139
+ let bestColor: ColorTypes = mostContrastingColor;
140
+ let bestContrast = highestContrast;
151
141
 
152
- for (
153
- let i = 0;
154
- i < MAX_BISECTION_ITERATIONS &&
155
- Math.abs( betterL - worseL ) > LIGHTNESS_EPSILON;
156
- i++
157
- ) {
158
- let newL = ( worseL + betterL ) / 2;
159
- let newC = seed.oklch.c;
142
+ for ( let i = 0; i < MAX_BISECTION_ITERATIONS; i++ ) {
143
+ // Linear interpolation between worse and better L values, weighted by the contrast difference.
144
+ const newL =
145
+ ( worseL * ( betterContrast - target ) -
146
+ betterL * ( worseContrast - target ) ) /
147
+ ( betterContrast - worseContrast );
160
148
 
161
- if ( taperChromaOptions ) {
162
- ( { l: newL, c: newC } = taperChroma(
163
- seed,
164
- newL,
165
- taperChromaOptions
166
- ) );
167
- }
149
+ bestColor = getColorForL( newL );
150
+ bestContrast = getContrast( reference, bestColor );
168
151
 
169
- const newColor = clampToGamut(
170
- new Color( 'oklch', [ newL, newC, seed.oklch.h ] )
171
- );
172
- const newContrast = getCachedContrast( reference, newColor );
152
+ if ( Math.abs( bestContrast - target ) <= LIGHTNESS_EPSILON ) {
153
+ break;
154
+ }
173
155
 
174
- if ( newContrast >= target ) {
156
+ // Update one of the boundary L values, using the Illinois method.
157
+ if ( bestContrast >= target ) {
175
158
  betterL = newL;
176
- // Only update the resulting color when the target is met, this ensuring
177
- // at the end of the search the target is always met.
178
- bestContrastFound = newContrast;
179
- resultingColor = newColor;
159
+ betterContrast = bestContrast;
160
+ if ( replacedBetter ) {
161
+ worseContrast = ( worseContrast + target ) / 2;
162
+ }
163
+ replacedBetter = true;
164
+ replacedWorse = false;
180
165
  } else {
181
166
  worseL = newL;
167
+ worseContrast = bestContrast;
168
+ if ( replacedWorse ) {
169
+ betterContrast = ( betterContrast + target ) / 2;
170
+ }
171
+ replacedWorse = true;
172
+ replacedBetter = false;
182
173
  }
183
174
  }
184
175
 
185
176
  return {
186
- color: resultingColor,
177
+ color: bestColor,
187
178
  reached: true,
188
- achieved: bestContrastFound,
179
+ achieved: bestContrast,
189
180
  };
190
181
  }
@@ -1,12 +1,21 @@
1
1
  /**
2
2
  * External dependencies
3
3
  */
4
- import Color from 'colorjs.io';
4
+ import {
5
+ clone,
6
+ get,
7
+ OKLCH,
8
+ parse,
9
+ set,
10
+ type ColorTypes,
11
+ type PlainColorObject,
12
+ } from 'colorjs.io/fn';
5
13
 
6
14
  /**
7
15
  * Internal dependencies
8
16
  */
9
- import { getCachedContrast, getColorString } from './cache-utils';
17
+ import './register-color-spaces';
18
+ import { getContrast, getColorString } from './color-utils';
10
19
  import { findColorMeetingRequirements } from './find-color-with-constraints';
11
20
  import {
12
21
  clampToGamut,
@@ -36,7 +45,6 @@ import { LIGHTNESS_EPSILON, MAX_BISECTION_ITERATIONS } from './constants';
36
45
  * @param params.pinLightness - Optional lightness override for a given step
37
46
  * @param params.pinLightness.stepName
38
47
  * @param params.pinLightness.value
39
- * @param params.debug
40
48
  * @return Object containing ramp results and satisfaction status
41
49
  */
42
50
  function calculateRamp( {
@@ -46,9 +54,8 @@ function calculateRamp( {
46
54
  mainDir,
47
55
  oppDir,
48
56
  pinLightness,
49
- debug = false,
50
57
  }: {
51
- seed: Color;
58
+ seed: ColorTypes;
52
59
  sortedSteps: ( keyof Ramp )[];
53
60
  config: RampConfig;
54
61
  mainDir: RampDirection;
@@ -57,7 +64,6 @@ function calculateRamp( {
57
64
  stepName: keyof Ramp;
58
65
  value: number;
59
66
  };
60
- debug?: boolean;
61
67
  } ) {
62
68
  const rampResults = {} as Record<
63
69
  keyof Ramp,
@@ -69,7 +75,7 @@ function calculateRamp( {
69
75
 
70
76
  // Keep track of the calculated colors, as they are going to be useful
71
77
  // when other colors reference them.
72
- const calculatedColors = new Map< keyof Ramp | 'seed', Color >();
78
+ const calculatedColors = new Map< keyof Ramp | 'seed', ColorTypes >();
73
79
  calculatedColors.set( 'seed', seed );
74
80
 
75
81
  for ( const stepName of sortedSteps ) {
@@ -91,7 +97,7 @@ function calculateRamp( {
91
97
  if ( sameAsIfPossible ) {
92
98
  const candidateColor = calculatedColors.get( sameAsIfPossible );
93
99
  if ( candidateColor ) {
94
- const candidateContrast = getCachedContrast(
100
+ const candidateContrast = getContrast(
95
101
  referenceColor,
96
102
  candidateColor
97
103
  );
@@ -111,7 +117,7 @@ function calculateRamp( {
111
117
  }
112
118
 
113
119
  function computeDirection(
114
- color: Color,
120
+ color: ColorTypes,
115
121
  followDirection: FollowDirection
116
122
  ): RampDirection {
117
123
  if ( followDirection === 'main' ) {
@@ -163,7 +169,6 @@ function calculateRamp( {
163
169
  strict: false,
164
170
  lightnessConstraint,
165
171
  taperChromaOptions,
166
- debug,
167
172
  }
168
173
  );
169
174
 
@@ -179,7 +184,7 @@ function calculateRamp( {
179
184
  // Weight the deficit by how much seed adjustment would help this constraint
180
185
  // If seed has low contrast vs reference, adjusting seed has high impact
181
186
  // If seed has high contrast vs reference, adjusting seed has low impact
182
- const impactWeight = 1 / getCachedContrast( seed, referenceColor );
187
+ const impactWeight = 1 / getContrast( seed, referenceColor );
183
188
  const weightedDeficit = deficitVsTarget * impactWeight;
184
189
 
185
190
  // Track the most impactful failure for seed optimization
@@ -213,7 +218,6 @@ export function buildRamp(
213
218
  {
214
219
  mainDirection,
215
220
  pinLightness,
216
- debug = false,
217
221
  rescaleToFitContrastTargets = true,
218
222
  }: {
219
223
  mainDirection?: RampDirection;
@@ -222,12 +226,11 @@ export function buildRamp(
222
226
  value: number;
223
227
  };
224
228
  rescaleToFitContrastTargets?: boolean;
225
- debug?: boolean;
226
229
  } = {}
227
230
  ): RampResult {
228
- let seed: Color;
231
+ let seed: PlainColorObject;
229
232
  try {
230
- seed = clampToGamut( new Color( seedArg ) );
233
+ seed = clampToGamut( parse( seedArg ) );
231
234
  } catch ( error ) {
232
235
  throw new Error(
233
236
  `Invalid seed color "${ seedArg }": ${
@@ -263,32 +266,17 @@ export function buildRamp(
263
266
  mainDir,
264
267
  oppDir,
265
268
  pinLightness,
266
- debug,
267
269
  } );
268
270
  const toReturn = {
269
271
  ramp: rampResults,
270
272
  direction: mainDir,
271
273
  } as RampResult;
272
274
 
273
- if ( debug ) {
274
- // eslint-disable-next-line no-console
275
- console.log( `First run`, {
276
- SATISFIED_ALL_CONTRAST_REQUIREMENTS,
277
- UNSATISFIED_DIRECTION,
278
- seed: seed.toString(),
279
- sortedSteps,
280
- config,
281
- mainDir,
282
- oppDir,
283
- pinLightness,
284
- } );
285
- }
286
-
287
275
  if (
288
276
  ! SATISFIED_ALL_CONTRAST_REQUIREMENTS &&
289
277
  rescaleToFitContrastTargets
290
278
  ) {
291
- let worseSeedL = seed.oklch.l;
279
+ let worseSeedL = get( seed, [ OKLCH, 'l' ] );
292
280
  // For a scale with the "lighter" direction, the contrast can be improved
293
281
  // by darkening the seed. For "darker" direction, by lightening the seed.
294
282
  let betterSeedL = UNSATISFIED_DIRECTION === 'lighter' ? 0 : 1;
@@ -302,20 +290,13 @@ export function buildRamp(
302
290
  i++
303
291
  ) {
304
292
  const newSeed = clampToGamut(
305
- seed.clone().set( {
306
- l: ( worseSeedL + betterSeedL ) / 2,
307
- } )
293
+ set(
294
+ clone( seed ),
295
+ [ OKLCH, 'l' ],
296
+ ( worseSeedL + betterSeedL ) / 2
297
+ )
308
298
  );
309
299
 
310
- if ( debug ) {
311
- // eslint-disable-next-line no-console
312
- console.log( `Iteration ${ i }`, {
313
- worseSeedL,
314
- newSeedL: ( worseSeedL + betterSeedL ) / 2,
315
- betterSeedL,
316
- } );
317
- }
318
-
319
300
  const iterationResults = calculateRamp( {
320
301
  seed: newSeed,
321
302
  sortedSteps,
@@ -323,35 +304,20 @@ export function buildRamp(
323
304
  mainDir,
324
305
  oppDir,
325
306
  pinLightness,
326
- debug,
327
307
  } );
328
308
 
329
309
  if ( iterationResults.SATISFIED_ALL_CONTRAST_REQUIREMENTS ) {
330
- betterSeedL = newSeed.oklch.l;
310
+ betterSeedL = get( newSeed, [ OKLCH, 'l' ] );
331
311
  // Only update toReturn when the ramp satisfies all constraints.
332
312
  toReturn.ramp = iterationResults.rampResults;
333
313
  } else if ( UNSATISFIED_DIRECTION !== mainDir ) {
334
314
  // Failing constraint is in opposite direction to main ramp direction
335
315
  // We've moved too far in mainDir, constrain the search
336
- betterSeedL = newSeed.oklch.l;
316
+ betterSeedL = get( newSeed, [ OKLCH, 'l' ] );
337
317
  } else {
338
318
  // Failing constraint is in same direction as main ramp direction
339
319
  // We haven't moved far enough in mainDir, continue searching
340
- worseSeedL = newSeed.oklch.l;
341
- }
342
-
343
- if ( debug ) {
344
- // eslint-disable-next-line no-console
345
- console.log( `Retry #${ i }`, {
346
- SATISFIED_ALL_CONTRAST_REQUIREMENTS,
347
- UNSATISFIED_DIRECTION,
348
- seed: newSeed.toString(),
349
- sortedSteps,
350
- config,
351
- mainDir,
352
- oppDir,
353
- pinLightness,
354
- } );
320
+ worseSeedL = get( newSeed, [ OKLCH, 'l' ] );
355
321
  }
356
322
  }
357
323
  }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { ColorSpace, OKLCH, P3, sRGB, HSL } from 'colorjs.io/fn';
5
+
6
+ // Ensures that all color spaces used in color ramps are registered globally, a
7
+ // requirement for using colorjs.io's procedural API.
8
+ //
9
+ // See: https://colorjs.io/docs/procedural
10
+ ColorSpace.register( sRGB );
11
+ ColorSpace.register( OKLCH );
12
+ ColorSpace.register( P3 );
13
+ ColorSpace.register( HSL );
@@ -1,8 +1,20 @@
1
- // npm i colorjs.io
2
1
  /**
3
2
  * External dependencies
4
3
  */
5
- import Color from 'colorjs.io';
4
+ import {
5
+ get,
6
+ inGamut,
7
+ OKLCH,
8
+ P3,
9
+ sRGB,
10
+ type ColorTypes,
11
+ type ColorObject,
12
+ } from 'colorjs.io/fn';
13
+
14
+ /**
15
+ * Internal dependencies
16
+ */
17
+ import './register-color-spaces';
6
18
 
7
19
  export interface TaperChromaOptions {
8
20
  gamut?: 'p3' | 'srgb'; // target gamut (default "p3")
@@ -29,11 +41,12 @@ export interface TaperChromaOptions {
29
41
  * @param options
30
42
  */
31
43
  export function taperChroma(
32
- seed: Color, // already OKLCH
44
+ seed: ColorTypes, // already OKLCH
33
45
  lTarget: number, // [0..1]
34
46
  options: TaperChromaOptions = {}
35
- ): { l: number; c: number } {
47
+ ): { l: number; c: number } | ColorObject {
36
48
  const gamut = options.gamut ?? 'p3';
49
+ const gamutSpace = gamut === 'p3' ? P3 : sRGB;
37
50
  const alpha = options.alpha ?? 0.65; // 0.7-0.8 works well for accent surface
38
51
  const carry = options.carry ?? 0.5;
39
52
  const cUpperBound = options.cUpperBound ?? 0.45;
@@ -43,8 +56,8 @@ export function taperChroma(
43
56
  const kDark = options.kDark ?? 0.85;
44
57
  const achromaEpsilon = options.achromaEpsilon ?? 0.005;
45
58
 
46
- const cSeed = Math.max( 0, seed.oklch.c );
47
- let hSeed = Number( seed.oklch.h );
59
+ const cSeed = Math.max( 0, get( seed, [ OKLCH, 'c' ] ) );
60
+ let hSeed = Number( get( seed, [ OKLCH, 'h' ] ) );
48
61
 
49
62
  const chromaIsTiny = cSeed < achromaEpsilon;
50
63
  const hueIsInvalid = ! Number.isFinite( hSeed );
@@ -54,17 +67,25 @@ export function taperChroma(
54
67
  hSeed = normalizeHue( options.hueFallback );
55
68
  } else {
56
69
  // Respect achromatic intent: grayscale at target L
57
- return new Color( 'oklch', [ clamp01( lTarget ), 0, 0 ] );
70
+ return {
71
+ spaceId: 'oklch',
72
+ coords: [ clamp01( lTarget ), 0, 0 ],
73
+ };
58
74
  }
59
75
  }
60
76
 
61
77
  // Capacity at seed and target
62
- const lSeed = clamp01( seed.oklch.l );
63
- const cmaxSeed = getCachedMaxChromaAtLH( lSeed, hSeed, gamut, cUpperBound );
78
+ const lSeed = clamp01( get( seed, [ OKLCH, 'l' ] ) );
79
+ const cmaxSeed = getCachedMaxChromaAtLH(
80
+ lSeed,
81
+ hSeed,
82
+ gamutSpace,
83
+ cUpperBound
84
+ );
64
85
  const cmaxTarget = getCachedMaxChromaAtLH(
65
86
  clamp01( lTarget ),
66
87
  hSeed,
67
- gamut,
88
+ gamutSpace,
68
89
  cUpperBound
69
90
  );
70
91
 
@@ -89,10 +110,13 @@ export function taperChroma(
89
110
 
90
111
  // Downward-only clamp (preserve L & H)
91
112
  const lOut = clamp01( lTarget );
92
- const candidate = new Color( 'oklch', [ lOut, cPlanned, hSeed ] );
93
- if ( ! candidate.inGamut( gamut ) ) {
113
+ const candidate: ColorTypes = {
114
+ spaceId: 'oklch',
115
+ coords: [ lOut, cPlanned, hSeed ],
116
+ };
117
+ if ( ! inGamut( candidate, gamutSpace ) ) {
94
118
  const cap = Math.min( cPlanned, cUpperBound );
95
- cPlanned = getCachedMaxChromaAtLH( lOut, hSeed, gamut, cap );
119
+ cPlanned = getCachedMaxChromaAtLH( lOut, hSeed, gamutSpace, cap );
96
120
  }
97
121
 
98
122
  cPlanned = Math.min( cPlanned, cSeed );
@@ -178,16 +202,17 @@ function quantize( x: number, step: number ): number {
178
202
  function getCachedMaxChromaAtLH(
179
203
  l: number,
180
204
  h: number,
181
- gamut: 'p3' | 'srgb',
205
+ gamutSpace: typeof P3 | typeof sRGB,
182
206
  cap: number
183
207
  ): number {
208
+ const gamut = gamutSpace === P3 ? 'p3' : 'srgb';
184
209
  const key = keyMax( l, h, gamut, cap );
185
210
  const hit = maxChromaCache.get( key );
186
211
  if ( typeof hit === 'number' ) {
187
212
  return hit;
188
213
  }
189
214
 
190
- const computed = maxInGamutChromaAtLH( l, h, gamut, cap );
215
+ const computed = maxInGamutChromaAtLH( l, h, gamutSpace, cap );
191
216
  maxChromaCache.set( key, computed );
192
217
  return computed;
193
218
  }
@@ -196,13 +221,13 @@ function getCachedMaxChromaAtLH(
196
221
  * Binary-search the max in-gamut chroma at fixed (L,H) in the target gamut
197
222
  * @param l
198
223
  * @param h
199
- * @param gamut
224
+ * @param gamutSpace
200
225
  * @param cap
201
226
  */
202
227
  function maxInGamutChromaAtLH(
203
228
  l: number,
204
229
  h: number,
205
- gamut: 'p3' | 'srgb',
230
+ gamutSpace: typeof P3 | typeof sRGB,
206
231
  cap: number
207
232
  ): number {
208
233
  let lo = 0;
@@ -214,8 +239,11 @@ function maxInGamutChromaAtLH(
214
239
 
215
240
  for ( let i = 0; i < 18; i++ ) {
216
241
  const mid = ( lo + hi ) / 2;
217
- const probe = new Color( 'oklch', [ lFixed, mid, hFixed ] );
218
- if ( probe.inGamut( gamut ) ) {
242
+ const probe: ColorTypes = {
243
+ spaceId: 'oklch',
244
+ coords: [ lFixed, mid, hFixed ],
245
+ };
246
+ if ( inGamut( probe, gamutSpace ) ) {
219
247
  ok = mid;
220
248
  lo = mid;
221
249
  } else {
@@ -1,11 +1,12 @@
1
1
  /**
2
2
  * External dependencies
3
3
  */
4
- import type Color from 'colorjs.io';
4
+ import { toGamut, to, P3, OKLCH, type ColorTypes } from 'colorjs.io/fn';
5
5
 
6
6
  /**
7
7
  * Internal dependencies
8
8
  */
9
+ import './register-color-spaces';
9
10
  import {
10
11
  WHITE,
11
12
  BLACK,
@@ -14,16 +15,14 @@ import {
14
15
  ACCENT_SCALE_BASE_LIGHTNESS_THRESHOLDS,
15
16
  } from './constants';
16
17
  import type { Ramp, RampStepConfig, RampDirection } from './types';
17
- import { getCachedContrast } from './cache-utils';
18
+ import { getContrast } from './color-utils';
18
19
 
19
20
  /**
20
21
  * Make sure that a color is valid in the p3 gamut, and converts it to oklch.
21
22
  * @param c
22
23
  */
23
- export const clampToGamut = ( c: Color ) =>
24
- c
25
- .toGamut( { space: 'p3', method: 'css' } ) // map into Display-P3 using CSS OKLCH method
26
- .to( 'oklch' );
24
+ export const clampToGamut = ( c: ColorTypes ) =>
25
+ to( toGamut( c, { space: P3, method: 'css' } ), OKLCH ); // map into Display-P3 using CSS OKLCH method
27
26
 
28
27
  /**
29
28
  * Build a dependency graph from the steps configuration
@@ -119,14 +118,14 @@ export function sortByDependency(
119
118
  * ramp direction value.
120
119
  */
121
120
  export function computeBetterFgColorDirection(
122
- seed: Color,
121
+ seed: ColorTypes,
123
122
  preferLighter?: boolean
124
123
  ): {
125
124
  better: RampDirection;
126
125
  worse: RampDirection;
127
126
  } {
128
- const contrastAgainstBlack = getCachedContrast( seed, BLACK );
129
- const contrastAgainstWhite = getCachedContrast( seed, WHITE );
127
+ const contrastAgainstBlack = getContrast( seed, BLACK );
128
+ const contrastAgainstWhite = getContrast( seed, WHITE );
130
129
 
131
130
  return contrastAgainstBlack >
132
131
  contrastAgainstWhite +