@oxyhq/bloom 0.75.0 → 0.76.1
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/lib/commonjs/theme/color-policy.js +36 -12
- package/lib/commonjs/theme/color-policy.js.map +1 -1
- package/lib/module/theme/color-policy.js +36 -12
- package/lib/module/theme/color-policy.js.map +1 -1
- package/lib/typescript/commonjs/theme/color-policy.d.ts.map +1 -1
- package/lib/typescript/module/theme/color-policy.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/theme/__tests__/__fixtures__/golden-resolved-tokens.json +132 -132
- package/src/theme/__tests__/__snapshots__/visual-gallery.test.tsx.snap +110 -110
- package/src/theme/color-policy.ts +36 -17
|
@@ -94,8 +94,14 @@ const noLegibleForeground = (argb: number): boolean =>
|
|
|
94
94
|
/** The tone a white-label fill sits at in LIGHT mode. */
|
|
95
95
|
const LIGHT_FILL_TONE = 45;
|
|
96
96
|
|
|
97
|
-
/**
|
|
98
|
-
const
|
|
97
|
+
/** The tone a white-label fill sits at in DARK: the ceiling white text allows. */
|
|
98
|
+
const DARK_FILL_TONE = 49;
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* No dark-mode ACCENT sits below this. Some hues peak dark — violet at tone 33 —
|
|
102
|
+
* and rendering one there gives a slab rather than a highlight.
|
|
103
|
+
*/
|
|
104
|
+
const DARK_ACCENT_FLOOR = 58;
|
|
99
105
|
|
|
100
106
|
/**
|
|
101
107
|
* Accent fills sit at their hue's PEAK tone in dark — where the hue is most
|
|
@@ -190,6 +196,23 @@ function vividHueNear(hue: number): number {
|
|
|
190
196
|
return bestChroma - baseChroma >= VIVID_SNAP_MIN_GAIN ? bestHue : hue;
|
|
191
197
|
}
|
|
192
198
|
|
|
199
|
+
/**
|
|
200
|
+
* The tone a BRAND fill sits at.
|
|
201
|
+
*
|
|
202
|
+
* Normally the ceiling white text allows, so a Follow button, an avatar and the
|
|
203
|
+
* compose button all carry a white label. But a seed that is ALREADY light —
|
|
204
|
+
* yellow at tone 82, faircoin at 90 — cannot be dragged down there without
|
|
205
|
+
* ceasing to be itself: yellow turns to brown and lime to moss. Those keep their
|
|
206
|
+
* own tone and take a black label instead, which is what a bright yellow button
|
|
207
|
+
* wants anyway. The 25-tone budget is what separates the two cases, and it is
|
|
208
|
+
* why 11 of the 13 presets carry white and exactly the two light ones do not.
|
|
209
|
+
*/
|
|
210
|
+
function whiteLabelTone(palette: TonalPalette, seedTone: number, isDark: boolean): number {
|
|
211
|
+
const target = isDark ? DARK_FILL_TONE : LIGHT_FILL_TONE;
|
|
212
|
+
const candidate = Math.max(target, seedTone - 25);
|
|
213
|
+
return contrastOf(palette.tone(candidate), true) >= AA ? candidate : seedTone;
|
|
214
|
+
}
|
|
215
|
+
|
|
193
216
|
/** Settle a fill tone so some foreground is legible, then pair it with that foreground. */
|
|
194
217
|
function fillPair(palette: TonalPalette, startTone: number): { fill: string; foreground: string } {
|
|
195
218
|
let tone = startTone;
|
|
@@ -246,19 +269,15 @@ export function buildPolicyTokens(
|
|
|
246
269
|
): PolicyTokens {
|
|
247
270
|
const seed = Hct.fromInt(argbFromHex(seedHex));
|
|
248
271
|
|
|
249
|
-
//
|
|
250
|
-
//
|
|
251
|
-
//
|
|
252
|
-
//
|
|
253
|
-
//
|
|
254
|
-
//
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
const primary = fillPair(
|
|
259
|
-
brandPalette,
|
|
260
|
-
isDark ? Math.max(seed.tone, DARK_FLOOR) : LIGHT_FILL_TONE,
|
|
261
|
-
);
|
|
272
|
+
// Max chroma at a tone that carries a WHITE label, in both modes.
|
|
273
|
+
//
|
|
274
|
+
// The brand fill is what a compose button, a Follow button, an avatar and the
|
|
275
|
+
// user's own chat bubble paint with, and those carry white text. White needs
|
|
276
|
+
// tone <= 49, so that is the ceiling — in dark as much as in light. Letting the
|
|
277
|
+
// dark fill sit at the seed's own tone instead makes it brighter, but every one
|
|
278
|
+
// of those labels turns black, which costs more than the brightness buys.
|
|
279
|
+
const brandPalette = TonalPalette.fromHueAndChroma(seed.hue, 200);
|
|
280
|
+
const primary = fillPair(brandPalette, whiteLabelTone(brandPalette, seed.tone, isDark));
|
|
262
281
|
|
|
263
282
|
const tokens: PolicyTokens = {
|
|
264
283
|
'--primary': primary.fill,
|
|
@@ -309,7 +328,7 @@ export function buildPolicyTokens(
|
|
|
309
328
|
// black. The accent chroma cap already keeps these hues out of neon territory,
|
|
310
329
|
// and the hue itself is the caller's brand rather than ours to move. The
|
|
311
330
|
// analyzer still runs where it was designed to, inside `color-roles`.
|
|
312
|
-
const tone = isDark ? Math.max(peakTone(hue),
|
|
331
|
+
const tone = isDark ? Math.max(peakTone(hue), DARK_ACCENT_FLOOR) : LIGHT_FILL_TONE;
|
|
313
332
|
const pair = fillPair(fillPalette, tone);
|
|
314
333
|
tokens[`--${role}`] = pair.fill;
|
|
315
334
|
tokens[`--${role}-foreground`] = pair.foreground;
|
|
@@ -336,7 +355,7 @@ export function buildPolicyTokens(
|
|
|
336
355
|
for (const [role, hex] of Object.entries(STATUS_SEEDS)) {
|
|
337
356
|
const status = Hct.fromInt(argbFromHex(hex));
|
|
338
357
|
const palette = TonalPalette.fromHueAndChroma(status.hue, status.chroma);
|
|
339
|
-
const pair = fillPair(palette, isDark ?
|
|
358
|
+
const pair = fillPair(palette, isDark ? DARK_FILL_TONE : LIGHT_FILL_TONE);
|
|
340
359
|
tokens[`--${role}`] = pair.fill;
|
|
341
360
|
tokens[`--${role}-foreground`] = pair.foreground;
|
|
342
361
|
tokens[`--${role}-text`] = rgb(
|