@wordpress/theme 0.1.0 → 0.2.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 (168) 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-ds-tokens-docs/index.ts +5 -24
  5. package/bin/terrazzo-plugin-figma-ds-token-manager/index.ts +11 -4
  6. package/bin/terrazzo-plugin-inline-alias-values/index.ts +84 -0
  7. package/bin/terrazzo-plugin-known-wpds-css-variables/index.ts +19 -39
  8. package/build/color-ramps/index.js +21 -39
  9. package/build/color-ramps/index.js.map +3 -3
  10. package/build/color-ramps/lib/color-utils.js +39 -0
  11. package/build/color-ramps/lib/color-utils.js.map +7 -0
  12. package/build/color-ramps/lib/constants.js +20 -27
  13. package/build/color-ramps/lib/constants.js.map +3 -3
  14. package/build/color-ramps/lib/default-ramps.js +220 -0
  15. package/build/color-ramps/lib/default-ramps.js.map +7 -0
  16. package/build/color-ramps/lib/find-color-with-constraints.js +60 -91
  17. package/build/color-ramps/lib/find-color-with-constraints.js.map +3 -3
  18. package/build/color-ramps/lib/index.js +77 -119
  19. package/build/color-ramps/lib/index.js.map +3 -3
  20. package/build/color-ramps/lib/ramp-configs.js +15 -14
  21. package/build/color-ramps/lib/ramp-configs.js.map +2 -2
  22. package/build/color-ramps/lib/register-color-spaces.js +7 -0
  23. package/build/color-ramps/lib/register-color-spaces.js.map +7 -0
  24. package/build/color-ramps/lib/taper-chroma.js +35 -27
  25. package/build/color-ramps/lib/taper-chroma.js.map +3 -3
  26. package/build/color-ramps/lib/types.js +2 -1
  27. package/build/color-ramps/lib/types.js.map +1 -1
  28. package/build/color-ramps/lib/utils.js +75 -11
  29. package/build/color-ramps/lib/utils.js.map +2 -2
  30. package/build/context.js +3 -2
  31. package/build/context.js.map +1 -1
  32. package/build/index.js +2 -1
  33. package/build/index.js.map +1 -1
  34. package/build/lock-unlock.js +3 -2
  35. package/build/lock-unlock.js.map +1 -1
  36. package/build/prebuilt/js/design-tokens.js +19 -11
  37. package/build/prebuilt/js/design-tokens.js.map +2 -2
  38. package/build/prebuilt/json/figma.json +165 -783
  39. package/build/prebuilt/ts/color-tokens.js +137 -0
  40. package/build/prebuilt/ts/color-tokens.js.map +7 -0
  41. package/build/private-apis.js +3 -2
  42. package/build/private-apis.js.map +1 -1
  43. package/build/theme-provider.js +19 -17
  44. package/build/theme-provider.js.map +4 -4
  45. package/build/token-id.js +30 -0
  46. package/build/token-id.js.map +7 -0
  47. package/build/types/css-modules.d.js +0 -1
  48. package/build/types.js +2 -1
  49. package/build/types.js.map +1 -1
  50. package/build/use-theme-provider-styles.js +67 -62
  51. package/build/use-theme-provider-styles.js.map +3 -3
  52. package/build-module/color-ramps/index.js +20 -28
  53. package/build-module/color-ramps/index.js.map +2 -2
  54. package/build-module/color-ramps/lib/color-utils.js +19 -0
  55. package/build-module/color-ramps/lib/color-utils.js.map +7 -0
  56. package/build-module/color-ramps/lib/constants.js +14 -11
  57. package/build-module/color-ramps/lib/constants.js.map +2 -2
  58. package/build-module/color-ramps/lib/default-ramps.js +196 -0
  59. package/build-module/color-ramps/lib/default-ramps.js.map +7 -0
  60. package/build-module/color-ramps/lib/find-color-with-constraints.js +61 -87
  61. package/build-module/color-ramps/lib/find-color-with-constraints.js.map +2 -2
  62. package/build-module/color-ramps/lib/index.js +85 -109
  63. package/build-module/color-ramps/lib/index.js.map +3 -3
  64. package/build-module/color-ramps/lib/ramp-configs.js +14 -13
  65. package/build-module/color-ramps/lib/ramp-configs.js.map +2 -2
  66. package/build-module/color-ramps/lib/register-color-spaces.js +7 -0
  67. package/build-module/color-ramps/lib/register-color-spaces.js.map +7 -0
  68. package/build-module/color-ramps/lib/taper-chroma.js +40 -16
  69. package/build-module/color-ramps/lib/taper-chroma.js.map +2 -2
  70. package/build-module/color-ramps/lib/utils.js +70 -6
  71. package/build-module/color-ramps/lib/utils.js.map +2 -2
  72. package/build-module/context.js +2 -1
  73. package/build-module/context.js.map +1 -1
  74. package/build-module/index.js +1 -0
  75. package/build-module/index.js.map +1 -1
  76. package/build-module/lock-unlock.js +2 -1
  77. package/build-module/lock-unlock.js.map +1 -1
  78. package/build-module/prebuilt/js/design-tokens.js +18 -10
  79. package/build-module/prebuilt/js/design-tokens.js.map +2 -2
  80. package/build-module/prebuilt/json/figma.json +165 -783
  81. package/build-module/prebuilt/ts/color-tokens.js +117 -0
  82. package/build-module/prebuilt/ts/color-tokens.js.map +7 -0
  83. package/build-module/private-apis.js +2 -1
  84. package/build-module/private-apis.js.map +1 -1
  85. package/build-module/theme-provider.js +18 -6
  86. package/build-module/theme-provider.js.map +3 -3
  87. package/build-module/token-id.js +6 -0
  88. package/build-module/token-id.js.map +7 -0
  89. package/build-module/use-theme-provider-styles.js +69 -57
  90. package/build-module/use-theme-provider-styles.js.map +2 -2
  91. package/build-types/color-ramps/index.d.ts +9 -16
  92. package/build-types/color-ramps/index.d.ts.map +1 -1
  93. package/build-types/color-ramps/lib/color-utils.d.ts +22 -0
  94. package/build-types/color-ramps/lib/color-utils.d.ts.map +1 -0
  95. package/build-types/color-ramps/lib/constants.d.ts +7 -9
  96. package/build-types/color-ramps/lib/constants.d.ts.map +1 -1
  97. package/build-types/color-ramps/lib/default-ramps.d.ts +7 -0
  98. package/build-types/color-ramps/lib/default-ramps.d.ts.map +1 -0
  99. package/build-types/color-ramps/lib/find-color-with-constraints.d.ts +8 -7
  100. package/build-types/color-ramps/lib/find-color-with-constraints.d.ts.map +1 -1
  101. package/build-types/color-ramps/lib/index.d.ts +5 -2
  102. package/build-types/color-ramps/lib/index.d.ts.map +1 -1
  103. package/build-types/color-ramps/lib/register-color-spaces.d.ts +2 -0
  104. package/build-types/color-ramps/lib/register-color-spaces.d.ts.map +1 -0
  105. package/build-types/color-ramps/lib/taper-chroma.d.ts +7 -3
  106. package/build-types/color-ramps/lib/taper-chroma.d.ts.map +1 -1
  107. package/build-types/color-ramps/lib/utils.d.ts +28 -5
  108. package/build-types/color-ramps/lib/utils.d.ts.map +1 -1
  109. package/build-types/color-ramps/stories/index.story.d.ts.map +1 -1
  110. package/build-types/prebuilt/ts/color-tokens.d.ts +7 -0
  111. package/build-types/prebuilt/ts/color-tokens.d.ts.map +1 -0
  112. package/build-types/stories/index.story.d.ts.map +1 -1
  113. package/build-types/theme-provider.d.ts.map +1 -1
  114. package/build-types/token-id.d.ts +9 -0
  115. package/build-types/token-id.d.ts.map +1 -0
  116. package/build-types/use-theme-provider-styles.d.ts +4 -0
  117. package/build-types/use-theme-provider-styles.d.ts.map +1 -1
  118. package/docs/ds-tokens.md +22 -156
  119. package/package.json +19 -9
  120. package/src/color-ramps/index.ts +24 -41
  121. package/src/color-ramps/lib/color-utils.ts +34 -0
  122. package/src/color-ramps/lib/constants.ts +13 -9
  123. package/src/color-ramps/lib/default-ramps.ts +200 -0
  124. package/src/color-ramps/lib/find-color-with-constraints.ts +83 -116
  125. package/src/color-ramps/lib/index.ts +107 -145
  126. package/src/color-ramps/lib/ramp-configs.ts +3 -3
  127. package/src/color-ramps/lib/register-color-spaces.ts +13 -0
  128. package/src/color-ramps/lib/taper-chroma.ts +47 -19
  129. package/src/color-ramps/lib/utils.ts +117 -14
  130. package/src/color-ramps/stories/index.story.tsx +16 -22
  131. package/src/color-ramps/test/__snapshots__/index.test.ts.snap +45722 -376
  132. package/src/color-ramps/test/index.test.ts +68 -29
  133. package/src/prebuilt/css/design-tokens.css +88 -355
  134. package/src/prebuilt/js/design-tokens.js +17 -10
  135. package/src/prebuilt/json/figma.json +165 -783
  136. package/src/prebuilt/ts/color-tokens.ts +117 -0
  137. package/src/stories/index.story.tsx +4 -18
  138. package/src/test/token-id.test.ts +12 -0
  139. package/src/token-id.ts +9 -0
  140. package/src/use-theme-provider-styles.ts +67 -60
  141. package/terrazzo.config.ts +15 -12
  142. package/tokens/color.json +221 -69
  143. package/tokens/dimension.json +75 -0
  144. package/tsconfig.bin.json +13 -0
  145. package/tsconfig.bin.tsbuildinfo +1 -0
  146. package/tsconfig.json +6 -4
  147. package/tsconfig.src.json +9 -0
  148. package/tsconfig.src.tsbuildinfo +1 -0
  149. package/bin/build-tokens.js +0 -83
  150. package/build/color-ramps/lib/cache-utils.js +0 -57
  151. package/build/color-ramps/lib/cache-utils.js.map +0 -7
  152. package/build/prebuilt/ts/design-tokens.js +0 -354
  153. package/build/prebuilt/ts/design-tokens.js.map +0 -7
  154. package/build/style.module.css.js +0 -2
  155. package/build-module/color-ramps/lib/cache-utils.js +0 -31
  156. package/build-module/color-ramps/lib/cache-utils.js.map +0 -7
  157. package/build-module/prebuilt/ts/design-tokens.js +0 -334
  158. package/build-module/prebuilt/ts/design-tokens.js.map +0 -7
  159. package/build-module/style.module.css.js +0 -1
  160. package/build-style/style.css +0 -3
  161. package/build-types/color-ramps/lib/cache-utils.d.ts +0 -22
  162. package/build-types/color-ramps/lib/cache-utils.d.ts.map +0 -1
  163. package/build-types/prebuilt/ts/design-tokens.d.ts +0 -7
  164. package/build-types/prebuilt/ts/design-tokens.d.ts.map +0 -1
  165. package/src/color-ramps/lib/cache-utils.ts +0 -56
  166. package/src/prebuilt/ts/design-tokens.ts +0 -335
  167. package/tokens/spacing.json +0 -45
  168. package/tsconfig.tsbuildinfo +0 -1
@@ -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,35 +1,36 @@
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,
12
13
  UNIVERSAL_CONTRAST_TOPUP,
13
14
  WHITE_TEXT_CONTRAST_MARGIN,
14
15
  ACCENT_SCALE_BASE_LIGHTNESS_THRESHOLDS,
16
+ MAX_BISECTION_ITERATIONS,
17
+ CONTRAST_EPSILON,
15
18
  } from './constants';
16
- import type { Ramp, RampStepConfig, RampDirection } from './types';
17
- import { getCachedContrast } from './cache-utils';
19
+ import type { Ramp, RampConfig, RampDirection } from './types';
20
+ import { getContrast } from './color-utils';
18
21
 
19
22
  /**
20
23
  * Make sure that a color is valid in the p3 gamut, and converts it to oklch.
21
24
  * @param c
22
25
  */
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' );
26
+ export const clampToGamut = ( c: ColorTypes ) =>
27
+ to( toGamut( c, { space: P3, method: 'css' } ), OKLCH ); // map into Display-P3 using CSS OKLCH method
27
28
 
28
29
  /**
29
30
  * Build a dependency graph from the steps configuration
30
31
  * @param config - The steps configuration object
31
32
  */
32
- function buildDependencyGraph( config: Record< keyof Ramp, RampStepConfig > ): {
33
+ function buildDependencyGraph( config: RampConfig ): {
33
34
  dependencies: Map< keyof Ramp, ( keyof Ramp | 'seed' )[] >;
34
35
  dependents: Map< keyof Ramp | 'seed', ( keyof Ramp )[] >;
35
36
  } {
@@ -67,9 +68,7 @@ function buildDependencyGraph( config: Record< keyof Ramp, RampStepConfig > ): {
67
68
  * Topologically sort steps based on their dependencies
68
69
  * @param config - The steps configuration object
69
70
  */
70
- export function sortByDependency(
71
- config: Record< keyof Ramp, RampStepConfig >
72
- ): ( keyof Ramp )[] {
71
+ export function sortByDependency( config: RampConfig ): ( keyof Ramp )[] {
73
72
  const { dependents } = buildDependencyGraph( config );
74
73
  const result: ( keyof Ramp )[] = [];
75
74
  const visited = new Set< keyof Ramp | 'seed' >();
@@ -109,6 +108,37 @@ export function sortByDependency(
109
108
 
110
109
  return result;
111
110
  }
111
+ /**
112
+ * Return minimal set of steps that are needed to calculate `stepName` from the seed.
113
+ * @param stepName Name of the step.
114
+ * @param config Configuration of the ramp.
115
+ * @return Array of steps that `stepName` depends on.
116
+ */
117
+ export function stepsForStep(
118
+ stepName: keyof Ramp,
119
+ config: RampConfig
120
+ ): ( keyof Ramp )[] {
121
+ const result = new Set< keyof Ramp >();
122
+ function visit( step: keyof Ramp | 'seed' ) {
123
+ if ( step === 'seed' || result.has( step ) ) {
124
+ return;
125
+ }
126
+
127
+ const stepConfig = config[ step ];
128
+ if ( ! stepConfig ) {
129
+ return;
130
+ }
131
+
132
+ visit( stepConfig.contrast.reference );
133
+ if ( stepConfig.sameAsIfPossible ) {
134
+ visit( stepConfig.sameAsIfPossible );
135
+ }
136
+
137
+ result.add( step );
138
+ }
139
+ visit( stepName );
140
+ return Array.from( result );
141
+ }
112
142
 
113
143
  /**
114
144
  * Finds out whether a lighter or a darker foreground color achieves a better
@@ -119,14 +149,14 @@ export function sortByDependency(
119
149
  * ramp direction value.
120
150
  */
121
151
  export function computeBetterFgColorDirection(
122
- seed: Color,
152
+ seed: ColorTypes,
123
153
  preferLighter?: boolean
124
154
  ): {
125
155
  better: RampDirection;
126
156
  worse: RampDirection;
127
157
  } {
128
- const contrastAgainstBlack = getCachedContrast( seed, BLACK );
129
- const contrastAgainstWhite = getCachedContrast( seed, WHITE );
158
+ const contrastAgainstBlack = getContrast( seed, BLACK );
159
+ const contrastAgainstWhite = getContrast( seed, WHITE );
130
160
 
131
161
  return contrastAgainstBlack >
132
162
  contrastAgainstWhite +
@@ -159,3 +189,76 @@ export function clampAccentScaleReferenceLightness(
159
189
  const thresholds = ACCENT_SCALE_BASE_LIGHTNESS_THRESHOLDS[ direction ];
160
190
  return Math.max( thresholds.min, Math.min( thresholds.max, rawLightness ) );
161
191
  }
192
+
193
+ /**
194
+ * Find the value of of `L` (luminance) that produces a `C` (color) that has a
195
+ * `value` (contrast delta) equal to zero.
196
+ * @param calculateC Calculate `C` from a given `L`.
197
+ * @param calculateValue Calculate value (delta) for a given `C`.
198
+ * @param initLowerL Initial lower value of `L`.
199
+ * @param initLowerValue Initial lower delta (negative).
200
+ * @param initUpperL Initial upper value of `L`.
201
+ * @param initUpperValue Initial upper delta (positive).
202
+ * @return Resulting value of type `C`.
203
+ */
204
+ export function solveWithBisect< C >(
205
+ calculateC: ( l: number ) => C,
206
+ calculateValue: ( t: C ) => number,
207
+ initLowerL: number,
208
+ initLowerValue: number,
209
+ initUpperL: number,
210
+ initUpperValue: number
211
+ ): C {
212
+ let lowerL = initLowerL;
213
+ let lowerValue = initLowerValue;
214
+ let lowerReplaced = false;
215
+
216
+ let upperL = initUpperL;
217
+ let upperValue = initUpperValue;
218
+ let upperReplaced = false;
219
+
220
+ let bestC: C;
221
+ let bestValue: number;
222
+ let iterations = 0;
223
+
224
+ while ( true ) {
225
+ iterations++;
226
+
227
+ // Linear interpolation: find the point where a line would cross the zero axis.
228
+ const newL =
229
+ ( lowerL * upperValue - upperL * lowerValue ) /
230
+ ( upperValue - lowerValue );
231
+
232
+ bestC = calculateC( newL );
233
+ bestValue = calculateValue( bestC );
234
+
235
+ if (
236
+ Math.abs( bestValue ) <= CONTRAST_EPSILON ||
237
+ iterations >= MAX_BISECTION_ITERATIONS
238
+ ) {
239
+ break;
240
+ }
241
+
242
+ // Update the lower/upper bracket values. When only one side is repeatedly updated,
243
+ // apply so-called "Illinois trick" for faster convergence: halve the opposite value.
244
+ if ( bestValue <= 0 ) {
245
+ lowerL = newL;
246
+ lowerValue = bestValue;
247
+ if ( lowerReplaced ) {
248
+ upperValue /= 2;
249
+ }
250
+ lowerReplaced = true;
251
+ upperReplaced = false;
252
+ } else {
253
+ upperL = newL;
254
+ upperValue = bestValue;
255
+ if ( upperReplaced ) {
256
+ lowerValue /= 2;
257
+ }
258
+ upperReplaced = true;
259
+ lowerReplaced = false;
260
+ }
261
+ }
262
+
263
+ return bestC;
264
+ }
@@ -44,9 +44,7 @@ export const Default: StoryObj< typeof ColorGen > = {
44
44
  render: ( args ) => {
45
45
  const bgSeed = args.background ?? DEFAULT_SEED_COLORS.bg;
46
46
  const primarySeed = args.primary ?? DEFAULT_SEED_COLORS.primary;
47
- const bgRamp = buildBgRamp( {
48
- seed: bgSeed,
49
- } );
47
+ const bgRamp = buildBgRamp( bgSeed );
50
48
 
51
49
  const bgRampObj = {
52
50
  seed: {
@@ -61,47 +59,42 @@ export const Default: StoryObj< typeof ColorGen > = {
61
59
  name: 'bgFill1' as const,
62
60
  value: primarySeed,
63
61
  },
64
- ramp: buildAccentRamp( { seed: primarySeed, bgRamp } ).ramp,
62
+ ramp: buildAccentRamp( primarySeed, bgRamp ).ramp,
65
63
  };
66
64
  const infoRampObj = {
67
65
  seed: {
68
66
  name: 'bgFill1' as const,
69
67
  value: DEFAULT_SEED_COLORS.info,
70
68
  },
71
- ramp: buildAccentRamp( {
72
- seed: DEFAULT_SEED_COLORS.info,
73
- bgRamp,
74
- } ).ramp,
69
+ ramp: buildAccentRamp( DEFAULT_SEED_COLORS.info, bgRamp ).ramp,
75
70
  };
76
71
  const successRampObj = {
77
72
  seed: {
78
73
  name: 'bgFill1' as const,
79
74
  value: DEFAULT_SEED_COLORS.success,
80
75
  },
81
- ramp: buildAccentRamp( {
82
- seed: DEFAULT_SEED_COLORS.success,
83
- bgRamp,
84
- } ).ramp,
76
+ ramp: buildAccentRamp( DEFAULT_SEED_COLORS.success, bgRamp ).ramp,
85
77
  };
86
78
  const warningRampObj = {
87
79
  seed: {
88
80
  name: 'bgFill1' as const,
89
81
  value: DEFAULT_SEED_COLORS.warning,
90
82
  },
91
- ramp: buildAccentRamp( {
92
- seed: DEFAULT_SEED_COLORS.warning,
93
- bgRamp,
94
- } ).ramp,
83
+ ramp: buildAccentRamp( DEFAULT_SEED_COLORS.warning, bgRamp ).ramp,
84
+ };
85
+ const cautionRampObj = {
86
+ seed: {
87
+ name: 'bgFill1' as const,
88
+ value: DEFAULT_SEED_COLORS.caution,
89
+ },
90
+ ramp: buildAccentRamp( DEFAULT_SEED_COLORS.caution, bgRamp ).ramp,
95
91
  };
96
92
  const errorRampObj = {
97
93
  seed: {
98
94
  name: 'bgFill1' as const,
99
95
  value: DEFAULT_SEED_COLORS.error,
100
96
  },
101
- ramp: buildAccentRamp( {
102
- seed: DEFAULT_SEED_COLORS.error,
103
- bgRamp,
104
- } ).ramp,
97
+ ramp: buildAccentRamp( DEFAULT_SEED_COLORS.error, bgRamp ).ramp,
105
98
  };
106
99
 
107
100
  const unmetTargets = checkAccessibleCombinations( {
@@ -123,6 +116,7 @@ export const Default: StoryObj< typeof ColorGen > = {
123
116
  infoRampObj,
124
117
  successRampObj,
125
118
  warningRampObj,
119
+ cautionRampObj,
126
120
  errorRampObj,
127
121
  ] }
128
122
  />
@@ -222,7 +216,7 @@ export const SampleCombinations: StoryObj< typeof ColorGen > = {
222
216
  ];
223
217
 
224
218
  const ramps = combinations.map( ( { background, primary } ) => {
225
- const bgRamp = buildBgRamp( { seed: background } );
219
+ const bgRamp = buildBgRamp( background );
226
220
 
227
221
  const bgRampObj = {
228
222
  seed: {
@@ -237,7 +231,7 @@ export const SampleCombinations: StoryObj< typeof ColorGen > = {
237
231
  name: 'bgFill1' as const,
238
232
  value: primary,
239
233
  },
240
- ramp: buildAccentRamp( { seed: primary, bgRamp } ).ramp,
234
+ ramp: buildAccentRamp( primary, bgRamp ).ramp,
241
235
  };
242
236
 
243
237
  return [ bgRampObj, primaryRampObj ];