@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.
- package/README.md +69 -9
- package/bin/generate-default-ramps/index.ts +49 -0
- package/bin/generate-primitive-tokens/index.ts +14 -9
- package/bin/terrazzo-plugin-figma-ds-token-manager/index.ts +11 -4
- package/build/color-ramps/index.js +21 -39
- package/build/color-ramps/index.js.map +3 -3
- package/build/color-ramps/lib/color-utils.js +39 -0
- package/build/color-ramps/lib/color-utils.js.map +7 -0
- package/build/color-ramps/lib/constants.js +18 -25
- package/build/color-ramps/lib/constants.js.map +3 -3
- package/build/color-ramps/lib/default-ramps.js +220 -0
- package/build/color-ramps/lib/default-ramps.js.map +7 -0
- package/build/color-ramps/lib/find-color-with-constraints.js +57 -71
- package/build/color-ramps/lib/find-color-with-constraints.js.map +3 -3
- package/build/color-ramps/lib/index.js +25 -68
- package/build/color-ramps/lib/index.js.map +3 -3
- package/build/color-ramps/lib/ramp-configs.js +12 -11
- package/build/color-ramps/lib/ramp-configs.js.map +1 -1
- package/build/color-ramps/lib/register-color-spaces.js +7 -0
- package/build/color-ramps/lib/register-color-spaces.js.map +7 -0
- package/build/color-ramps/lib/taper-chroma.js +35 -27
- package/build/color-ramps/lib/taper-chroma.js.map +3 -3
- package/build/color-ramps/lib/types.js +2 -1
- package/build/color-ramps/lib/types.js.map +1 -1
- package/build/color-ramps/lib/utils.js +12 -9
- package/build/color-ramps/lib/utils.js.map +2 -2
- package/build/context.js +3 -2
- package/build/context.js.map +1 -1
- package/build/index.js +2 -1
- package/build/index.js.map +1 -1
- package/build/lock-unlock.js +3 -2
- package/build/lock-unlock.js.map +1 -1
- package/build/prebuilt/js/design-tokens.js +14 -1
- package/build/prebuilt/js/design-tokens.js.map +2 -2
- package/build/prebuilt/json/figma.json +214 -32
- package/build/prebuilt/ts/design-tokens.js +38 -1
- package/build/prebuilt/ts/design-tokens.js.map +2 -2
- package/build/private-apis.js +3 -2
- package/build/private-apis.js.map +1 -1
- package/build/theme-provider.js +19 -17
- package/build/theme-provider.js.map +4 -4
- package/build/types/css-modules.d.js +0 -1
- package/build/types.js +2 -1
- package/build/types.js.map +1 -1
- package/build/use-theme-provider-styles.js +50 -36
- package/build/use-theme-provider-styles.js.map +3 -3
- package/build-module/color-ramps/index.js +20 -28
- package/build-module/color-ramps/index.js.map +2 -2
- package/build-module/color-ramps/lib/color-utils.js +19 -0
- package/build-module/color-ramps/lib/color-utils.js.map +7 -0
- package/build-module/color-ramps/lib/constants.js +13 -10
- package/build-module/color-ramps/lib/constants.js.map +2 -2
- package/build-module/color-ramps/lib/default-ramps.js +196 -0
- package/build-module/color-ramps/lib/default-ramps.js.map +7 -0
- package/build-module/color-ramps/lib/find-color-with-constraints.js +56 -60
- package/build-module/color-ramps/lib/find-color-with-constraints.js.map +2 -2
- package/build-module/color-ramps/lib/index.js +28 -55
- package/build-module/color-ramps/lib/index.js.map +3 -3
- package/build-module/color-ramps/lib/ramp-configs.js +11 -10
- package/build-module/color-ramps/lib/ramp-configs.js.map +1 -1
- package/build-module/color-ramps/lib/register-color-spaces.js +7 -0
- package/build-module/color-ramps/lib/register-color-spaces.js.map +7 -0
- package/build-module/color-ramps/lib/taper-chroma.js +40 -16
- package/build-module/color-ramps/lib/taper-chroma.js.map +2 -2
- package/build-module/color-ramps/lib/utils.js +7 -4
- package/build-module/color-ramps/lib/utils.js.map +2 -2
- package/build-module/context.js +2 -1
- package/build-module/context.js.map +1 -1
- package/build-module/index.js +1 -0
- package/build-module/index.js.map +1 -1
- package/build-module/lock-unlock.js +2 -1
- package/build-module/lock-unlock.js.map +1 -1
- package/build-module/prebuilt/js/design-tokens.js +13 -0
- package/build-module/prebuilt/js/design-tokens.js.map +2 -2
- package/build-module/prebuilt/json/figma.json +214 -32
- package/build-module/prebuilt/ts/design-tokens.js +37 -0
- package/build-module/prebuilt/ts/design-tokens.js.map +2 -2
- package/build-module/private-apis.js +2 -1
- package/build-module/private-apis.js.map +1 -1
- package/build-module/theme-provider.js +18 -6
- package/build-module/theme-provider.js.map +3 -3
- package/build-module/use-theme-provider-styles.js +52 -31
- package/build-module/use-theme-provider-styles.js.map +2 -2
- package/build-types/color-ramps/index.d.ts +9 -16
- package/build-types/color-ramps/index.d.ts.map +1 -1
- package/build-types/color-ramps/lib/color-utils.d.ts +22 -0
- package/build-types/color-ramps/lib/color-utils.d.ts.map +1 -0
- package/build-types/color-ramps/lib/constants.d.ts +6 -8
- package/build-types/color-ramps/lib/constants.d.ts.map +1 -1
- package/build-types/color-ramps/lib/default-ramps.d.ts +7 -0
- package/build-types/color-ramps/lib/default-ramps.d.ts.map +1 -0
- package/build-types/color-ramps/lib/find-color-with-constraints.d.ts +7 -5
- package/build-types/color-ramps/lib/find-color-with-constraints.d.ts.map +1 -1
- package/build-types/color-ramps/lib/index.d.ts +5 -2
- package/build-types/color-ramps/lib/index.d.ts.map +1 -1
- package/build-types/color-ramps/lib/register-color-spaces.d.ts +2 -0
- package/build-types/color-ramps/lib/register-color-spaces.d.ts.map +1 -0
- package/build-types/color-ramps/lib/taper-chroma.d.ts +7 -3
- package/build-types/color-ramps/lib/taper-chroma.d.ts.map +1 -1
- package/build-types/color-ramps/lib/utils.d.ts +7 -3
- package/build-types/color-ramps/lib/utils.d.ts.map +1 -1
- package/build-types/color-ramps/stories/index.story.d.ts.map +1 -1
- package/build-types/prebuilt/ts/design-tokens.d.ts.map +1 -1
- package/build-types/use-theme-provider-styles.d.ts +4 -0
- package/build-types/use-theme-provider-styles.d.ts.map +1 -1
- package/docs/ds-tokens.md +34 -0
- package/package.json +19 -9
- package/src/color-ramps/index.ts +24 -41
- package/src/color-ramps/lib/color-utils.ts +34 -0
- package/src/color-ramps/lib/constants.ts +8 -6
- package/src/color-ramps/lib/default-ramps.ts +200 -0
- package/src/color-ramps/lib/find-color-with-constraints.ts +70 -79
- package/src/color-ramps/lib/index.ts +27 -61
- package/src/color-ramps/lib/register-color-spaces.ts +13 -0
- package/src/color-ramps/lib/taper-chroma.ts +47 -19
- package/src/color-ramps/lib/utils.ts +8 -9
- package/src/color-ramps/stories/index.story.tsx +16 -22
- package/src/color-ramps/test/__snapshots__/index.test.ts.snap +96 -96
- package/src/color-ramps/test/index.test.ts +27 -15
- package/src/prebuilt/css/design-tokens.css +90 -32
- package/src/prebuilt/js/design-tokens.js +12 -0
- package/src/prebuilt/json/figma.json +214 -32
- package/src/prebuilt/ts/design-tokens.ts +36 -0
- package/src/use-theme-provider-styles.ts +47 -25
- package/tokens/color.json +184 -32
- package/tsconfig.bin.json +13 -0
- package/tsconfig.bin.tsbuildinfo +1 -0
- package/tsconfig.json +6 -4
- package/tsconfig.src.json +9 -0
- package/tsconfig.src.tsbuildinfo +1 -0
- package/bin/build-tokens.js +0 -83
- package/build/color-ramps/lib/cache-utils.js +0 -57
- package/build/color-ramps/lib/cache-utils.js.map +0 -7
- package/build/style.module.css.js +0 -2
- package/build-module/color-ramps/lib/cache-utils.js +0 -31
- package/build-module/color-ramps/lib/cache-utils.js.map +0 -7
- package/build-module/style.module.css.js +0 -1
- package/build-style/style.css +0 -3
- package/build-types/color-ramps/lib/cache-utils.d.ts +0 -22
- package/build-types/color-ramps/lib/cache-utils.d.ts.map +0 -1
- package/src/color-ramps/lib/cache-utils.ts +0 -56
- package/tsconfig.tsbuildinfo +0 -1
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* External dependencies
|
|
3
3
|
*/
|
|
4
|
-
import
|
|
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 {
|
|
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:
|
|
39
|
-
seed:
|
|
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:
|
|
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
|
|
58
|
+
return { color: seed, reached: true, achieved: 1 };
|
|
61
59
|
}
|
|
62
60
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
|
-
(
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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
|
|
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 (
|
|
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
|
|
122
|
-
`(boundary achieves ${
|
|
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:
|
|
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
|
|
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
|
|
150
|
-
let
|
|
139
|
+
let bestColor: ColorTypes = mostContrastingColor;
|
|
140
|
+
let bestContrast = highestContrast;
|
|
151
141
|
|
|
152
|
-
for (
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
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
|
-
|
|
162
|
-
|
|
163
|
-
seed,
|
|
164
|
-
newL,
|
|
165
|
-
taperChromaOptions
|
|
166
|
-
) );
|
|
167
|
-
}
|
|
149
|
+
bestColor = getColorForL( newL );
|
|
150
|
+
bestContrast = getContrast( reference, bestColor );
|
|
168
151
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
const newContrast = getCachedContrast( reference, newColor );
|
|
152
|
+
if ( Math.abs( bestContrast - target ) <= LIGHTNESS_EPSILON ) {
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
173
155
|
|
|
174
|
-
|
|
156
|
+
// Update one of the boundary L values, using the Illinois method.
|
|
157
|
+
if ( bestContrast >= target ) {
|
|
175
158
|
betterL = newL;
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
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:
|
|
177
|
+
color: bestColor,
|
|
187
178
|
reached: true,
|
|
188
|
-
achieved:
|
|
179
|
+
achieved: bestContrast,
|
|
189
180
|
};
|
|
190
181
|
}
|
|
@@ -1,12 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* External dependencies
|
|
3
3
|
*/
|
|
4
|
-
import
|
|
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
|
|
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:
|
|
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',
|
|
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 =
|
|
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:
|
|
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 /
|
|
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:
|
|
231
|
+
let seed: PlainColorObject;
|
|
229
232
|
try {
|
|
230
|
-
seed = clampToGamut(
|
|
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
|
|
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
|
-
|
|
306
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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:
|
|
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
|
|
47
|
-
let hSeed = Number( seed
|
|
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
|
|
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
|
|
63
|
-
const cmaxSeed = getCachedMaxChromaAtLH(
|
|
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
|
-
|
|
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 =
|
|
93
|
-
|
|
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,
|
|
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
|
-
|
|
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,
|
|
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
|
|
224
|
+
* @param gamutSpace
|
|
200
225
|
* @param cap
|
|
201
226
|
*/
|
|
202
227
|
function maxInGamutChromaAtLH(
|
|
203
228
|
l: number,
|
|
204
229
|
h: number,
|
|
205
|
-
|
|
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 =
|
|
218
|
-
|
|
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
|
|
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 {
|
|
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:
|
|
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:
|
|
121
|
+
seed: ColorTypes,
|
|
123
122
|
preferLighter?: boolean
|
|
124
123
|
): {
|
|
125
124
|
better: RampDirection;
|
|
126
125
|
worse: RampDirection;
|
|
127
126
|
} {
|
|
128
|
-
const contrastAgainstBlack =
|
|
129
|
-
const contrastAgainstWhite =
|
|
127
|
+
const contrastAgainstBlack = getContrast( seed, BLACK );
|
|
128
|
+
const contrastAgainstWhite = getContrast( seed, WHITE );
|
|
130
129
|
|
|
131
130
|
return contrastAgainstBlack >
|
|
132
131
|
contrastAgainstWhite +
|