@wordpress/theme 0.1.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/LICENSE.md +788 -0
- package/README.md +67 -0
- package/bin/build-tokens.js +83 -0
- package/bin/generate-primitive-tokens/index.ts +115 -0
- package/bin/terrazzo-plugin-ds-tokens-docs/index.ts +103 -0
- package/bin/terrazzo-plugin-figma-ds-token-manager/index.ts +210 -0
- package/bin/terrazzo-plugin-figma-ds-token-manager/lib.ts +1 -0
- package/bin/terrazzo-plugin-known-wpds-css-variables/index.ts +72 -0
- package/build/color-ramps/index.js +132 -0
- package/build/color-ramps/index.js.map +7 -0
- package/build/color-ramps/lib/cache-utils.js +57 -0
- package/build/color-ramps/lib/cache-utils.js.map +7 -0
- package/build/color-ramps/lib/constants.js +105 -0
- package/build/color-ramps/lib/constants.js.map +7 -0
- package/build/color-ramps/lib/find-color-with-constraints.js +141 -0
- package/build/color-ramps/lib/find-color-with-constraints.js.map +7 -0
- package/build/color-ramps/lib/index.js +264 -0
- package/build/color-ramps/lib/index.js.map +7 -0
- package/build/color-ramps/lib/ramp-configs.js +315 -0
- package/build/color-ramps/lib/ramp-configs.js.map +7 -0
- package/build/color-ramps/lib/taper-chroma.js +159 -0
- package/build/color-ramps/lib/taper-chroma.js.map +7 -0
- package/build/color-ramps/lib/types.js +17 -0
- package/build/color-ramps/lib/types.js.map +7 -0
- package/build/color-ramps/lib/utils.js +106 -0
- package/build/color-ramps/lib/utils.js.map +7 -0
- package/build/context.js +34 -0
- package/build/context.js.map +7 -0
- package/build/index.js +29 -0
- package/build/index.js.map +7 -0
- package/build/lock-unlock.js +35 -0
- package/build/lock-unlock.js.map +7 -0
- package/build/prebuilt/js/design-tokens.js +135 -0
- package/build/prebuilt/js/design-tokens.js.map +7 -0
- package/build/prebuilt/json/figma.json +1317 -0
- package/build/prebuilt/ts/design-tokens.js +354 -0
- package/build/prebuilt/ts/design-tokens.js.map +7 -0
- package/build/private-apis.js +36 -0
- package/build/private-apis.js.map +7 -0
- package/build/style.module.css.js +2 -0
- package/build/theme-provider.js +92 -0
- package/build/theme-provider.js.map +7 -0
- package/build/types/css-modules.d.js +2 -0
- package/build/types/css-modules.d.js.map +7 -0
- package/build/types.js +17 -0
- package/build/types.js.map +7 -0
- package/build/use-theme-provider-styles.js +230 -0
- package/build/use-theme-provider-styles.js.map +7 -0
- package/build-module/color-ramps/index.js +95 -0
- package/build-module/color-ramps/index.js.map +7 -0
- package/build-module/color-ramps/lib/cache-utils.js +31 -0
- package/build-module/color-ramps/lib/cache-utils.js.map +7 -0
- package/build-module/color-ramps/lib/constants.js +63 -0
- package/build-module/color-ramps/lib/constants.js.map +7 -0
- package/build-module/color-ramps/lib/find-color-with-constraints.js +112 -0
- package/build-module/color-ramps/lib/find-color-with-constraints.js.map +7 -0
- package/build-module/color-ramps/lib/index.js +235 -0
- package/build-module/color-ramps/lib/index.js.map +7 -0
- package/build-module/color-ramps/lib/ramp-configs.js +290 -0
- package/build-module/color-ramps/lib/ramp-configs.js.map +7 -0
- package/build-module/color-ramps/lib/taper-chroma.js +125 -0
- package/build-module/color-ramps/lib/taper-chroma.js.map +7 -0
- package/build-module/color-ramps/lib/types.js +1 -0
- package/build-module/color-ramps/lib/types.js.map +7 -0
- package/build-module/color-ramps/lib/utils.js +84 -0
- package/build-module/color-ramps/lib/utils.js.map +7 -0
- package/build-module/context.js +10 -0
- package/build-module/context.js.map +7 -0
- package/build-module/index.js +5 -0
- package/build-module/index.js.map +7 -0
- package/build-module/lock-unlock.js +10 -0
- package/build-module/lock-unlock.js.map +7 -0
- package/build-module/prebuilt/js/design-tokens.js +115 -0
- package/build-module/prebuilt/js/design-tokens.js.map +7 -0
- package/build-module/prebuilt/json/figma.json +1317 -0
- package/build-module/prebuilt/ts/design-tokens.js +334 -0
- package/build-module/prebuilt/ts/design-tokens.js.map +7 -0
- package/build-module/private-apis.js +12 -0
- package/build-module/private-apis.js.map +7 -0
- package/build-module/style.module.css.js +1 -0
- package/build-module/theme-provider.js +58 -0
- package/build-module/theme-provider.js.map +7 -0
- package/build-module/types/css-modules.d.js +1 -0
- package/build-module/types/css-modules.d.js.map +7 -0
- package/build-module/types.js +1 -0
- package/build-module/types.js.map +7 -0
- package/build-module/use-theme-provider-styles.js +200 -0
- package/build-module/use-theme-provider-styles.js.map +7 -0
- package/build-style/style.css +3 -0
- package/build-types/color-ramps/index.d.ts +44 -0
- package/build-types/color-ramps/index.d.ts.map +1 -0
- package/build-types/color-ramps/lib/cache-utils.d.ts +22 -0
- package/build-types/color-ramps/lib/cache-utils.d.ts.map +1 -0
- package/build-types/color-ramps/lib/constants.d.ts +38 -0
- package/build-types/color-ramps/lib/constants.d.ts.map +1 -0
- package/build-types/color-ramps/lib/find-color-with-constraints.d.ts +37 -0
- package/build-types/color-ramps/lib/find-color-with-constraints.d.ts.map +1 -0
- package/build-types/color-ramps/lib/index.d.ts +11 -0
- package/build-types/color-ramps/lib/index.d.ts.map +1 -0
- package/build-types/color-ramps/lib/ramp-configs.d.ts +7 -0
- package/build-types/color-ramps/lib/ramp-configs.d.ts.map +1 -0
- package/build-types/color-ramps/lib/taper-chroma.d.ts +32 -0
- package/build-types/color-ramps/lib/taper-chroma.d.ts.map +1 -0
- package/build-types/color-ramps/lib/types.d.ts +78 -0
- package/build-types/color-ramps/lib/types.d.ts.map +1 -0
- package/build-types/color-ramps/lib/utils.d.ts +38 -0
- package/build-types/color-ramps/lib/utils.d.ts.map +1 -0
- package/build-types/color-ramps/stories/index.story.d.ts +14 -0
- package/build-types/color-ramps/stories/index.story.d.ts.map +1 -0
- package/build-types/color-ramps/stories/ramp-table.d.ts +19 -0
- package/build-types/color-ramps/stories/ramp-table.d.ts.map +1 -0
- package/build-types/context.d.ts +10 -0
- package/build-types/context.d.ts.map +1 -0
- package/build-types/index.d.ts +2 -0
- package/build-types/index.d.ts.map +1 -0
- package/build-types/lock-unlock.d.ts +2 -0
- package/build-types/lock-unlock.d.ts.map +1 -0
- package/build-types/prebuilt/js/design-tokens.d.ts +3 -0
- package/build-types/prebuilt/js/design-tokens.d.ts.map +1 -0
- package/build-types/prebuilt/ts/design-tokens.d.ts +7 -0
- package/build-types/prebuilt/ts/design-tokens.d.ts.map +1 -0
- package/build-types/private-apis.d.ts +2 -0
- package/build-types/private-apis.d.ts.map +1 -0
- package/build-types/stories/index.story.d.ts +15 -0
- package/build-types/stories/index.story.d.ts.map +1 -0
- package/build-types/theme-provider.d.ts +3 -0
- package/build-types/theme-provider.d.ts.map +1 -0
- package/build-types/types.d.ts +42 -0
- package/build-types/types.d.ts.map +1 -0
- package/build-types/use-theme-provider-styles.d.ts +17 -0
- package/build-types/use-theme-provider-styles.d.ts.map +1 -0
- package/docs/ds-tokens.md +283 -0
- package/package.json +58 -0
- package/src/color-ramps/index.ts +155 -0
- package/src/color-ramps/lib/cache-utils.ts +56 -0
- package/src/color-ramps/lib/constants.ts +85 -0
- package/src/color-ramps/lib/find-color-with-constraints.ts +190 -0
- package/src/color-ramps/lib/index.ts +369 -0
- package/src/color-ramps/lib/ramp-configs.ts +309 -0
- package/src/color-ramps/lib/taper-chroma.ts +226 -0
- package/src/color-ramps/lib/types.ts +90 -0
- package/src/color-ramps/lib/utils.ts +161 -0
- package/src/color-ramps/stories/index.story.tsx +264 -0
- package/src/color-ramps/stories/ramp-table.tsx +212 -0
- package/src/color-ramps/test/__snapshots__/index.test.ts.snap +1280 -0
- package/src/color-ramps/test/index.test.ts +94 -0
- package/src/context.ts +19 -0
- package/src/index.ts +2 -0
- package/src/lock-unlock.ts +10 -0
- package/src/prebuilt/css/design-tokens.css +401 -0
- package/src/prebuilt/js/design-tokens.js +116 -0
- package/src/prebuilt/json/figma.json +1317 -0
- package/src/prebuilt/ts/design-tokens.ts +335 -0
- package/src/private-apis.ts +12 -0
- package/src/stories/index.story.tsx +426 -0
- package/src/style.module.css +3 -0
- package/src/theme-provider.tsx +87 -0
- package/src/types/css-modules.d.ts +4 -0
- package/src/types.ts +44 -0
- package/src/use-theme-provider-styles.ts +247 -0
- package/terrazzo.config.ts +102 -0
- package/tokens/border.json +34 -0
- package/tokens/color.json +877 -0
- package/tokens/elevation.json +201 -0
- package/tokens/spacing.json +45 -0
- package/tokens/typography.json +93 -0
- package/tsconfig.json +9 -0
- package/tsconfig.tsbuildinfo +1 -0
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal dependencies
|
|
3
|
+
*/
|
|
4
|
+
import type { RampStepConfig, RampConfig, RampDirection } from './types';
|
|
5
|
+
import type { TaperChromaOptions } from './taper-chroma';
|
|
6
|
+
|
|
7
|
+
const lightnessConstraintForegroundHighContrast = (
|
|
8
|
+
direction: RampDirection
|
|
9
|
+
) =>
|
|
10
|
+
direction === 'lighter'
|
|
11
|
+
? 0.9551 // lightness of #f0f0f0 (ie $gray-100)
|
|
12
|
+
: 0.235; // lightness of #1e1e1e (ie $gray-900)
|
|
13
|
+
const lightnessConstraintForegroundMediumContrast = (
|
|
14
|
+
direction: RampDirection
|
|
15
|
+
) =>
|
|
16
|
+
direction === 'lighter'
|
|
17
|
+
? 0.77 // lightness of #b4b4b4
|
|
18
|
+
: 0.56; // lightness of #747474
|
|
19
|
+
const lightnessConstraintBgFill = ( direction: RampDirection ) =>
|
|
20
|
+
direction === 'lighter'
|
|
21
|
+
? 0.67 // lightness of #969696 (7:1 vs black)
|
|
22
|
+
: 0.45; // lightness of #555555 (7:1 vs white)
|
|
23
|
+
|
|
24
|
+
const BG_SURFACE_TAPER_CHROMA: TaperChromaOptions = {
|
|
25
|
+
alpha: 0.7,
|
|
26
|
+
};
|
|
27
|
+
const FG_TAPER_CHROMA: TaperChromaOptions = {
|
|
28
|
+
alpha: 0.6,
|
|
29
|
+
kLight: 0.2,
|
|
30
|
+
kDark: 0.2,
|
|
31
|
+
};
|
|
32
|
+
const STROKE_TAPER_CHROMA: TaperChromaOptions = {
|
|
33
|
+
alpha: 0.6,
|
|
34
|
+
radiusDark: 0.01,
|
|
35
|
+
radiusLight: 0.01,
|
|
36
|
+
kLight: 0.8,
|
|
37
|
+
kDark: 0.8,
|
|
38
|
+
};
|
|
39
|
+
const ACCENT_SURFACE_TAPER_CHROMA: TaperChromaOptions = {
|
|
40
|
+
alpha: 0.75,
|
|
41
|
+
radiusDark: 0.01,
|
|
42
|
+
radiusLight: 0.01,
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const fgSurface4Config: RampStepConfig = {
|
|
46
|
+
contrast: {
|
|
47
|
+
reference: 'surface3',
|
|
48
|
+
followDirection: 'main',
|
|
49
|
+
target: 7,
|
|
50
|
+
preferLighter: true,
|
|
51
|
+
},
|
|
52
|
+
lightness: lightnessConstraintForegroundHighContrast,
|
|
53
|
+
taperChromaOptions: FG_TAPER_CHROMA,
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export const BG_RAMP_CONFIG: RampConfig = {
|
|
57
|
+
// Surface
|
|
58
|
+
surface1: {
|
|
59
|
+
contrast: {
|
|
60
|
+
reference: 'surface2',
|
|
61
|
+
followDirection: 'opposite',
|
|
62
|
+
target: 1.02,
|
|
63
|
+
ignoreWhenAdjustingSeed: true,
|
|
64
|
+
},
|
|
65
|
+
taperChromaOptions: BG_SURFACE_TAPER_CHROMA,
|
|
66
|
+
},
|
|
67
|
+
surface2: {
|
|
68
|
+
contrast: {
|
|
69
|
+
reference: 'seed',
|
|
70
|
+
followDirection: 'main',
|
|
71
|
+
target: 1,
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
surface3: {
|
|
75
|
+
contrast: {
|
|
76
|
+
reference: 'surface2',
|
|
77
|
+
followDirection: 'main',
|
|
78
|
+
target: 1.02,
|
|
79
|
+
},
|
|
80
|
+
taperChromaOptions: BG_SURFACE_TAPER_CHROMA,
|
|
81
|
+
},
|
|
82
|
+
surface4: {
|
|
83
|
+
contrast: {
|
|
84
|
+
reference: 'surface2',
|
|
85
|
+
followDirection: 'main',
|
|
86
|
+
target: 1.08,
|
|
87
|
+
},
|
|
88
|
+
taperChromaOptions: BG_SURFACE_TAPER_CHROMA,
|
|
89
|
+
},
|
|
90
|
+
surface5: {
|
|
91
|
+
contrast: {
|
|
92
|
+
reference: 'surface2',
|
|
93
|
+
followDirection: 'main',
|
|
94
|
+
target: 1.2,
|
|
95
|
+
},
|
|
96
|
+
taperChromaOptions: BG_SURFACE_TAPER_CHROMA,
|
|
97
|
+
},
|
|
98
|
+
surface6: {
|
|
99
|
+
contrast: {
|
|
100
|
+
reference: 'surface2',
|
|
101
|
+
followDirection: 'main',
|
|
102
|
+
target: 1.4,
|
|
103
|
+
},
|
|
104
|
+
taperChromaOptions: BG_SURFACE_TAPER_CHROMA,
|
|
105
|
+
},
|
|
106
|
+
// Bg fill
|
|
107
|
+
bgFill1: {
|
|
108
|
+
contrast: {
|
|
109
|
+
reference: 'surface2',
|
|
110
|
+
followDirection: 'main',
|
|
111
|
+
target: 4,
|
|
112
|
+
},
|
|
113
|
+
lightness: lightnessConstraintBgFill,
|
|
114
|
+
},
|
|
115
|
+
bgFill2: {
|
|
116
|
+
contrast: {
|
|
117
|
+
reference: 'bgFill1',
|
|
118
|
+
followDirection: 'main',
|
|
119
|
+
target: 1.2,
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
bgFillInverted1: {
|
|
123
|
+
contrast: {
|
|
124
|
+
reference: 'bgFillInverted2',
|
|
125
|
+
followDirection: 'opposite',
|
|
126
|
+
target: 1.2,
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
bgFillInverted2: fgSurface4Config,
|
|
130
|
+
bgFillDark: {
|
|
131
|
+
contrast: {
|
|
132
|
+
reference: 'surface3',
|
|
133
|
+
followDirection: 'darker', // This is what causes the token to be always dark
|
|
134
|
+
target: 7,
|
|
135
|
+
ignoreWhenAdjustingSeed: true,
|
|
136
|
+
},
|
|
137
|
+
lightness: lightnessConstraintForegroundHighContrast,
|
|
138
|
+
taperChromaOptions: FG_TAPER_CHROMA,
|
|
139
|
+
},
|
|
140
|
+
// Stroke
|
|
141
|
+
stroke1: {
|
|
142
|
+
contrast: {
|
|
143
|
+
reference: 'stroke3',
|
|
144
|
+
followDirection: 'opposite',
|
|
145
|
+
target: 2.2,
|
|
146
|
+
},
|
|
147
|
+
taperChromaOptions: STROKE_TAPER_CHROMA,
|
|
148
|
+
},
|
|
149
|
+
stroke2: {
|
|
150
|
+
contrast: {
|
|
151
|
+
reference: 'stroke3',
|
|
152
|
+
followDirection: 'opposite',
|
|
153
|
+
target: 1.5,
|
|
154
|
+
},
|
|
155
|
+
taperChromaOptions: STROKE_TAPER_CHROMA,
|
|
156
|
+
},
|
|
157
|
+
stroke3: {
|
|
158
|
+
contrast: {
|
|
159
|
+
reference: 'surface3',
|
|
160
|
+
followDirection: 'main',
|
|
161
|
+
target: 3,
|
|
162
|
+
},
|
|
163
|
+
taperChromaOptions: STROKE_TAPER_CHROMA,
|
|
164
|
+
},
|
|
165
|
+
stroke4: {
|
|
166
|
+
contrast: {
|
|
167
|
+
reference: 'stroke3',
|
|
168
|
+
followDirection: 'main',
|
|
169
|
+
target: 1.5,
|
|
170
|
+
},
|
|
171
|
+
taperChromaOptions: STROKE_TAPER_CHROMA,
|
|
172
|
+
},
|
|
173
|
+
// fgSurface
|
|
174
|
+
fgSurface1: {
|
|
175
|
+
contrast: {
|
|
176
|
+
reference: 'surface3',
|
|
177
|
+
followDirection: 'main',
|
|
178
|
+
target: 2,
|
|
179
|
+
preferLighter: true,
|
|
180
|
+
},
|
|
181
|
+
taperChromaOptions: FG_TAPER_CHROMA,
|
|
182
|
+
},
|
|
183
|
+
fgSurface2: {
|
|
184
|
+
contrast: {
|
|
185
|
+
reference: 'surface3',
|
|
186
|
+
followDirection: 'main',
|
|
187
|
+
target: 3,
|
|
188
|
+
preferLighter: true,
|
|
189
|
+
},
|
|
190
|
+
taperChromaOptions: FG_TAPER_CHROMA,
|
|
191
|
+
},
|
|
192
|
+
fgSurface3: {
|
|
193
|
+
contrast: {
|
|
194
|
+
reference: 'surface3',
|
|
195
|
+
followDirection: 'main',
|
|
196
|
+
target: 4.5,
|
|
197
|
+
preferLighter: true,
|
|
198
|
+
},
|
|
199
|
+
lightness: lightnessConstraintForegroundMediumContrast,
|
|
200
|
+
taperChromaOptions: FG_TAPER_CHROMA,
|
|
201
|
+
},
|
|
202
|
+
fgSurface4: fgSurface4Config,
|
|
203
|
+
// fgFill
|
|
204
|
+
fgFill: {
|
|
205
|
+
contrast: {
|
|
206
|
+
reference: 'bgFill1',
|
|
207
|
+
followDirection: 'best',
|
|
208
|
+
target: 4.5,
|
|
209
|
+
preferLighter: true,
|
|
210
|
+
},
|
|
211
|
+
lightness: lightnessConstraintForegroundHighContrast,
|
|
212
|
+
taperChromaOptions: FG_TAPER_CHROMA,
|
|
213
|
+
},
|
|
214
|
+
fgFillInverted: {
|
|
215
|
+
contrast: {
|
|
216
|
+
reference: 'bgFillInverted1',
|
|
217
|
+
followDirection: 'best',
|
|
218
|
+
target: 4.5,
|
|
219
|
+
preferLighter: true,
|
|
220
|
+
},
|
|
221
|
+
lightness: lightnessConstraintForegroundHighContrast,
|
|
222
|
+
taperChromaOptions: FG_TAPER_CHROMA,
|
|
223
|
+
},
|
|
224
|
+
fgFillDark: {
|
|
225
|
+
contrast: {
|
|
226
|
+
reference: 'bgFillDark',
|
|
227
|
+
followDirection: 'best',
|
|
228
|
+
target: 4.5,
|
|
229
|
+
preferLighter: true,
|
|
230
|
+
},
|
|
231
|
+
lightness: lightnessConstraintForegroundHighContrast,
|
|
232
|
+
taperChromaOptions: FG_TAPER_CHROMA,
|
|
233
|
+
},
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
// BG_RAMP: seed => surface2 => {bgFill, surface3 => all other tokens}
|
|
237
|
+
// ACCENT_RAMP: seed => bgFill1 => surface2 => surface3 => all other tokens
|
|
238
|
+
export const ACCENT_RAMP_CONFIG: RampConfig = {
|
|
239
|
+
...BG_RAMP_CONFIG,
|
|
240
|
+
surface1: {
|
|
241
|
+
...BG_RAMP_CONFIG.surface1,
|
|
242
|
+
taperChromaOptions: ACCENT_SURFACE_TAPER_CHROMA,
|
|
243
|
+
},
|
|
244
|
+
surface2: {
|
|
245
|
+
contrast: {
|
|
246
|
+
reference: 'bgFill1',
|
|
247
|
+
followDirection: 'opposite',
|
|
248
|
+
target: BG_RAMP_CONFIG.bgFill1.contrast.target,
|
|
249
|
+
ignoreWhenAdjustingSeed: true,
|
|
250
|
+
},
|
|
251
|
+
taperChromaOptions: ACCENT_SURFACE_TAPER_CHROMA,
|
|
252
|
+
},
|
|
253
|
+
surface3: {
|
|
254
|
+
...BG_RAMP_CONFIG.surface3,
|
|
255
|
+
taperChromaOptions: ACCENT_SURFACE_TAPER_CHROMA,
|
|
256
|
+
},
|
|
257
|
+
surface4: {
|
|
258
|
+
...BG_RAMP_CONFIG.surface4,
|
|
259
|
+
taperChromaOptions: ACCENT_SURFACE_TAPER_CHROMA,
|
|
260
|
+
},
|
|
261
|
+
surface5: {
|
|
262
|
+
...BG_RAMP_CONFIG.surface5,
|
|
263
|
+
taperChromaOptions: ACCENT_SURFACE_TAPER_CHROMA,
|
|
264
|
+
},
|
|
265
|
+
surface6: {
|
|
266
|
+
...BG_RAMP_CONFIG.surface6,
|
|
267
|
+
taperChromaOptions: ACCENT_SURFACE_TAPER_CHROMA,
|
|
268
|
+
},
|
|
269
|
+
bgFill1: {
|
|
270
|
+
contrast: {
|
|
271
|
+
reference: 'seed',
|
|
272
|
+
followDirection: 'main',
|
|
273
|
+
target: 1,
|
|
274
|
+
},
|
|
275
|
+
},
|
|
276
|
+
stroke1: {
|
|
277
|
+
...BG_RAMP_CONFIG.stroke1,
|
|
278
|
+
},
|
|
279
|
+
stroke2: {
|
|
280
|
+
...BG_RAMP_CONFIG.stroke2,
|
|
281
|
+
},
|
|
282
|
+
stroke3: {
|
|
283
|
+
...BG_RAMP_CONFIG.stroke3,
|
|
284
|
+
sameAsIfPossible: 'fgSurface3',
|
|
285
|
+
taperChromaOptions: undefined,
|
|
286
|
+
},
|
|
287
|
+
stroke4: {
|
|
288
|
+
...BG_RAMP_CONFIG.stroke4,
|
|
289
|
+
taperChromaOptions: undefined,
|
|
290
|
+
},
|
|
291
|
+
// fgSurface: do not de-saturate
|
|
292
|
+
fgSurface1: {
|
|
293
|
+
...BG_RAMP_CONFIG.fgSurface1,
|
|
294
|
+
taperChromaOptions: undefined,
|
|
295
|
+
},
|
|
296
|
+
fgSurface2: {
|
|
297
|
+
...BG_RAMP_CONFIG.fgSurface2,
|
|
298
|
+
taperChromaOptions: undefined,
|
|
299
|
+
},
|
|
300
|
+
fgSurface3: {
|
|
301
|
+
...BG_RAMP_CONFIG.fgSurface3,
|
|
302
|
+
taperChromaOptions: undefined,
|
|
303
|
+
sameAsIfPossible: 'bgFill1',
|
|
304
|
+
},
|
|
305
|
+
fgSurface4: {
|
|
306
|
+
...BG_RAMP_CONFIG.fgSurface4,
|
|
307
|
+
taperChromaOptions: undefined,
|
|
308
|
+
},
|
|
309
|
+
};
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
// npm i colorjs.io
|
|
2
|
+
/**
|
|
3
|
+
* External dependencies
|
|
4
|
+
*/
|
|
5
|
+
import Color from 'colorjs.io';
|
|
6
|
+
|
|
7
|
+
export interface TaperChromaOptions {
|
|
8
|
+
gamut?: 'p3' | 'srgb'; // target gamut (default "p3")
|
|
9
|
+
alpha?: number; // base fraction of Cmax at target (default 0.62)
|
|
10
|
+
carry?: number; // seed vividness carry exponent β in [0..1] (default 0.5)
|
|
11
|
+
cUpperBound?: number; // hard search cap for C (default 0.45)
|
|
12
|
+
// Continuous taper around the seed (desaturate both sides slightly)
|
|
13
|
+
radiusLight?: number; // distance in L where kLight is reached (default 0.20)
|
|
14
|
+
radiusDark?: number; // distance in L where kDark is reached (default 0.20)
|
|
15
|
+
kLight?: number; // floor multiplier near lighter side (default 0.85)
|
|
16
|
+
kDark?: number; // floor multiplier near darker side (default 0.85)
|
|
17
|
+
// Achromatic handling
|
|
18
|
+
hueFallback?: number; // degrees: if seed is achromatic and you still want color
|
|
19
|
+
achromaEpsilon?: number; // ≤ this chroma → treat as achromatic (default 0.005)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Given the seed and the target lightness, tapers the chroma smoothly.
|
|
24
|
+
* - C_intended = Cmax(Lt,H0) * alpha * (seedRelative^carry)
|
|
25
|
+
* - Continuous taper vs |Lt - Ls| to softly reduce chroma for neighbors
|
|
26
|
+
* - Downward-only clamp on C (preserve L & H)
|
|
27
|
+
* @param seed
|
|
28
|
+
* @param lTarget
|
|
29
|
+
* @param options
|
|
30
|
+
*/
|
|
31
|
+
export function taperChroma(
|
|
32
|
+
seed: Color, // already OKLCH
|
|
33
|
+
lTarget: number, // [0..1]
|
|
34
|
+
options: TaperChromaOptions = {}
|
|
35
|
+
): { l: number; c: number } {
|
|
36
|
+
const gamut = options.gamut ?? 'p3';
|
|
37
|
+
const alpha = options.alpha ?? 0.65; // 0.7-0.8 works well for accent surface
|
|
38
|
+
const carry = options.carry ?? 0.5;
|
|
39
|
+
const cUpperBound = options.cUpperBound ?? 0.45;
|
|
40
|
+
const radiusLight = options.radiusLight ?? 0.2;
|
|
41
|
+
const radiusDark = options.radiusDark ?? 0.2;
|
|
42
|
+
const kLight = options.kLight ?? 0.85;
|
|
43
|
+
const kDark = options.kDark ?? 0.85;
|
|
44
|
+
const achromaEpsilon = options.achromaEpsilon ?? 0.005;
|
|
45
|
+
|
|
46
|
+
const cSeed = Math.max( 0, seed.oklch.c );
|
|
47
|
+
let hSeed = Number( seed.oklch.h );
|
|
48
|
+
|
|
49
|
+
const chromaIsTiny = cSeed < achromaEpsilon;
|
|
50
|
+
const hueIsInvalid = ! Number.isFinite( hSeed );
|
|
51
|
+
|
|
52
|
+
if ( chromaIsTiny || hueIsInvalid ) {
|
|
53
|
+
if ( typeof options.hueFallback === 'number' ) {
|
|
54
|
+
hSeed = normalizeHue( options.hueFallback );
|
|
55
|
+
} else {
|
|
56
|
+
// Respect achromatic intent: grayscale at target L
|
|
57
|
+
return new Color( 'oklch', [ clamp01( lTarget ), 0, 0 ] );
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Capacity at seed and target
|
|
62
|
+
const lSeed = clamp01( seed.oklch.l );
|
|
63
|
+
const cmaxSeed = getCachedMaxChromaAtLH( lSeed, hSeed, gamut, cUpperBound );
|
|
64
|
+
const cmaxTarget = getCachedMaxChromaAtLH(
|
|
65
|
+
clamp01( lTarget ),
|
|
66
|
+
hSeed,
|
|
67
|
+
gamut,
|
|
68
|
+
cUpperBound
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
// Seed vividness ratio (hue-fair normalization)
|
|
72
|
+
let seedRelative = 0;
|
|
73
|
+
const denom = cmaxSeed > 0 ? cmaxSeed : 1e-6;
|
|
74
|
+
seedRelative = clamp01( cSeed / denom );
|
|
75
|
+
|
|
76
|
+
// Intended chroma from local capacity, tempered by seed vividness
|
|
77
|
+
const cIntendedBase = alpha * cmaxTarget;
|
|
78
|
+
const cWithCarry =
|
|
79
|
+
cIntendedBase * Math.pow( seedRelative, clamp01( carry ) );
|
|
80
|
+
|
|
81
|
+
// Gentle, symmetric desaturation vs distance in L
|
|
82
|
+
const t = continuousTaper( lSeed, lTarget, {
|
|
83
|
+
radiusLight,
|
|
84
|
+
radiusDark,
|
|
85
|
+
kLight,
|
|
86
|
+
kDark,
|
|
87
|
+
} );
|
|
88
|
+
let cPlanned = cWithCarry * t;
|
|
89
|
+
|
|
90
|
+
// Downward-only clamp (preserve L & H)
|
|
91
|
+
const lOut = clamp01( lTarget );
|
|
92
|
+
const candidate = new Color( 'oklch', [ lOut, cPlanned, hSeed ] );
|
|
93
|
+
if ( ! candidate.inGamut( gamut ) ) {
|
|
94
|
+
const cap = Math.min( cPlanned, cUpperBound );
|
|
95
|
+
cPlanned = getCachedMaxChromaAtLH( lOut, hSeed, gamut, cap );
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
cPlanned = Math.min( cPlanned, cSeed );
|
|
99
|
+
|
|
100
|
+
return { l: lOut, c: cPlanned };
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/* ---------------- helpers & caches ---------------- */
|
|
104
|
+
|
|
105
|
+
function clamp01( x: number ): number {
|
|
106
|
+
if ( x < 0 ) {
|
|
107
|
+
return 0;
|
|
108
|
+
}
|
|
109
|
+
if ( x > 1 ) {
|
|
110
|
+
return 1;
|
|
111
|
+
}
|
|
112
|
+
return x;
|
|
113
|
+
}
|
|
114
|
+
function normalizeHue( h: number ): number {
|
|
115
|
+
let hue = h % 360;
|
|
116
|
+
if ( hue < 0 ) {
|
|
117
|
+
hue += 360;
|
|
118
|
+
}
|
|
119
|
+
return hue;
|
|
120
|
+
}
|
|
121
|
+
function raisedCosine( u: number ): number {
|
|
122
|
+
const x = clamp01( u );
|
|
123
|
+
return 0.5 - 0.5 * Math.cos( Math.PI * x );
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* smooth, distance-from-seed chroma taper (raised-cosine per side)
|
|
128
|
+
* @param seedL
|
|
129
|
+
* @param targetL
|
|
130
|
+
* @param opts
|
|
131
|
+
* @param opts.radiusLight
|
|
132
|
+
* @param opts.radiusDark
|
|
133
|
+
* @param opts.kLight
|
|
134
|
+
* @param opts.kDark
|
|
135
|
+
*/
|
|
136
|
+
function continuousTaper(
|
|
137
|
+
seedL: number,
|
|
138
|
+
targetL: number,
|
|
139
|
+
opts: {
|
|
140
|
+
radiusLight: number;
|
|
141
|
+
radiusDark: number;
|
|
142
|
+
kLight: number;
|
|
143
|
+
kDark: number;
|
|
144
|
+
}
|
|
145
|
+
): number {
|
|
146
|
+
const d = targetL - seedL;
|
|
147
|
+
if ( d >= 0 ) {
|
|
148
|
+
const u = opts.radiusLight > 0 ? Math.abs( d ) / opts.radiusLight : 1;
|
|
149
|
+
const w = raisedCosine( u > 1 ? 1 : u );
|
|
150
|
+
return 1 - ( 1 - opts.kLight ) * w;
|
|
151
|
+
}
|
|
152
|
+
const u = opts.radiusDark > 0 ? Math.abs( d ) / opts.radiusDark : 1;
|
|
153
|
+
const w = raisedCosine( u > 1 ? 1 : u );
|
|
154
|
+
return 1 - ( 1 - opts.kDark ) * w;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/* ---- chroma-capacity queries with small caches ---- */
|
|
158
|
+
|
|
159
|
+
const maxChromaCache = new Map< string, number >();
|
|
160
|
+
|
|
161
|
+
function keyMax(
|
|
162
|
+
l: number,
|
|
163
|
+
h: number,
|
|
164
|
+
gamut: 'p3' | 'srgb',
|
|
165
|
+
cap: number
|
|
166
|
+
): string {
|
|
167
|
+
// Quantize to keep cache compact
|
|
168
|
+
const lq = quantize( l, 1e-3 );
|
|
169
|
+
const hq = quantize( normalizeHue( h ), 1e-1 );
|
|
170
|
+
const cq = quantize( cap, 1e-3 );
|
|
171
|
+
return `${ gamut }|L:${ lq }|H:${ hq }|cap:${ cq }`;
|
|
172
|
+
}
|
|
173
|
+
function quantize( x: number, step: number ): number {
|
|
174
|
+
const k = Math.round( x / step );
|
|
175
|
+
return k * step;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function getCachedMaxChromaAtLH(
|
|
179
|
+
l: number,
|
|
180
|
+
h: number,
|
|
181
|
+
gamut: 'p3' | 'srgb',
|
|
182
|
+
cap: number
|
|
183
|
+
): number {
|
|
184
|
+
const key = keyMax( l, h, gamut, cap );
|
|
185
|
+
const hit = maxChromaCache.get( key );
|
|
186
|
+
if ( typeof hit === 'number' ) {
|
|
187
|
+
return hit;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const computed = maxInGamutChromaAtLH( l, h, gamut, cap );
|
|
191
|
+
maxChromaCache.set( key, computed );
|
|
192
|
+
return computed;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Binary-search the max in-gamut chroma at fixed (L,H) in the target gamut
|
|
197
|
+
* @param l
|
|
198
|
+
* @param h
|
|
199
|
+
* @param gamut
|
|
200
|
+
* @param cap
|
|
201
|
+
*/
|
|
202
|
+
function maxInGamutChromaAtLH(
|
|
203
|
+
l: number,
|
|
204
|
+
h: number,
|
|
205
|
+
gamut: 'p3' | 'srgb',
|
|
206
|
+
cap: number
|
|
207
|
+
): number {
|
|
208
|
+
let lo = 0;
|
|
209
|
+
let hi = cap;
|
|
210
|
+
let ok = 0;
|
|
211
|
+
|
|
212
|
+
const lFixed = clamp01( l );
|
|
213
|
+
const hFixed = normalizeHue( h );
|
|
214
|
+
|
|
215
|
+
for ( let i = 0; i < 18; i++ ) {
|
|
216
|
+
const mid = ( lo + hi ) / 2;
|
|
217
|
+
const probe = new Color( 'oklch', [ lFixed, mid, hFixed ] );
|
|
218
|
+
if ( probe.inGamut( gamut ) ) {
|
|
219
|
+
ok = mid;
|
|
220
|
+
lo = mid;
|
|
221
|
+
} else {
|
|
222
|
+
hi = mid;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return ok;
|
|
226
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal dependencies
|
|
3
|
+
*/
|
|
4
|
+
import type { TaperChromaOptions } from './taper-chroma';
|
|
5
|
+
|
|
6
|
+
export type Ramp = {
|
|
7
|
+
// Backgrounds for surfaces (nuanced, slight variations compared to bg)
|
|
8
|
+
surface1: string;
|
|
9
|
+
surface2: string;
|
|
10
|
+
surface3: string;
|
|
11
|
+
surface4: string;
|
|
12
|
+
surface5: string;
|
|
13
|
+
surface6: string;
|
|
14
|
+
// Strokes
|
|
15
|
+
stroke1: string;
|
|
16
|
+
stroke2: string;
|
|
17
|
+
stroke3: string;
|
|
18
|
+
stroke4: string;
|
|
19
|
+
// Stronger backgrounds for primary UI elements
|
|
20
|
+
bgFill1: string;
|
|
21
|
+
bgFill2: string;
|
|
22
|
+
bgFillInverted1: string;
|
|
23
|
+
bgFillInverted2: string;
|
|
24
|
+
bgFillDark: string;
|
|
25
|
+
// Foreground (text, icon) colors
|
|
26
|
+
fgSurface1: string;
|
|
27
|
+
fgSurface2: string;
|
|
28
|
+
fgSurface3: string;
|
|
29
|
+
fgSurface4: string;
|
|
30
|
+
// Foreground (text, icon) colors on top of bgFill
|
|
31
|
+
fgFill: string;
|
|
32
|
+
fgFillInverted: string;
|
|
33
|
+
fgFillDark: string;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export type RampDirection = 'lighter' | 'darker';
|
|
37
|
+
export type FollowDirection = 'main' | 'opposite' | 'best' | RampDirection;
|
|
38
|
+
export type ContrastRequirement = {
|
|
39
|
+
/** The reference color against which to calculate the contrast */
|
|
40
|
+
reference: keyof Ramp | 'seed';
|
|
41
|
+
/**
|
|
42
|
+
* Which direction should the algorithm search a matching color in:
|
|
43
|
+
* - main: follow the same direction as the ramp's main direction
|
|
44
|
+
* - opposite: follow the opposite direction of the ramp
|
|
45
|
+
* - best: pick the direction that has the most contrast headroom
|
|
46
|
+
* - hardcoded ramp direction (useful for generating colors that always
|
|
47
|
+
* light/dark regardless of the ramp direction)
|
|
48
|
+
*/
|
|
49
|
+
followDirection: FollowDirection;
|
|
50
|
+
/**
|
|
51
|
+
* Prefer "lighter" direction when searching for a contrasting color.
|
|
52
|
+
* Especially useful for foreground color to counter the poor results that the
|
|
53
|
+
* WCAG algo gives when contrasting white text over mid-lightness backgrounds.
|
|
54
|
+
*/
|
|
55
|
+
preferLighter?: boolean;
|
|
56
|
+
/**
|
|
57
|
+
* The contrast target to meet.
|
|
58
|
+
*/
|
|
59
|
+
target: number;
|
|
60
|
+
/**
|
|
61
|
+
* When true, the algorithm won't count a failure in meeting the contrast
|
|
62
|
+
* target as a reason to recalculate the ramp.
|
|
63
|
+
*/
|
|
64
|
+
ignoreWhenAdjustingSeed?: boolean;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
export type RampStepConfig = {
|
|
68
|
+
contrast: ContrastRequirement;
|
|
69
|
+
lightness?: ( direction: RampDirection ) => number;
|
|
70
|
+
taperChromaOptions?: TaperChromaOptions;
|
|
71
|
+
/**
|
|
72
|
+
* If specified, try to reuse the color from this step if it meets
|
|
73
|
+
* the contrast requirements. This reduces the number of unique colors
|
|
74
|
+
* in the ramp and improves consistency.
|
|
75
|
+
*/
|
|
76
|
+
sameAsIfPossible?: keyof Ramp;
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
export type RampConfig = Record< keyof Ramp, RampStepConfig >;
|
|
80
|
+
|
|
81
|
+
export type RampResult = {
|
|
82
|
+
ramp: Record<
|
|
83
|
+
keyof Ramp,
|
|
84
|
+
{
|
|
85
|
+
color: string;
|
|
86
|
+
warning: boolean;
|
|
87
|
+
}
|
|
88
|
+
>;
|
|
89
|
+
direction: RampDirection;
|
|
90
|
+
};
|