From f2378776c9a237f8b38b1318c2fd506b08caa0f4 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Tue, 1 Sep 2026 17:53:40 +0200 Subject: [PATCH 01/44] Theme: Use a perceptual five-step foreground scale --- .../internal/_wpds-token-fallbacks.scss | 13 +- packages/theme/prebuilt/css/design-tokens.css | 4 +- .../prebuilt/js/design-token-fallbacks.mjs | 13 +- packages/theme/src/color-ramps/index.ts | 2 + .../color-ramps/lib/build-foreground-scale.ts | 439 ++++++++++++++++++ .../theme/src/color-ramps/lib/constants.ts | 4 +- .../src/color-ramps/lib/default-ramps.ts | 18 +- packages/theme/src/color-ramps/lib/index.ts | 24 +- .../theme/src/color-ramps/lib/ramp-configs.ts | 232 ++++++--- packages/theme/src/color-ramps/lib/types.ts | 41 +- packages/theme/src/color-ramps/lib/utils.ts | 8 +- .../src/color-ramps/stories/ramp-table.tsx | 6 + .../test/__snapshots__/index.test.ts.snap | 177 ++++--- .../theme/src/color-ramps/test/index.test.ts | 53 +++ .../theme/src/prebuilt/ts/color-tokens.ts | 32 +- .../src/test/theme-provider.browser.test.tsx | 29 ++ packages/theme/tokens/color.json | 115 +++-- 17 files changed, 977 insertions(+), 233 deletions(-) create mode 100644 packages/theme/src/color-ramps/lib/build-foreground-scale.ts diff --git a/packages/base-styles/internal/_wpds-token-fallbacks.scss b/packages/base-styles/internal/_wpds-token-fallbacks.scss index 655f141f8d8209..ddbf0d01a017d2 100644 --- a/packages/base-styles/internal/_wpds-token-fallbacks.scss +++ b/packages/base-styles/internal/_wpds-token-fallbacks.scss @@ -33,9 +33,6 @@ $fallbacks: ( "--wpds-color-background-interactive-error-weak": "#0000", "--wpds-color-background-interactive-error-weak-active": "#fae5e1", "--wpds-color-background-interactive-error-weak-disabled": "#0000", - "--wpds-color-background-interactive-neutral": "#fff", - "--wpds-color-background-interactive-neutral-active": "#fff", - "--wpds-color-background-interactive-neutral-disabled": "#fff", "--wpds-color-background-interactive-neutral-strong": "#2d2d2d", "--wpds-color-background-interactive-neutral-strong-active": "#1e1e1e", "--wpds-color-background-interactive-neutral-strong-disabled": "#e6e6e6", @@ -71,9 +68,9 @@ $fallbacks: ( "--wpds-color-foreground-content-caution": "#281d00", "--wpds-color-foreground-content-caution-weak": "#836a00", "--wpds-color-foreground-content-error": "#470000", - "--wpds-color-foreground-content-error-weak": "#cc1818", + "--wpds-color-foreground-content-error-weak": "#d71a1a", "--wpds-color-foreground-content-info": "#001b4f", - "--wpds-color-foreground-content-info-weak": "#006bd7", + "--wpds-color-foreground-content-info-weak": "#006ec6", "--wpds-color-foreground-content-neutral": "#1e1e1e", "--wpds-color-foreground-content-neutral-weak": "#707070", "--wpds-color-foreground-content-success": "#002900", @@ -81,20 +78,20 @@ $fallbacks: ( "--wpds-color-foreground-content-warning": "#2e1900", "--wpds-color-foreground-content-warning-weak": "#936300", "--wpds-color-foreground-interactive-brand": - "var(--wp-admin-theme-color, #3858e9)", + "color-mix(in oklch, var(--wp-admin-theme-color, #3858e9) 98%, black)", "--wpds-color-foreground-interactive-brand-active": "color-mix(in oklch, var(--wp-admin-theme-color, #3858e9) 52%, black)", "--wpds-color-foreground-interactive-brand-disabled": "#8d8d8d", "--wpds-color-foreground-interactive-brand-strong": "#fff", "--wpds-color-foreground-interactive-brand-strong-active": "#fff", "--wpds-color-foreground-interactive-brand-strong-disabled": "#8d8d8d", - "--wpds-color-foreground-interactive-error": "#cc1818", + "--wpds-color-foreground-interactive-error": "#be1616", "--wpds-color-foreground-interactive-error-active": "#470000", "--wpds-color-foreground-interactive-error-disabled": "#8d8d8d", "--wpds-color-foreground-interactive-error-strong": "#f2efef", "--wpds-color-foreground-interactive-error-strong-active": "#f2efef", "--wpds-color-foreground-interactive-error-strong-disabled": "#8d8d8d", - "--wpds-color-foreground-interactive-neutral": "#1e1e1e", + "--wpds-color-foreground-interactive-neutral": "#646464", "--wpds-color-foreground-interactive-neutral-active": "#1e1e1e", "--wpds-color-foreground-interactive-neutral-disabled": "#8d8d8d", "--wpds-color-foreground-interactive-neutral-strong": "#f0f0f0", diff --git a/packages/theme/prebuilt/css/design-tokens.css b/packages/theme/prebuilt/css/design-tokens.css index 705d362888fbbb..a28e98271d1e32 100644 --- a/packages/theme/prebuilt/css/design-tokens.css +++ b/packages/theme/prebuilt/css/design-tokens.css @@ -142,7 +142,7 @@ /* Foreground color for content like text with warning tone and weak emphasis. */ --wpds-color-foreground-content-warning-weak: #936300; /* Foreground color for interactive elements with brand tone and normal emphasis. */ - --wpds-color-foreground-interactive-brand: #3858e9; + --wpds-color-foreground-interactive-brand: #3351e8; /* Foreground color for interactive elements with brand tone and normal emphasis that are hovered, focused, or active. */ --wpds-color-foreground-interactive-brand-active: #0b0070; /* Foreground color for interactive elements with brand tone and normal emphasis, in their disabled state. */ @@ -154,7 +154,7 @@ /* Foreground color for interactive elements with brand tone and strong emphasis, in their disabled state. */ --wpds-color-foreground-interactive-brand-strong-disabled: #8d8d8d; /* Foreground color for interactive elements with error tone and normal emphasis. */ - --wpds-color-foreground-interactive-error: #cc1818; + --wpds-color-foreground-interactive-error: #be1616; /* Foreground color for interactive elements with error tone and normal emphasis that are hovered, focused, or active. */ --wpds-color-foreground-interactive-error-active: #470000; /* Foreground color for interactive elements with error tone and normal emphasis, in their disabled state. */ diff --git a/packages/theme/prebuilt/js/design-token-fallbacks.mjs b/packages/theme/prebuilt/js/design-token-fallbacks.mjs index c6b3712b9578b6..d53e3a92b25a9b 100644 --- a/packages/theme/prebuilt/js/design-token-fallbacks.mjs +++ b/packages/theme/prebuilt/js/design-token-fallbacks.mjs @@ -31,9 +31,6 @@ export default { '--wpds-color-background-interactive-error-weak': '#0000', '--wpds-color-background-interactive-error-weak-active': '#fae5e1', '--wpds-color-background-interactive-error-weak-disabled': '#0000', - '--wpds-color-background-interactive-neutral': '#fff', - '--wpds-color-background-interactive-neutral-active': '#fff', - '--wpds-color-background-interactive-neutral-disabled': '#fff', '--wpds-color-background-interactive-neutral-strong': '#2d2d2d', '--wpds-color-background-interactive-neutral-strong-active': '#1e1e1e', '--wpds-color-background-interactive-neutral-strong-disabled': '#e6e6e6', @@ -69,9 +66,9 @@ export default { '--wpds-color-foreground-content-caution': '#281d00', '--wpds-color-foreground-content-caution-weak': '#836a00', '--wpds-color-foreground-content-error': '#470000', - '--wpds-color-foreground-content-error-weak': '#cc1818', + '--wpds-color-foreground-content-error-weak': '#d71a1a', '--wpds-color-foreground-content-info': '#001b4f', - '--wpds-color-foreground-content-info-weak': '#006bd7', + '--wpds-color-foreground-content-info-weak': '#006ec6', '--wpds-color-foreground-content-neutral': '#1e1e1e', '--wpds-color-foreground-content-neutral-weak': '#707070', '--wpds-color-foreground-content-success': '#002900', @@ -79,20 +76,20 @@ export default { '--wpds-color-foreground-content-warning': '#2e1900', '--wpds-color-foreground-content-warning-weak': '#936300', '--wpds-color-foreground-interactive-brand': - 'var(--wp-admin-theme-color, #3858e9)', + 'color-mix(in oklch, var(--wp-admin-theme-color, #3858e9) 98%, black)', '--wpds-color-foreground-interactive-brand-active': 'color-mix(in oklch, var(--wp-admin-theme-color, #3858e9) 52%, black)', '--wpds-color-foreground-interactive-brand-disabled': '#8d8d8d', '--wpds-color-foreground-interactive-brand-strong': '#fff', '--wpds-color-foreground-interactive-brand-strong-active': '#fff', '--wpds-color-foreground-interactive-brand-strong-disabled': '#8d8d8d', - '--wpds-color-foreground-interactive-error': '#cc1818', + '--wpds-color-foreground-interactive-error': '#be1616', '--wpds-color-foreground-interactive-error-active': '#470000', '--wpds-color-foreground-interactive-error-disabled': '#8d8d8d', '--wpds-color-foreground-interactive-error-strong': '#f2efef', '--wpds-color-foreground-interactive-error-strong-active': '#f2efef', '--wpds-color-foreground-interactive-error-strong-disabled': '#8d8d8d', - '--wpds-color-foreground-interactive-neutral': '#1e1e1e', + '--wpds-color-foreground-interactive-neutral': '#646464', '--wpds-color-foreground-interactive-neutral-active': '#1e1e1e', '--wpds-color-foreground-interactive-neutral-disabled': '#8d8d8d', '--wpds-color-foreground-interactive-neutral-strong': '#f0f0f0', diff --git a/packages/theme/src/color-ramps/index.ts b/packages/theme/src/color-ramps/index.ts index 4e2723765e7b18..1f0d9b3b849534 100644 --- a/packages/theme/src/color-ramps/index.ts +++ b/packages/theme/src/color-ramps/index.ts @@ -26,6 +26,7 @@ export function buildBgRamp( seed: string ) { const STEP_TO_PIN = 'surface2'; function getBgRampInfo( ramp: InternalRampResult ): { mainDirection: RampDirection; + backgroundRamp: InternalRampResult; pinLightness: { stepName: keyof Ramp; value: number; @@ -33,6 +34,7 @@ function getBgRampInfo( ramp: InternalRampResult ): { } { return { mainDirection: ramp.direction, + backgroundRamp: ramp, pinLightness: { stepName: STEP_TO_PIN, value: clampAccentScaleReferenceLightness( diff --git a/packages/theme/src/color-ramps/lib/build-foreground-scale.ts b/packages/theme/src/color-ramps/lib/build-foreground-scale.ts new file mode 100644 index 00000000000000..a0c04d2b699133 --- /dev/null +++ b/packages/theme/src/color-ramps/lib/build-foreground-scale.ts @@ -0,0 +1,439 @@ +import { + ColorSpace, + contrastAPCA, + get, + OKLab, + OKLCH, + OKLCH_sRGB as OklchSrgb, + sRGB, + to, + type PlainColorObject, +} from 'colorjs.io/fn'; +import { clampToGamut, getColorString, getContrast } from './color-utils.ts'; +import { UNIVERSAL_CONTRAST_TOPUP } from './constants.ts'; +import { taperChroma } from './taper-chroma.ts'; +import type { + ForegroundScaleConfig, + ForegroundRampStep, + RampResult, +} from './types.ts'; +import { solveWithBisect } from './utils.ts'; + +ColorSpace.register( sRGB ); +ColorSpace.register( OKLab ); +ColorSpace.register( OKLCH ); +ColorSpace.register( OklchSrgb ); + +type GetColorForLightness = ( lightness: number ) => PlainColorObject; + +function getChromaPreservingColorForLightness( + seed: PlainColorObject, + lightness: number +) { + return clampToGamut( { + space: OKLCH, + coords: [ + lightness, + get( seed, [ OKLCH, 'c' ] ), + get( seed, [ OKLCH, 'h' ] ), + ], + alpha: seed.alpha, + } ); +} + +function createColorForLightness( + seed: PlainColorObject, + config: ForegroundScaleConfig +): GetColorForLightness { + if ( config.chroma.mode === 'tapered' ) { + const taperOptions = config.chroma.options; + return ( lightness ) => { + const tapered = taperChroma( seed, lightness, taperOptions ); + if ( 'l' in tapered && 'c' in tapered ) { + return clampToGamut( { + space: OKLCH, + coords: [ + tapered.l, + tapered.c, + get( seed, [ OKLCH, 'h' ] ), + ], + alpha: seed.alpha, + } ); + } + + return clampToGamut( tapered ); + }; + } + + const relativeSeed = to( seed, OklchSrgb ); + const relativeChroma = get( relativeSeed, [ OklchSrgb, 'c' ] ); + const hue = get( relativeSeed, [ OklchSrgb, 'h' ] ); + const colorCache = new Map< number, PlainColorObject >(); + + return ( lightness ) => { + const cachedColor = colorCache.get( lightness ); + if ( cachedColor ) { + return cachedColor; + } + + const color = to( + { + space: OklchSrgb, + coords: [ lightness, relativeChroma, hue ], + alpha: seed.alpha, + }, + OKLCH + ); + colorCache.set( lightness, color ); + return color; + }; +} + +function getPerceptualContrastMagnitude( + background: PlainColorObject, + foreground: string | PlainColorObject +) { + return Math.abs( contrastAPCA( background, foreground ) ); +} + +function getConstraintReferences( + step: ForegroundScaleConfig[ 'steps' ][ number ], + ramp: RampResult, + backgroundRamp: RampResult +) { + return Array.from( + new Set( [ + ...step.contrast.references.map( ( name ) => ramp.ramp[ name ] ), + ...step.contrast.references.map( + ( name ) => backgroundRamp.ramp[ name ] + ), + ] ) + ); +} + +function getContrastTargetMargin( + color: PlainColorObject, + references: readonly string[], + target: number +) { + const adjustedTarget = target + UNIVERSAL_CONTRAST_TOPUP; + return Math.min( + ...references.map( ( reference ) => + Math.log( getContrast( reference, color ) / adjustedTarget ) + ) + ); +} + +function meetsContrastTarget( + color: PlainColorObject, + step: ForegroundScaleConfig[ 'steps' ][ number ], + ramp: RampResult, + backgroundRamp: RampResult +) { + return ( + getContrastTargetMargin( + color, + getConstraintReferences( step, ramp, backgroundRamp ), + step.contrast.target + ) >= 0 + ); +} + +function findColorAtContrastTarget( { + getColorAtLightness, + weakColor, + strongColor, + references, + target, +}: { + getColorAtLightness: GetColorForLightness; + weakColor: PlainColorObject; + strongColor: PlainColorObject; + references: readonly string[]; + target: number; +} ) { + const weakMargin = getContrastTargetMargin( weakColor, references, target ); + if ( weakMargin >= 0 ) { + return weakColor; + } + + const strongMargin = getContrastTargetMargin( + strongColor, + references, + target + ); + if ( strongMargin < 0 ) { + return strongColor; + } + + return solveWithBisect( + getColorAtLightness, + ( color ) => getContrastTargetMargin( color, references, target ), + get( weakColor, [ OKLCH, 'l' ] ), + weakMargin, + get( strongColor, [ OKLCH, 'l' ] ), + strongMargin + ); +} + +function findColorAtPerceptualContrast( { + background, + getColorAtLightness, + weakColor, + strongColor, + target, +}: { + background: PlainColorObject; + getColorAtLightness: GetColorForLightness; + weakColor: PlainColorObject; + strongColor: PlainColorObject; + target: number; +} ) { + const weakContrast = getPerceptualContrastMagnitude( + background, + weakColor + ); + if ( target <= weakContrast ) { + return weakColor; + } + + const strongLightness = get( strongColor, [ OKLCH, 'l' ] ); + const scaleStrongColor = getColorAtLightness( strongLightness ); + const strongContrast = getPerceptualContrastMagnitude( + background, + scaleStrongColor + ); + if ( target >= strongContrast ) { + return scaleStrongColor; + } + + return solveWithBisect( + getColorAtLightness, + ( color ) => + getPerceptualContrastMagnitude( background, color ) - target, + get( weakColor, [ OKLCH, 'l' ] ), + weakContrast - target, + strongLightness, + strongContrast - target + ); +} + +function serializeColorMeetingContrast( { + color, + getColorAtLightness, + strongColor, + references, + target, +}: { + color: PlainColorObject; + getColorAtLightness: GetColorForLightness; + strongColor: PlainColorObject; + references: readonly string[]; + target: number; +} ) { + const serializedColor = getColorString( color ); + if ( + references.every( + ( reference ) => getContrast( reference, serializedColor ) >= target + ) + ) { + return serializedColor; + } + + const colorLightness = get( color, [ OKLCH, 'l' ] ); + const strongLightness = get( strongColor, [ OKLCH, 'l' ] ); + const direction = Math.sign( strongLightness - colorLightness ); + const maximumOffset = Math.abs( strongLightness - colorLightness ); + + for ( let offset = 0.00025; offset < maximumOffset; offset += 0.00025 ) { + const strengthened = getColorString( + getColorAtLightness( colorLightness + direction * offset ) + ); + if ( + references.every( + ( reference ) => + getContrast( reference, strengthened ) >= target + ) + ) { + return strengthened; + } + } + + return getColorString( strongColor ); +} + +/** + * Rebuild the foreground part of an otherwise complete ramp. APCA positions + * the intermediate steps within the visible contrast range, while WCAG ratios + * remain hard floors across the surfaces where each step is used. + * + * @param ramp Ramp whose foreground steps will be rebuilt. + * @param backgroundRamp Background ramp on which the colors will appear. + * @param config Foreground scale anchors, positions, and constraints. + */ +export function buildForegroundScale( + ramp: RampResult, + backgroundRamp: RampResult, + config: ForegroundScaleConfig +): RampResult { + const seed = clampToGamut( ramp.ramp[ config.seed ] ); + const getColorAtLightness = createColorForLightness( seed, config ); + // Parse and convert this once. APCA evaluates it for every intermediate + // foreground candidate. + const displayBackground = to( + clampToGamut( backgroundRamp.ramp[ config.perceptualReference ] ), + sRGB + ); + const strongStep = config.steps.at( -1 )!; + const currentStrongColor = clampToGamut( ramp.ramp[ strongStep.name ] ); + const strongEndpoint = getColorAtLightness( + ramp.direction === 'lighter' ? 1 : 0 + ); + const strongColor = meetsContrastTarget( + currentStrongColor, + strongStep, + ramp, + backgroundRamp + ) + ? currentStrongColor + : findColorAtContrastTarget( { + getColorAtLightness, + weakColor: currentStrongColor, + strongColor: strongEndpoint, + references: getConstraintReferences( + strongStep, + ramp, + backgroundRamp + ), + target: strongStep.contrast.target, + } ); + const colors = new Map< ForegroundRampStep, PlainColorObject >(); + + for ( const step of config.steps.filter( + ( { preserveAnchor } ) => preserveAnchor + ) ) { + if ( step === strongStep ) { + colors.set( step.name, strongColor ); + continue; + } + + const currentColor = clampToGamut( ramp.ramp[ step.name ] ); + colors.set( + step.name, + meetsContrastTarget( currentColor, step, ramp, backgroundRamp ) + ? currentColor + : findColorAtContrastTarget( { + getColorAtLightness: ( lightness ) => + getChromaPreservingColorForLightness( + currentColor, + lightness + ), + weakColor: currentColor, + strongColor, + references: getConstraintReferences( + step, + ramp, + backgroundRamp + ), + target: step.contrast.target, + } ) + ); + } + + const firstStep = config.steps[ 0 ]; + const firstColor = colors.get( firstStep.name )!; + const weakContrast = getPerceptualContrastMagnitude( + displayBackground, + firstColor + ); + const strongContrast = getPerceptualContrastMagnitude( + displayBackground, + strongColor + ); + + for ( const step of config.steps.filter( + ( { preserveAnchor } ) => ! preserveAnchor + ) ) { + const stepIndex = config.steps.indexOf( step ); + const previousStep = config.steps[ stepIndex - 1 ]; + const previousColor = colors.get( previousStep.name )!; + const target = + weakContrast + ( strongContrast - weakContrast ) * step.progress; + const previousContrast = getPerceptualContrastMagnitude( + displayBackground, + previousColor + ); + const candidate = + target <= previousContrast + ? previousColor + : findColorAtPerceptualContrast( { + background: displayBackground, + getColorAtLightness, + weakColor: getColorAtLightness( + get( previousColor, [ OKLCH, 'l' ] ) + ), + strongColor, + target, + } ); + + colors.set( + step.name, + meetsContrastTarget( candidate, step, ramp, backgroundRamp ) + ? candidate + : findColorAtContrastTarget( { + getColorAtLightness, + weakColor: candidate, + strongColor, + references: getConstraintReferences( + step, + ramp, + backgroundRamp + ), + target: step.contrast.target, + } ) + ); + } + + const nextRamp = { ...ramp.ramp }; + for ( const step of config.steps ) { + const color = colors.get( step.name )!; + nextRamp[ step.name ] = serializeColorMeetingContrast( { + color, + getColorAtLightness: + step.preserveAnchor && step !== strongStep + ? ( lightness ) => + getChromaPreservingColorForLightness( + color, + lightness + ) + : getColorAtLightness, + strongColor, + references: getConstraintReferences( step, ramp, backgroundRamp ), + target: step.contrast.target, + } ); + } + + const foregroundStepNames = new Set< keyof typeof ramp.ramp >( + config.steps.map( ( { name } ) => name ) + ); + const warnings = ( ramp.warnings ?? [] ).filter( + ( step ) => ! foregroundStepNames.has( step ) + ); + for ( const step of config.steps ) { + if ( + getConstraintReferences( step, ramp, backgroundRamp ).some( + ( reference ) => + getContrast( reference, nextRamp[ step.name ] ) < + step.contrast.target + ) + ) { + warnings.push( step.name ); + } + } + + return { + ...ramp, + ramp: nextRamp, + warnings: warnings.length > 0 ? warnings : undefined, + }; +} diff --git a/packages/theme/src/color-ramps/lib/constants.ts b/packages/theme/src/color-ramps/lib/constants.ts index bebd29f9639b9e..5020d94622250c 100644 --- a/packages/theme/src/color-ramps/lib/constants.ts +++ b/packages/theme/src/color-ramps/lib/constants.ts @@ -47,12 +47,12 @@ export const CONTRAST_COMBINATIONS: { }[] = [ { bgs: [ 'surface1', 'surface2', 'surface3' ], - fgs: [ 'fgSurface3', 'fgSurface4' ], + fgs: [ 'fgSurface3', 'fgSurface4', 'fgSurface5' ], target: 4.5, }, { bgs: [ 'surface4', 'surface5' ], - fgs: [ 'fgSurface4' ], + fgs: [ 'fgSurface4', 'fgSurface5' ], target: 4.5, }, { diff --git a/packages/theme/src/color-ramps/lib/default-ramps.ts b/packages/theme/src/color-ramps/lib/default-ramps.ts index 6414d3de1c5d87..c0f2c48e04e64d 100644 --- a/packages/theme/src/color-ramps/lib/default-ramps.ts +++ b/packages/theme/src/color-ramps/lib/default-ramps.ts @@ -15,7 +15,8 @@ export const DEFAULT_RAMPS: Record< surface5: '#e6e6e6', surface4: '#ededed', surface3: '#fff', - fgSurface4: '#1e1e1e', + fgSurface5: '#1e1e1e', + fgSurface4: '#646464', fgSurface3: '#707070', fgSurface2: '#8d8d8d', fgSurface1: '#aeaeae', @@ -42,8 +43,9 @@ export const DEFAULT_RAMPS: Record< surface5: '#dce3f4', surface4: '#e5eaf7', surface3: '#fff', - fgSurface4: '#0b0070', - fgSurface3: '#3858e9', + fgSurface5: '#0b0070', + fgSurface4: '#3351e8', + fgSurface3: '#3e60ea', fgSurface2: '#5a82ff', fgSurface1: '#86a9ff', stroke3: '#3858e9', @@ -69,8 +71,9 @@ export const DEFAULT_RAMPS: Record< surface5: '#d3e4f7', surface4: '#deebfa', surface3: '#fff', - fgSurface4: '#001b4f', - fgSurface3: '#006bd7', + fgSurface5: '#001b4f', + fgSurface4: '#0063b2', + fgSurface3: '#006ec6', fgSurface2: '#008bfa', fgSurface1: '#59b0ff', stroke3: '#006bd7', @@ -177,8 +180,9 @@ export const DEFAULT_RAMPS: Record< surface5: '#f7dbd7', surface4: '#fae5e1', surface3: '#fff', - fgSurface4: '#470000', - fgSurface3: '#cc1818', + fgSurface5: '#470000', + fgSurface4: '#be1616', + fgSurface3: '#d71a1a', fgSurface2: '#f74c40', fgSurface1: '#ff8879', stroke3: '#cc1818', diff --git a/packages/theme/src/color-ramps/lib/index.ts b/packages/theme/src/color-ramps/lib/index.ts index b254e11e955d7e..f1577f7c37eb61 100644 --- a/packages/theme/src/color-ramps/lib/index.ts +++ b/packages/theme/src/color-ramps/lib/index.ts @@ -6,6 +6,7 @@ import { parseSeedColor, } from './color-utils.ts'; import { findColorMeetingRequirements } from './find-color-with-constraints.ts'; +import { buildForegroundScale } from './build-foreground-scale.ts'; import { sortByDependency, computeBetterFgColorDirection, @@ -19,6 +20,7 @@ import type { RampDirection, RampConfig, RampResult, + RampStepsConfig, } from './types.ts'; import { BLACK, WHITE, CONTRAST_EPSILON } from './constants.ts'; @@ -46,7 +48,7 @@ function calculateRamp( { }: { seed: PlainColorObject; sortedSteps: ( keyof Ramp )[]; - config: RampConfig; + config: RampStepsConfig; mainDir: RampDirection; oppDir: RampDirection; pinLightness?: { @@ -212,6 +214,7 @@ export function buildRamp( { mainDirection, pinLightness, + backgroundRamp, rescaleToFitContrastTargets = true, }: { mainDirection?: RampDirection; @@ -219,6 +222,7 @@ export function buildRamp( stepName: keyof Ramp; value: number; }; + backgroundRamp?: RampResult; rescaleToFitContrastTargets?: boolean; } = {} ): RampResult { @@ -249,7 +253,7 @@ export function buildRamp( } // Get the correct calculation order based on dependencies - const sortedSteps = sortByDependency( config ); + const sortedSteps = sortByDependency( config.steps ); // Calculate the ramp with the initial seed. const { @@ -261,7 +265,7 @@ export function buildRamp( } = calculateRamp( { seed, sortedSteps, - config, + config: config.steps, mainDir, oppDir, pinLightness, @@ -271,7 +275,7 @@ export function buildRamp( let bestWarnings = warnings; if ( maxDeficit > CONTRAST_EPSILON && rescaleToFitContrastTargets ) { - const iterSteps = stepsForStep( maxDeficitStep!, config ); + const iterSteps = stepsForStep( maxDeficitStep!, config.steps ); function getSeedForL( l: number ): PlainColorObject { return clampToGamut( set( clone( seed ), [ OKLCH, 'l' ], l ) ); @@ -281,7 +285,7 @@ export function buildRamp( const iterationResults = calculateRamp( { seed: s, sortedSteps: iterSteps, - config, + config: config.steps, mainDir, oppDir, pinLightness, @@ -316,7 +320,7 @@ export function buildRamp( const finalResult = calculateRamp( { seed: bestSeed, sortedSteps, - config, + config: config.steps, mainDir, oppDir, pinLightness, @@ -334,9 +338,15 @@ export function buildRamp( bestRamp.surface3 = tmpSurface1; } - return { + const rampResult = { ramp: bestRamp, warnings: bestWarnings, direction: mainDir, }; + + return buildForegroundScale( + rampResult, + backgroundRamp ?? rampResult, + config.foregroundScale + ); } diff --git a/packages/theme/src/color-ramps/lib/ramp-configs.ts b/packages/theme/src/color-ramps/lib/ramp-configs.ts index 2e8f002159a896..7eb1ed72340a93 100644 --- a/packages/theme/src/color-ramps/lib/ramp-configs.ts +++ b/packages/theme/src/color-ramps/lib/ramp-configs.ts @@ -39,7 +39,9 @@ const ACCENT_SURFACE_TAPER_CHROMA: TaperChromaOptions = { radiusLight: 0.01, }; -const fgSurface4Config: RampStepConfig = { +const fgSurface5Config: RampStepConfig = { + // Keep the former fgSurface4 endpoint in the base solver. The foreground + // scale pass exposes it as fgSurface5 and inserts a new state step before it. contrast: { reference: 'surface3', followDirection: 'main', @@ -50,7 +52,62 @@ const fgSurface4Config: RampStepConfig = { taperChromaOptions: FG_TAPER_CHROMA, }; -export const BG_RAMP_CONFIG: RampConfig = { +const FOREGROUND_SCALE_STEPS = [ + { + name: 'fgSurface1', + progress: 0, + preserveAnchor: true, + contrast: { references: [ 'surface3' ], target: 2 }, + }, + { + name: 'fgSurface2', + progress: 0.2, + preserveAnchor: true, + contrast: { references: [ 'surface3' ], target: 3 }, + }, + { + name: 'fgSurface3', + // APCA positions this step at 40% of the visible contrast range. WCAG + // contrast remains the hard minimum against every listed surface. + progress: 0.4, + contrast: { + references: [ 'surface1', 'surface2', 'surface3' ], + target: 4.5, + }, + }, + { + name: 'fgSurface4', + // Leave the final 40% of the visible contrast range for the active state. + progress: 0.6, + contrast: { + references: [ + 'surface1', + 'surface2', + 'surface3', + 'surface4', + 'surface5', + ], + target: 4.5, + }, + }, + { + name: 'fgSurface5', + progress: 1, + preserveAnchor: true, + contrast: { + references: [ + 'surface1', + 'surface2', + 'surface3', + 'surface4', + 'surface5', + ], + target: 4.5, + }, + }, +] as const; + +const BG_RAMP_STEPS: RampConfig[ 'steps' ] = { // Surface surface1: { contrast: { @@ -123,7 +180,7 @@ export const BG_RAMP_CONFIG: RampConfig = { target: 1.2, }, }, - bgFillInverted2: fgSurface4Config, + bgFillInverted2: fgSurface5Config, bgFillDark: { contrast: { reference: 'surface3', @@ -196,7 +253,17 @@ export const BG_RAMP_CONFIG: RampConfig = { lightness: lightnessConstraintForegroundMediumContrast, taperChromaOptions: FG_TAPER_CHROMA, }, - fgSurface4: fgSurface4Config, + fgSurface4: { + contrast: { + reference: 'surface3', + followDirection: 'main', + target: 4.5, + preferLighter: true, + }, + taperChromaOptions: FG_TAPER_CHROMA, + sameAsIfPossible: 'fgSurface5', + }, + fgSurface5: fgSurface5Config, // fgFill fgFill: { contrast: { @@ -231,77 +298,102 @@ export const BG_RAMP_CONFIG: RampConfig = { }, }; +export const BG_RAMP_CONFIG: RampConfig = { + steps: BG_RAMP_STEPS, + foregroundScale: { + seed: 'surface2', + perceptualReference: 'surface2', + chroma: { + mode: 'tapered', + options: FG_TAPER_CHROMA, + }, + steps: FOREGROUND_SCALE_STEPS, + }, +}; + // BG_RAMP: seed => surface2 => {bgFill, surface3 => all other tokens} // ACCENT_RAMP: seed => bgFill1 => surface2 => surface3 => all other tokens export const ACCENT_RAMP_CONFIG: RampConfig = { - ...BG_RAMP_CONFIG, - surface1: { - ...BG_RAMP_CONFIG.surface1, - taperChromaOptions: ACCENT_SURFACE_TAPER_CHROMA, - }, - surface2: { - contrast: { - reference: 'bgFill1', - followDirection: 'opposite', - target: BG_RAMP_CONFIG.bgFill1.contrast.target, - ignoreWhenAdjustingSeed: true, + steps: { + ...BG_RAMP_CONFIG.steps, + surface1: { + ...BG_RAMP_CONFIG.steps.surface1, + taperChromaOptions: ACCENT_SURFACE_TAPER_CHROMA, }, - taperChromaOptions: ACCENT_SURFACE_TAPER_CHROMA, - }, - surface3: { - ...BG_RAMP_CONFIG.surface3, - taperChromaOptions: ACCENT_SURFACE_TAPER_CHROMA, - }, - surface4: { - ...BG_RAMP_CONFIG.surface4, - taperChromaOptions: ACCENT_SURFACE_TAPER_CHROMA, - }, - surface5: { - ...BG_RAMP_CONFIG.surface5, - taperChromaOptions: ACCENT_SURFACE_TAPER_CHROMA, - }, - surface6: { - ...BG_RAMP_CONFIG.surface6, - taperChromaOptions: ACCENT_SURFACE_TAPER_CHROMA, - }, - bgFill1: { - contrast: { - reference: 'seed', - followDirection: 'main', - target: 1, + surface2: { + contrast: { + reference: 'bgFill1', + followDirection: 'opposite', + target: BG_RAMP_CONFIG.steps.bgFill1.contrast.target, + ignoreWhenAdjustingSeed: true, + }, + taperChromaOptions: ACCENT_SURFACE_TAPER_CHROMA, + }, + surface3: { + ...BG_RAMP_CONFIG.steps.surface3, + taperChromaOptions: ACCENT_SURFACE_TAPER_CHROMA, + }, + surface4: { + ...BG_RAMP_CONFIG.steps.surface4, + taperChromaOptions: ACCENT_SURFACE_TAPER_CHROMA, + }, + surface5: { + ...BG_RAMP_CONFIG.steps.surface5, + taperChromaOptions: ACCENT_SURFACE_TAPER_CHROMA, + }, + surface6: { + ...BG_RAMP_CONFIG.steps.surface6, + taperChromaOptions: ACCENT_SURFACE_TAPER_CHROMA, + }, + bgFill1: { + contrast: { + reference: 'seed', + followDirection: 'main', + target: 1, + }, + }, + stroke1: { + ...BG_RAMP_CONFIG.steps.stroke1, + }, + stroke2: { + ...BG_RAMP_CONFIG.steps.stroke2, + }, + stroke3: { + ...BG_RAMP_CONFIG.steps.stroke3, + sameAsIfPossible: 'fgSurface3', + taperChromaOptions: undefined, + }, + stroke4: { + ...BG_RAMP_CONFIG.steps.stroke4, + taperChromaOptions: undefined, + }, + // fgSurface: do not de-saturate + fgSurface1: { + ...BG_RAMP_CONFIG.steps.fgSurface1, + taperChromaOptions: undefined, + }, + fgSurface2: { + ...BG_RAMP_CONFIG.steps.fgSurface2, + taperChromaOptions: undefined, + }, + fgSurface3: { + ...BG_RAMP_CONFIG.steps.fgSurface3, + taperChromaOptions: undefined, + sameAsIfPossible: 'bgFill1', + }, + fgSurface4: { + ...BG_RAMP_CONFIG.steps.fgSurface4, + taperChromaOptions: undefined, + }, + fgSurface5: { + ...BG_RAMP_CONFIG.steps.fgSurface5, + taperChromaOptions: undefined, }, }, - stroke1: { - ...BG_RAMP_CONFIG.stroke1, - }, - stroke2: { - ...BG_RAMP_CONFIG.stroke2, - }, - stroke3: { - ...BG_RAMP_CONFIG.stroke3, - sameAsIfPossible: 'fgSurface3', - taperChromaOptions: undefined, - }, - stroke4: { - ...BG_RAMP_CONFIG.stroke4, - taperChromaOptions: undefined, - }, - // fgSurface: do not de-saturate - fgSurface1: { - ...BG_RAMP_CONFIG.fgSurface1, - taperChromaOptions: undefined, - }, - fgSurface2: { - ...BG_RAMP_CONFIG.fgSurface2, - taperChromaOptions: undefined, - }, - fgSurface3: { - ...BG_RAMP_CONFIG.fgSurface3, - taperChromaOptions: undefined, - sameAsIfPossible: 'bgFill1', - }, - fgSurface4: { - ...BG_RAMP_CONFIG.fgSurface4, - taperChromaOptions: undefined, + foregroundScale: { + seed: 'bgFill1', + perceptualReference: 'surface2', + chroma: { mode: 'gamut-relative' }, + steps: FOREGROUND_SCALE_STEPS, }, }; diff --git a/packages/theme/src/color-ramps/lib/types.ts b/packages/theme/src/color-ramps/lib/types.ts index 48b4a5e188b048..6c29554a4a57cb 100644 --- a/packages/theme/src/color-ramps/lib/types.ts +++ b/packages/theme/src/color-ramps/lib/types.ts @@ -24,6 +24,7 @@ export type Ramp = { fgSurface2: string; fgSurface3: string; fgSurface4: string; + fgSurface5: string; // Foreground (text, icon) colors on top of bgFill fgFill: string; fgFillInverted: string; @@ -79,7 +80,45 @@ export type RampStepConfig = { sameAsIfPossible?: keyof Ramp; }; -export type RampConfig = Record< keyof Ramp, RampStepConfig >; +export type RampStepsConfig = Record< keyof Ramp, RampStepConfig >; + +export type ForegroundRampStep = + | 'fgSurface1' + | 'fgSurface2' + | 'fgSurface3' + | 'fgSurface4' + | 'fgSurface5'; + +export type ForegroundScaleConfig = { + /** Ramp step whose hue and chroma define the foreground scale. */ + seed: keyof Ramp; + /** Background step used to measure the APCA contrast range. */ + perceptualReference: keyof Ramp; + chroma: + | { + mode: 'tapered'; + options: TaperChromaOptions; + } + | { + mode: 'gamut-relative'; + }; + steps: readonly { + name: ForegroundRampStep; + /** Position in the APCA contrast range from the first to last step. */ + progress: number; + /** Preserve the base solver's color when it meets every WCAG floor. */ + preserveAnchor?: boolean; + contrast: { + references: readonly ( keyof Ramp )[]; + target: number; + }; + }[]; +}; + +export type RampConfig = { + steps: RampStepsConfig; + foregroundScale: ForegroundScaleConfig; +}; export type RampResult = { ramp: Record< keyof Ramp, string >; diff --git a/packages/theme/src/color-ramps/lib/utils.ts b/packages/theme/src/color-ramps/lib/utils.ts index 5888b2e24e1864..9f75e8320d5f37 100644 --- a/packages/theme/src/color-ramps/lib/utils.ts +++ b/packages/theme/src/color-ramps/lib/utils.ts @@ -8,14 +8,14 @@ import { MAX_BISECTION_ITERATIONS, CONTRAST_EPSILON, } from './constants.ts'; -import type { Ramp, RampConfig, RampDirection } from './types.ts'; +import type { Ramp, RampStepsConfig, RampDirection } from './types.ts'; import { getContrast } from './color-utils.ts'; /** * Build a dependency graph from the steps configuration * @param config - The steps configuration object */ -function buildDependencyGraph( config: RampConfig ): { +function buildDependencyGraph( config: RampStepsConfig ): { dependencies: Map< keyof Ramp, ( keyof Ramp | 'seed' )[] >; dependents: Map< keyof Ramp | 'seed', ( keyof Ramp )[] >; } { @@ -58,7 +58,7 @@ function buildDependencyGraph( config: RampConfig ): { * Topologically sort steps based on their dependencies * @param config - The steps configuration object */ -export function sortByDependency( config: RampConfig ): ( keyof Ramp )[] { +export function sortByDependency( config: RampStepsConfig ): ( keyof Ramp )[] { const { dependents } = buildDependencyGraph( config ); const result: ( keyof Ramp )[] = []; const visited = new Set< keyof Ramp | 'seed' >(); @@ -106,7 +106,7 @@ export function sortByDependency( config: RampConfig ): ( keyof Ramp )[] { */ export function stepsForStep( stepName: keyof Ramp, - config: RampConfig + config: RampStepsConfig ): ( keyof Ramp )[] { const result = new Set< keyof Ramp >(); function visit( step: keyof Ramp | 'seed' ) { diff --git a/packages/theme/src/color-ramps/stories/ramp-table.tsx b/packages/theme/src/color-ramps/stories/ramp-table.tsx index 019ab49eb2fbe0..af6dc6f92ec3a3 100644 --- a/packages/theme/src/color-ramps/stories/ramp-table.tsx +++ b/packages/theme/src/color-ramps/stories/ramp-table.tsx @@ -29,6 +29,7 @@ const RAMP_TOKENS_ORDER: { tokenName: keyof Ramp; abbr: string }[] = [ { tokenName: 'fgSurface2', abbr: 'FGS2' }, { tokenName: 'fgSurface3', abbr: 'FGS3' }, { tokenName: 'fgSurface4', abbr: 'FGS4' }, + { tokenName: 'fgSurface5', abbr: 'FGS5' }, { tokenName: 'fgFill', abbr: 'FGF' }, { tokenName: 'fgFillInverted', abbr: 'FGFI' }, { tokenName: 'fgFillDark', abbr: 'FGFD' }, @@ -337,6 +338,11 @@ export const RampTable = forwardRef< HTMLDivElement, RampTableProps >( background={ tokenName } ramp={ ramp } /> + ) : null } { tokenName === 'bgFill1' ? ( diff --git a/packages/theme/src/color-ramps/test/__snapshots__/index.test.ts.snap b/packages/theme/src/color-ramps/test/__snapshots__/index.test.ts.snap index 11a300367a2564..c1672aff99d9ea 100644 --- a/packages/theme/src/color-ramps/test/__snapshots__/index.test.ts.snap +++ b/packages/theme/src/color-ramps/test/__snapshots__/index.test.ts.snap @@ -1553,9 +1553,10 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgFillDark": "#f0f0f0", "fgFillInverted": "#1e1e1e", "fgSurface1": "#454545", - "fgSurface2": "#5e5e5e", - "fgSurface3": "#d1d1d1", - "fgSurface4": "#f0f0f0", + "fgSurface2": "#5f5f5f", + "fgSurface3": "#959595", + "fgSurface4": "#b6b6b6", + "fgSurface5": "#f0f0f0", "stroke1": "#787878", "stroke2": "#858585", "stroke3": "#d1d1d1", @@ -1596,8 +1597,9 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgFillInverted": "#1e1e1e", "fgSurface1": "#464646", "fgSurface2": "#606060", - "fgSurface3": "#d1d1d1", - "fgSurface4": "#f0f0f0", + "fgSurface3": "#969696", + "fgSurface4": "#b6b6b6", + "fgSurface5": "#f0f0f0", "stroke1": "#787878", "stroke2": "#858585", "stroke3": "#d1d1d1", @@ -1636,10 +1638,11 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgFill": "#1e1e1e", "fgFillDark": "#f0f0f0", "fgFillInverted": "#1e1e1e", - "fgSurface1": "#4e4e4e", + "fgSurface1": "#4f4f4f", "fgSurface2": "#696969", - "fgSurface3": "#d1d1d1", - "fgSurface4": "#f0f0f0", + "fgSurface3": "#999", + "fgSurface4": "#b8b8b8", + "fgSurface5": "#f0f0f0", "stroke1": "#787878", "stroke2": "#858585", "stroke3": "#d1d1d1", @@ -1680,8 +1683,9 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgFillInverted": "#1e1e1e", "fgSurface1": "#606060", "fgSurface2": "#7c7c7c", - "fgSurface3": "#d1d1d1", - "fgSurface4": "#f0f0f0", + "fgSurface3": "#a1a1a1", + "fgSurface4": "#bdbdbd", + "fgSurface5": "#f0f0f0", "stroke1": "#787878", "stroke2": "#858585", "stroke3": "#d1d1d1", @@ -1722,8 +1726,9 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgFillInverted": "#1e1e1e", "fgSurface1": "#7b7b7b", "fgSurface2": "#9a9a9a", - "fgSurface3": "#d2d2d2", - "fgSurface4": "#f0f0f0", + "fgSurface3": "#bcbcbc", + "fgSurface4": "#c8c8c8", + "fgSurface5": "#f0f0f0", "stroke1": "#787878", "stroke2": "#858585", "stroke3": "#d2d2d2", @@ -1765,7 +1770,8 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgSurface1": "#656565", "fgSurface2": "#4b4b4b", "fgSurface3": "#313131", - "fgSurface4": "#050505", + "fgSurface4": "#282828", + "fgSurface5": "#050505", "stroke1": "#777", "stroke2": "#6a6a6a", "stroke3": "#313131", @@ -1807,7 +1813,8 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgSurface1": "#7e7e7e", "fgSurface2": "#626262", "fgSurface3": "#484848", - "fgSurface4": "#1e1e1e", + "fgSurface4": "#404040", + "fgSurface5": "#1e1e1e", "stroke1": "#929292", "stroke2": "#838383", "stroke3": "#484848", @@ -1849,7 +1856,8 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgSurface1": "#969696", "fgSurface2": "#787878", "fgSurface3": "#5d5d5d", - "fgSurface4": "#1e1e1e", + "fgSurface4": "#555", + "fgSurface5": "#1e1e1e", "stroke1": "#acacac", "stroke2": "#9d9d9d", "stroke3": "#5d5d5d", @@ -1891,7 +1899,8 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#717171", - "fgSurface4": "#1e1e1e", + "fgSurface4": "#656565", + "fgSurface5": "#1e1e1e", "stroke1": "#c9c9c9", "stroke2": "#b7b7b7", "stroke3": "#717171", @@ -2938,7 +2947,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "ramp": { "bgFill1": "#4ab866", "bgFill2": "#37a756", - "bgFillDark": "#1b1f1c", + "bgFillDark": "#1c1f1c", "bgFillInverted1": "#003701", "bgFillInverted2": "#1b1f1c", "fgFill": "#1b1f1c", @@ -2980,7 +2989,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "ramp": { "bgFill1": "#4ab866", "bgFill2": "#37a756", - "bgFillDark": "#1b1f1c", + "bgFillDark": "#1c1f1c", "bgFillInverted1": "#003701", "bgFillInverted2": "#1b1f1c", "fgFill": "#1b1f1c", @@ -3022,7 +3031,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "ramp": { "bgFill1": "#4ab866", "bgFill2": "#37a756", - "bgFillDark": "#1b1f1c", + "bgFillDark": "#1c1f1c", "bgFillInverted1": "#003701", "bgFillInverted2": "#1b1f1c", "fgFill": "#1b1f1c", @@ -3198,7 +3207,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgFill": "#1f1e1b", "fgFillDark": "#faf1ca", "fgFillInverted": "#1f1e1b", - "fgSurface1": "#745e00", + "fgSurface1": "#745e01", "fgSurface2": "#957a00", "fgSurface3": "#f0d148", "fgSurface4": "#fff075", @@ -3240,7 +3249,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgFill": "#1f1e1b", "fgFillDark": "#faf1ca", "fgFillInverted": "#1f1e1b", - "fgSurface1": "#947900", + "fgSurface1": "#947901", "fgSurface2": "#b69800", "fgSurface3": "#f0d148", "fgSurface4": "#fff075", @@ -3326,10 +3335,11 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgFillInverted": "#faf1c9", "fgSurface1": "#977c00", "fgSurface2": "#776000", - "fgSurface3": "#594700", - "fgSurface4": "#281d00", - "stroke1": "#9d9268", - "stroke2": "#8d845d", + "fgSurface3": "#544814", + "fgSurface4": "#4b4011", + "fgSurface5": "#281d00", + "stroke1": "#9c9269", + "stroke2": "#8e845c", "stroke3": "#594700", "stroke4": "#392b00", "surface1": "#c8b76f", @@ -3706,10 +3716,11 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgFillInverted": "#f5efe5", "fgSurface1": "#a87300", "fgSurface2": "#855900", - "fgSurface3": "#644100", - "fgSurface4": "#2e1900", - "stroke1": "#a58e67", - "stroke2": "#95805d", + "fgSurface3": "#5b4416", + "fgSurface4": "#523c13", + "fgSurface5": "#2e1900", + "stroke1": "#a48e69", + "stroke2": "#94805f", "stroke3": "#644100", "stroke4": "#402700", "surface1": "#d5b16f", @@ -15597,7 +15608,8 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#717171", - "fgSurface4": "#1e1e1e", + "fgSurface4": "#656565", + "fgSurface5": "#1e1e1e", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", @@ -15632,7 +15644,8 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#717171", - "fgSurface4": "#1e1e1e", + "fgSurface4": "#656565", + "fgSurface5": "#1e1e1e", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", @@ -15667,7 +15680,8 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#717171", - "fgSurface4": "#1e1e1e", + "fgSurface4": "#656565", + "fgSurface5": "#1e1e1e", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", @@ -15702,7 +15716,8 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#717171", - "fgSurface4": "#1e1e1e", + "fgSurface4": "#656565", + "fgSurface5": "#1e1e1e", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", @@ -15737,7 +15752,8 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#717171", - "fgSurface4": "#1e1e1e", + "fgSurface4": "#656565", + "fgSurface5": "#1e1e1e", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", @@ -15772,7 +15788,8 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#717171", - "fgSurface4": "#1e1e1e", + "fgSurface4": "#656565", + "fgSurface5": "#1e1e1e", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", @@ -15807,7 +15824,8 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#717171", - "fgSurface4": "#1e1e1e", + "fgSurface4": "#656565", + "fgSurface5": "#1e1e1e", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", @@ -15842,7 +15860,8 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#717171", - "fgSurface4": "#1e1e1e", + "fgSurface4": "#656565", + "fgSurface5": "#1e1e1e", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", @@ -15877,7 +15896,8 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#717171", - "fgSurface4": "#1e1e1e", + "fgSurface4": "#656565", + "fgSurface5": "#1e1e1e", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", @@ -15912,7 +15932,8 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#717171", - "fgSurface4": "#1e1e1e", + "fgSurface4": "#656565", + "fgSurface5": "#1e1e1e", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", @@ -15947,7 +15968,8 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#717171", - "fgSurface4": "#1e1e1e", + "fgSurface4": "#656565", + "fgSurface5": "#1e1e1e", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", @@ -15982,7 +16004,8 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#717171", - "fgSurface4": "#1e1e1e", + "fgSurface4": "#656565", + "fgSurface5": "#1e1e1e", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", @@ -16017,7 +16040,8 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#717171", - "fgSurface4": "#1e1e1e", + "fgSurface4": "#656565", + "fgSurface5": "#1e1e1e", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", @@ -16052,7 +16076,8 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#717171", - "fgSurface4": "#1e1e1e", + "fgSurface4": "#656565", + "fgSurface5": "#1e1e1e", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", @@ -16087,7 +16112,8 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#717171", - "fgSurface4": "#1e1e1e", + "fgSurface4": "#656565", + "fgSurface5": "#1e1e1e", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", @@ -16122,7 +16148,8 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#717171", - "fgSurface4": "#1e1e1e", + "fgSurface4": "#656565", + "fgSurface5": "#1e1e1e", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", @@ -16157,7 +16184,8 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#717171", - "fgSurface4": "#1e1e1e", + "fgSurface4": "#656565", + "fgSurface5": "#1e1e1e", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", @@ -16192,7 +16220,8 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#717171", - "fgSurface4": "#1e1e1e", + "fgSurface4": "#656565", + "fgSurface5": "#1e1e1e", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", @@ -16227,7 +16256,8 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#717171", - "fgSurface4": "#1e1e1e", + "fgSurface4": "#656565", + "fgSurface5": "#1e1e1e", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", @@ -16262,7 +16292,8 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#717171", - "fgSurface4": "#1e1e1e", + "fgSurface4": "#656565", + "fgSurface5": "#1e1e1e", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", @@ -16297,7 +16328,8 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#717171", - "fgSurface4": "#1e1e1e", + "fgSurface4": "#656565", + "fgSurface5": "#1e1e1e", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", @@ -16332,7 +16364,8 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#717171", - "fgSurface4": "#1e1e1e", + "fgSurface4": "#656565", + "fgSurface5": "#1e1e1e", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", @@ -16367,7 +16400,8 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#717171", - "fgSurface4": "#1e1e1e", + "fgSurface4": "#656565", + "fgSurface5": "#1e1e1e", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", @@ -16402,7 +16436,8 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#717171", - "fgSurface4": "#1e1e1e", + "fgSurface4": "#656565", + "fgSurface5": "#1e1e1e", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", @@ -16437,7 +16472,8 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#717171", - "fgSurface4": "#1e1e1e", + "fgSurface4": "#656565", + "fgSurface5": "#1e1e1e", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", @@ -16472,7 +16508,8 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#717171", - "fgSurface4": "#1e1e1e", + "fgSurface4": "#656565", + "fgSurface5": "#1e1e1e", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", @@ -16507,7 +16544,8 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#717171", - "fgSurface4": "#1e1e1e", + "fgSurface4": "#656565", + "fgSurface5": "#1e1e1e", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", @@ -16542,7 +16580,8 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#717171", - "fgSurface4": "#1e1e1e", + "fgSurface4": "#656565", + "fgSurface5": "#1e1e1e", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", @@ -16577,7 +16616,8 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#717171", - "fgSurface4": "#1e1e1e", + "fgSurface4": "#656565", + "fgSurface5": "#1e1e1e", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", @@ -16612,7 +16652,8 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#717171", - "fgSurface4": "#1e1e1e", + "fgSurface4": "#656565", + "fgSurface5": "#1e1e1e", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", @@ -16647,7 +16688,8 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#717171", - "fgSurface4": "#1e1e1e", + "fgSurface4": "#656565", + "fgSurface5": "#1e1e1e", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", @@ -16682,7 +16724,8 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#717171", - "fgSurface4": "#1e1e1e", + "fgSurface4": "#656565", + "fgSurface5": "#1e1e1e", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", @@ -16717,7 +16760,8 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#717171", - "fgSurface4": "#1e1e1e", + "fgSurface4": "#656565", + "fgSurface5": "#1e1e1e", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", @@ -16752,7 +16796,8 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#717171", - "fgSurface4": "#1e1e1e", + "fgSurface4": "#656565", + "fgSurface5": "#1e1e1e", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", @@ -16787,7 +16832,8 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#717171", - "fgSurface4": "#1e1e1e", + "fgSurface4": "#656565", + "fgSurface5": "#1e1e1e", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", @@ -16822,7 +16868,8 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#717171", - "fgSurface4": "#1e1e1e", + "fgSurface4": "#656565", + "fgSurface5": "#1e1e1e", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", diff --git a/packages/theme/src/color-ramps/test/index.test.ts b/packages/theme/src/color-ramps/test/index.test.ts index f21cf530a3f473..7258b563db2612 100644 --- a/packages/theme/src/color-ramps/test/index.test.ts +++ b/packages/theme/src/color-ramps/test/index.test.ts @@ -52,6 +52,59 @@ function expectAccessibleFillStates( ramp: ReturnType< typeof buildRamp > ) { ).toBeGreaterThanOrEqual( 4.5 ); } +const foregroundSteps = [ + 'fgSurface1', + 'fgSurface2', + 'fgSurface3', + 'fgSurface4', + 'fgSurface5', +] as const; + +const perceptualSampleCombinations = [ + { + background: DEFAULT_SEED_COLORS.background, + primary: DEFAULT_SEED_COLORS.primary, + }, + { background: '#1e1e1e', primary: DEFAULT_SEED_COLORS.primary }, + { background: '#4f386e', primary: '#608010' }, + { background: '#777777', primary: '#d63638' }, + { background: '#fcfcfc', primary: '#ffd700' }, + { background: '#1e1e1e', primary: '#00ffff' }, +] as const; + +function getPerceptualContrastMagnitude( + background: string, + foreground: string +) { + return Math.abs( contrastAPCA( background, foreground ) ); +} + +function getForegroundConstraintReferences( + stepIndex: number, + ramp: ReturnType< typeof buildBgRamp >, + backgroundRamp: ReturnType< typeof buildBgRamp > +) { + let surfaceNames: readonly ( keyof typeof ramp.ramp )[]; + if ( stepIndex < 2 ) { + surfaceNames = [ 'surface3' ]; + } else if ( stepIndex < 3 ) { + surfaceNames = [ 'surface1', 'surface2', 'surface3' ]; + } else { + surfaceNames = [ + 'surface1', + 'surface2', + 'surface3', + 'surface4', + 'surface5', + ]; + } + + return [ + ...surfaceNames.map( ( name ) => ramp.ramp[ name ] ), + ...surfaceNames.map( ( name ) => backgroundRamp.ramp[ name ] ), + ]; +} + describe( 'buildRamps', () => { it( 'restores the weak intent background colors', () => { const bgRamp = buildBgRamp( DEFAULT_SEED_COLORS.background ); diff --git a/packages/theme/src/prebuilt/ts/color-tokens.ts b/packages/theme/src/prebuilt/ts/color-tokens.ts index aa16b1472ae4e4..fa4ec37471853a 100644 --- a/packages/theme/src/prebuilt/ts/color-tokens.ts +++ b/packages/theme/src/prebuilt/ts/color-tokens.ts @@ -21,8 +21,8 @@ export default { 'foreground-interactive-brand-strong-active', ], 'primary-surface4': [ 'background-interactive-brand-weak-active' ], - 'primary-fgSurface4': [ 'foreground-interactive-brand-active' ], - 'primary-fgSurface3': [ 'foreground-interactive-brand' ], + 'primary-fgSurface5': [ 'foreground-interactive-brand-active' ], + 'primary-fgSurface4': [ 'foreground-interactive-brand' ], 'primary-stroke3': [ 'background-thumb-brand', 'background-thumb-brand-active', @@ -35,19 +35,19 @@ export default { 'primary-surface1': [ 'background-surface-brand' ], 'info-surface2': [ 'background-surface-info-weak' ], 'info-surface4': [ 'background-surface-info' ], - 'info-fgSurface4': [ 'foreground-content-info' ], + 'info-fgSurface5': [ 'foreground-content-info' ], 'info-fgSurface3': [ 'foreground-content-info-weak' ], 'info-stroke3': [ 'stroke-surface-info-strong' ], 'info-stroke1': [ 'stroke-surface-info' ], 'success-surface2': [ 'background-surface-success-weak' ], 'success-surface4': [ 'background-surface-success' ], - 'success-fgSurface4': [ 'foreground-content-success' ], + 'success-fgSurface5': [ 'foreground-content-success' ], 'success-fgSurface3': [ 'foreground-content-success-weak' ], 'success-stroke3': [ 'stroke-surface-success-strong' ], 'success-stroke1': [ 'stroke-surface-success' ], 'warning-surface2': [ 'background-surface-warning-weak' ], 'warning-surface4': [ 'background-surface-warning' ], - 'warning-fgSurface4': [ 'foreground-content-warning' ], + 'warning-fgSurface5': [ 'foreground-content-warning' ], 'warning-fgSurface3': [ 'foreground-content-warning-weak' ], 'warning-stroke3': [ 'stroke-surface-warning-strong' ], 'warning-stroke1': [ 'stroke-surface-warning' ], @@ -65,14 +65,12 @@ export default { 'background-interactive-error-weak-active', 'background-surface-error', ], - 'error-fgSurface4': [ + 'error-fgSurface5': [ 'foreground-content-error', 'foreground-interactive-error-active', ], - 'error-fgSurface3': [ - 'foreground-content-error-weak', - 'foreground-interactive-error', - ], + 'error-fgSurface4': [ 'foreground-interactive-error' ], + 'error-fgSurface3': [ 'foreground-content-error-weak' ], 'error-stroke3': [ 'stroke-interactive-error', 'stroke-interactive-error-strong', @@ -87,19 +85,13 @@ export default { 'background-interactive-neutral-strong-disabled', ], 'bg-surface4': [ 'background-interactive-neutral-weak-active' ], - 'bg-surface3': [ - 'background-interactive-neutral', - 'background-interactive-neutral-active', - 'background-interactive-neutral-disabled', - 'background-surface-neutral-strong', - ], - 'bg-fgSurface4': [ - 'background-thumb-neutral', + 'bg-surface3': [ 'background-surface-neutral-strong' ], + 'bg-fgSurface5': [ 'foreground-content-neutral', - 'foreground-interactive-neutral', 'foreground-interactive-neutral-active', 'foreground-interactive-neutral-weak-active', ], + 'bg-fgSurface4': [ 'background-thumb-neutral', 'foreground-interactive-neutral' ], 'bg-fgSurface3': [ 'foreground-content-neutral-weak', 'foreground-interactive-neutral-weak', @@ -145,7 +137,7 @@ export default { 'bg-surface1': [ 'background-surface-neutral-weak' ], 'caution-surface2': [ 'background-surface-caution-weak' ], 'caution-surface4': [ 'background-surface-caution' ], - 'caution-fgSurface4': [ 'foreground-content-caution' ], + 'caution-fgSurface5': [ 'foreground-content-caution' ], 'caution-fgSurface3': [ 'foreground-content-caution-weak' ], 'caution-stroke3': [ 'stroke-surface-caution-strong' ], 'caution-stroke1': [ 'stroke-surface-caution' ], diff --git a/packages/theme/src/test/theme-provider.browser.test.tsx b/packages/theme/src/test/theme-provider.browser.test.tsx index 9e0258931560fc..f90416513fac1e 100644 --- a/packages/theme/src/test/theme-provider.browser.test.tsx +++ b/packages/theme/src/test/theme-provider.browser.test.tsx @@ -15,6 +15,12 @@ import '../../prebuilt/css/design-tokens.css'; // round-trip. Ramp generation itself is covered by the color-ramps tests.) const BRAND_BG = '--wpds-color-background-interactive-brand-strong'; const SURFACE_BG = '--wpds-color-background-surface-neutral'; +const FOREGROUND_CONTENT_NEUTRAL = '--wpds-color-foreground-content-neutral'; +const FOREGROUND_NEUTRAL = '--wpds-color-foreground-interactive-neutral'; +const FOREGROUND_NEUTRAL_ACTIVE = + '--wpds-color-foreground-interactive-neutral-active'; +const FOREGROUND_NEUTRAL_WEAK_ACTIVE = + '--wpds-color-foreground-interactive-neutral-weak-active'; const CURSOR_CONTROL = '--wpds-cursor-control'; const BORDER_RADIUS_SM = '--wpds-border-radius-sm'; const PRIMARY = '#1e90ff'; @@ -90,6 +96,29 @@ describe( 'ThemeProvider', () => { expect( readProp( provider, SURFACE_BG ) ).toBe( BACKGROUND ); } ); + it( 'maps resting and active neutral foregrounds to separate scale steps', async () => { + await render( + +
x
+
+ ); + + const provider = getScopingProvider( screen.getByTestId( 'child' ) ); + const restingForeground = readProp( provider, FOREGROUND_NEUTRAL ); + const activeForeground = readProp( + provider, + FOREGROUND_NEUTRAL_ACTIVE + ); + + expect( activeForeground ).toBe( + readProp( provider, FOREGROUND_CONTENT_NEUTRAL ) + ); + expect( activeForeground ).toBe( + readProp( provider, FOREGROUND_NEUTRAL_WEAK_ACTIVE ) + ); + expect( restingForeground ).not.toBe( activeForeground ); + } ); + it( 'does not define color tokens if neither customized nor inherited', async () => { await render( diff --git a/packages/theme/tokens/color.json b/packages/theme/tokens/color.json index 82ef57102a5854..55a370a63f4824 100644 --- a/packages/theme/tokens/color.json +++ b/packages/theme/tokens/color.json @@ -66,18 +66,25 @@ "hex": "#fff" } }, - "fgSurface4": { + "fgSurface5": { "$value": { "colorSpace": "srgb", "components": [ 0.043, 0, 0.439 ], "hex": "#0b0070" } }, + "fgSurface4": { + "$value": { + "colorSpace": "srgb", + "components": [ 0.2, 0.318, 0.91 ], + "hex": "#3351e8" + } + }, "fgSurface3": { "$value": { "colorSpace": "srgb", - "components": [ 0.22, 0.345, 0.914 ], - "hex": "#3858e9" + "components": [ 0.243, 0.376, 0.918 ], + "hex": "#3e60ea" } }, "fgSurface2": { @@ -222,18 +229,25 @@ "hex": "#fff" } }, - "fgSurface4": { + "fgSurface5": { "$value": { "colorSpace": "srgb", "components": [ 0, 0.106, 0.31 ], "hex": "#001b4f" } }, + "fgSurface4": { + "$value": { + "colorSpace": "srgb", + "components": [ 0, 0.388, 0.698 ], + "hex": "#0063b2" + } + }, "fgSurface3": { "$value": { "colorSpace": "srgb", - "components": [ 0, 0.42, 0.843 ], - "hex": "#006bd7" + "components": [ 0, 0.431, 0.776 ], + "hex": "#006ec6" } }, "fgSurface2": { @@ -378,13 +392,20 @@ "hex": "#fff" } }, - "fgSurface4": { + "fgSurface5": { "$value": { "colorSpace": "srgb", "components": [ 0, 0.161, 0 ], "hex": "#002900" } }, + "fgSurface4": { + "$value": { + "colorSpace": "srgb", + "components": [ 0.169, 0.443, 0.239 ], + "hex": "#2b713d" + } + }, "fgSurface3": { "$value": { "colorSpace": "srgb", @@ -534,13 +555,20 @@ "hex": "#fff" } }, - "fgSurface4": { + "fgSurface5": { "$value": { "colorSpace": "srgb", "components": [ 0.18, 0.098, 0 ], "hex": "#2e1900" } }, + "fgSurface4": { + "$value": { + "colorSpace": "srgb", + "components": [ 0.482, 0.365, 0.129 ], + "hex": "#7b5d21" + } + }, "fgSurface3": { "$value": { "colorSpace": "srgb", @@ -690,18 +718,25 @@ "hex": "#fff" } }, - "fgSurface4": { + "fgSurface5": { "$value": { "colorSpace": "srgb", "components": [ 0.278, 0, 0 ], "hex": "#470000" } }, + "fgSurface4": { + "$value": { + "colorSpace": "srgb", + "components": [ 0.745, 0.086, 0.086 ], + "hex": "#be1616" + } + }, "fgSurface3": { "$value": { "colorSpace": "srgb", - "components": [ 0.8, 0.094, 0.094 ], - "hex": "#cc1818" + "components": [ 0.843, 0.102, 0.102 ], + "hex": "#d71a1a" } }, "fgSurface2": { @@ -846,13 +881,20 @@ "hex": "#fff" } }, - "fgSurface4": { + "fgSurface5": { "$value": { "colorSpace": "srgb", "components": [ 0.118, 0.118, 0.118 ], "hex": "#1e1e1e" } }, + "fgSurface4": { + "$value": { + "colorSpace": "srgb", + "components": [ 0.392, 0.392, 0.392 ], + "hex": "#646464" + } + }, "fgSurface3": { "$value": { "colorSpace": "srgb", @@ -1002,13 +1044,20 @@ "hex": "#fff" } }, - "fgSurface4": { + "fgSurface5": { "$value": { "colorSpace": "srgb", "components": [ 0.157, 0.114, 0 ], "hex": "#281d00" } }, + "fgSurface4": { + "$value": { + "colorSpace": "srgb", + "components": [ 0.443, 0.38, 0.114 ], + "hex": "#71611d" + } + }, "fgSurface3": { "$value": { "colorSpace": "srgb", @@ -1026,8 +1075,8 @@ "fgSurface1": { "$value": { "colorSpace": "srgb", - "components": [ 0.78, 0.659, 0 ], - "hex": "#c7a800" + "components": [ 0.776, 0.659, 0 ], + "hex": "#c6a800" } }, "stroke3": { @@ -1162,18 +1211,6 @@ } }, "interactive": { - "neutral": { - "$value": "{wpds-color.primitive.bg.surface3}", - "$description": "Solid background color for neutral interactive controls, such as input fields and unchecked checkboxes." - }, - "neutral-active": { - "$value": "{wpds-color.primitive.bg.surface3}", - "$description": "Solid background color for neutral interactive controls that are hovered, focused, or active." - }, - "neutral-disabled": { - "$value": "{wpds-color.primitive.bg.surface3}", - "$description": "Solid background color for neutral interactive controls in their disabled state." - }, "neutral-strong": { "$value": "{wpds-color.primitive.bg.bgFillInverted1}", "$description": "Background color for interactive elements with neutral tone and strong emphasis." @@ -1306,7 +1343,7 @@ "foreground": { "content": { "neutral": { - "$value": "{wpds-color.primitive.bg.fgSurface4}", + "$value": "{wpds-color.primitive.bg.fgSurface5}", "$description": "Foreground color for content like text with normal emphasis." }, "neutral-weak": { @@ -1314,7 +1351,7 @@ "$description": "Foreground color for content like text with weak emphasis." }, "success": { - "$value": "{wpds-color.primitive.success.fgSurface4}", + "$value": "{wpds-color.primitive.success.fgSurface5}", "$description": "Foreground color for content like text with success tone and normal emphasis." }, "success-weak": { @@ -1322,7 +1359,7 @@ "$description": "Foreground color for content like text with success tone and weak emphasis." }, "info": { - "$value": "{wpds-color.primitive.info.fgSurface4}", + "$value": "{wpds-color.primitive.info.fgSurface5}", "$description": "Foreground color for content like text with info tone and normal emphasis." }, "info-weak": { @@ -1330,7 +1367,7 @@ "$description": "Foreground color for content like text with info tone and weak emphasis." }, "warning": { - "$value": "{wpds-color.primitive.warning.fgSurface4}", + "$value": "{wpds-color.primitive.warning.fgSurface5}", "$description": "Foreground color for content like text with warning tone and normal emphasis." }, "warning-weak": { @@ -1338,7 +1375,7 @@ "$description": "Foreground color for content like text with warning tone and weak emphasis." }, "caution": { - "$value": "{wpds-color.primitive.caution.fgSurface4}", + "$value": "{wpds-color.primitive.caution.fgSurface5}", "$description": "Foreground color for content like text with caution tone and normal emphasis." }, "caution-weak": { @@ -1346,7 +1383,7 @@ "$description": "Foreground color for content like text with caution tone and weak emphasis." }, "error": { - "$value": "{wpds-color.primitive.error.fgSurface4}", + "$value": "{wpds-color.primitive.error.fgSurface5}", "$description": "Foreground color for content like text with error tone and normal emphasis." }, "error-weak": { @@ -1360,7 +1397,7 @@ "$description": "Foreground color for interactive elements with neutral tone and normal emphasis." }, "neutral-active": { - "$value": "{wpds-color.primitive.bg.fgSurface4}", + "$value": "{wpds-color.primitive.bg.fgSurface5}", "$description": "Foreground color for interactive elements with neutral tone and normal emphasis that are hovered, focused, or active." }, "neutral-disabled": { @@ -1384,7 +1421,7 @@ "$description": "Foreground color for interactive elements with neutral tone and weak emphasis." }, "neutral-weak-active": { - "$value": "{wpds-color.primitive.bg.fgSurface4}", + "$value": "{wpds-color.primitive.bg.fgSurface5}", "$description": "Foreground color for interactive elements with neutral tone and weak emphasis that are hovered, focused, or active." }, "neutral-weak-disabled": { @@ -1392,11 +1429,11 @@ "$description": "Foreground color for interactive elements with neutral tone and weak emphasis, in their disabled state." }, "brand": { - "$value": "{wpds-color.primitive.primary.fgSurface3}", + "$value": "{wpds-color.primitive.primary.fgSurface4}", "$description": "Foreground color for interactive elements with brand tone and normal emphasis." }, "brand-active": { - "$value": "{wpds-color.primitive.primary.fgSurface4}", + "$value": "{wpds-color.primitive.primary.fgSurface5}", "$description": "Foreground color for interactive elements with brand tone and normal emphasis that are hovered, focused, or active." }, "brand-disabled": { @@ -1416,11 +1453,11 @@ "$description": "Foreground color for interactive elements with brand tone and strong emphasis, in their disabled state." }, "error": { - "$value": "{wpds-color.primitive.error.fgSurface3}", + "$value": "{wpds-color.primitive.error.fgSurface4}", "$description": "Foreground color for interactive elements with error tone and normal emphasis." }, "error-active": { - "$value": "{wpds-color.primitive.error.fgSurface4}", + "$value": "{wpds-color.primitive.error.fgSurface5}", "$description": "Foreground color for interactive elements with error tone and normal emphasis that are hovered, focused, or active." }, "error-disabled": { From 4393d76a25791d396c753002ca96d999ce7a6142 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Tue, 1 Sep 2026 17:55:57 +0200 Subject: [PATCH 02/44] Theme: Add foreground scale changelog entry --- packages/theme/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/theme/CHANGELOG.md b/packages/theme/CHANGELOG.md index 2eda412e79a881..3cc949bdd4943a 100644 --- a/packages/theme/CHANGELOG.md +++ b/packages/theme/CHANGELOG.md @@ -58,6 +58,7 @@ ### Enhancements +- Generate foreground scales with five perception-aware steps, separating resting and active interactive colors while preserving WCAG contrast floors. ([#82294](https://github.com/WordPress/gutenberg/pull/82294)) - Add a Lightning CSS visitor plugin that injects design system token fallbacks, available as `@wordpress/theme/lightningcss-plugins/lightningcss-ds-token-fallbacks`, with an optional peer range of `>=1.33.0 <2.0.0`. ([#80401](https://github.com/WordPress/gutenberg/pull/80401)) ### Bug Fixes From ffb5a8b27f99d9412c0ede9ea8af720519841462 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Tue, 1 Sep 2026 18:10:57 +0200 Subject: [PATCH 03/44] Theme: Add color ramp benchmark --- packages/theme/benchmark/README.md | 28 ++ packages/theme/benchmark/color-ramps.ts | 348 ++++++++++++++++++++++++ 2 files changed, 376 insertions(+) create mode 100644 packages/theme/benchmark/README.md create mode 100644 packages/theme/benchmark/color-ramps.ts diff --git a/packages/theme/benchmark/README.md b/packages/theme/benchmark/README.md new file mode 100644 index 00000000000000..c3c95345f0551a --- /dev/null +++ b/packages/theme/benchmark/README.md @@ -0,0 +1,28 @@ +# Color ramp benchmark + +This benchmark measures uncached color-ramp calculation. It covers one +background ramp, one accent ramp against a precomputed background, and the full +`ThemeProvider` ramp workload of one background plus six accent ramps. Each case +cycles through the default light seed, a dark `#1e1e1e` background, and the +Ectoplasm admin color scheme. + +Run it from the repository root: + +```sh +npm run benchmark:color-ramps --workspace @wordpress/theme +``` + +To compare two checkouts on the same machine, run the benchmark script from the +candidate checkout and point it at a prepared baseline worktree: + +```sh +npm run benchmark:color-ramps --workspace @wordpress/theme -- \ + --baseline-root /path/to/gutenberg-trunk \ + --baseline-label trunk \ + --label candidate +``` + +Both worktrees need their dependencies installed. Add `--json` to save +machine-readable results. Compare runs made with the same Node.js version and +similar system load. The benchmark reports measurements but does not enforce a +timing threshold because local and CI timings vary. diff --git a/packages/theme/benchmark/color-ramps.ts b/packages/theme/benchmark/color-ramps.ts new file mode 100644 index 00000000000000..2ad6d092d39c11 --- /dev/null +++ b/packages/theme/benchmark/color-ramps.ts @@ -0,0 +1,348 @@ +/* eslint-disable no-console -- This command writes benchmark output. */ +import { spawnSync } from 'node:child_process'; +import { dirname, resolve } from 'node:path'; +import { performance } from 'node:perf_hooks'; +import { parseArgs } from 'node:util'; +import { fileURLToPath, pathToFileURL } from 'node:url'; + +type RampModule = typeof import('../src/color-ramps/index.ts'); + +type BenchmarkResult = { + name: string; + iterationsPerSample: number; + medianMs: number; + p95Ms: number; + checksum: number; +}; + +type BenchmarkReport = { + label: string; + environment: { + node: string; + platform: NodeJS.Platform; + arch: string; + }; + warmupSamples: number; + measuredSamples: number; + fixtures: typeof FIXTURES; + results: BenchmarkResult[]; +}; + +type BenchmarkCase = { + name: string; + iterationsPerSample: number; + run: () => string; +}; + +const WARMUP_SAMPLES = 8; +const MEASURED_SAMPLES = 30; +const SCRIPT_PATH = fileURLToPath( import.meta.url ); +const DEFAULT_SOURCE_ROOT = resolve( dirname( SCRIPT_PATH ), '../../..' ); +const FIXTURES = [ + { + name: 'default-light', + background: '#fcfcfc', + primary: '#3858e9', + }, + { + name: 'default-dark', + background: '#1e1e1e', + primary: '#3858e9', + }, + { + name: 'ectoplasm', + background: '#4f386e', + primary: '#646c3e', + }, +] as const; + +const HELP = `Theme color ramp benchmark + +Usage: + npm run benchmark:color-ramps --workspace @wordpress/theme -- [options] + +Options: + --source-root Checkout to benchmark (default: current checkout) + --label Label for the candidate result + --baseline-root Checkout to benchmark as the baseline + --baseline-label Label for the baseline result + --json Print machine-readable output + --help, -h Show this help + +When --baseline-root is set, each checkout runs in a separate Node.js process +before the script reports the percentage difference.`; + +async function loadRampModule( sourceRoot: string ): Promise< RampModule > { + const modulePath = resolve( + sourceRoot, + 'packages/theme/src/color-ramps/index.ts' + ); + return import( pathToFileURL( modulePath ).href ) as Promise< RampModule >; +} + +function createCases( rampModule: RampModule ): BenchmarkCase[] { + const { buildAccentRamp, buildBgRamp, DEFAULT_SEED_COLORS } = rampModule; + const accentBackgroundRamps = FIXTURES.map( ( fixture ) => + buildBgRamp( fixture.background ) + ); + let backgroundIndex = 0; + let accentIndex = 0; + let themeIndex = 0; + + return [ + { + name: 'background-ramp', + iterationsPerSample: 20, + run: () => { + const fixture = FIXTURES[ backgroundIndex++ % FIXTURES.length ]; + return buildBgRamp( fixture.background ).ramp.fgFill; + }, + }, + { + name: 'accent-ramp', + iterationsPerSample: 20, + run: () => { + const fixtureIndex = accentIndex++ % FIXTURES.length; + return buildAccentRamp( + FIXTURES[ fixtureIndex ].primary, + accentBackgroundRamps[ fixtureIndex ] + ).ramp.fgFill; + }, + }, + { + name: 'theme-color-ramps', + iterationsPerSample: 5, + run: () => { + const fixture = FIXTURES[ themeIndex++ % FIXTURES.length ]; + const backgroundRamp = buildBgRamp( fixture.background ); + const accentSeeds = [ + fixture.primary, + DEFAULT_SEED_COLORS.info, + DEFAULT_SEED_COLORS.success, + DEFAULT_SEED_COLORS.caution, + DEFAULT_SEED_COLORS.warning, + DEFAULT_SEED_COLORS.error, + ]; + let result = ''; + + for ( const seed of accentSeeds ) { + result = buildAccentRamp( seed, backgroundRamp ).ramp + .fgFill; + } + return result; + }, + }, + ]; +} + +function measure( benchmarkCase: BenchmarkCase ): BenchmarkResult { + let checksum = 0; + const runSample = () => { + let result = ''; + const start = performance.now(); + for ( + let iteration = 0; + iteration < benchmarkCase.iterationsPerSample; + iteration++ + ) { + result = benchmarkCase.run(); + } + const duration = performance.now() - start; + + for ( const character of result ) { + checksum = + ( checksum * 31 + ( character.codePointAt( 0 ) ?? 0 ) ) % + 2_147_483_647; + } + return duration / benchmarkCase.iterationsPerSample; + }; + + for ( let sample = 0; sample < WARMUP_SAMPLES; sample++ ) { + runSample(); + } + + const samples = Array.from( { length: MEASURED_SAMPLES }, runSample ).sort( + ( a, b ) => a - b + ); + const middle = Math.floor( samples.length / 2 ); + const medianMs = + samples.length % 2 === 0 + ? ( samples[ middle - 1 ] + samples[ middle ] ) / 2 + : samples[ middle ]; + + return { + name: benchmarkCase.name, + iterationsPerSample: benchmarkCase.iterationsPerSample, + medianMs, + p95Ms: samples[ Math.ceil( samples.length * 0.95 ) - 1 ], + checksum, + }; +} + +async function runBenchmark( + sourceRoot: string, + label: string +): Promise< BenchmarkReport > { + const rampModule = await loadRampModule( sourceRoot ); + return { + label, + environment: { + node: process.version, + platform: process.platform, + arch: process.arch, + }, + warmupSamples: WARMUP_SAMPLES, + measuredSamples: MEASURED_SAMPLES, + fixtures: FIXTURES, + results: createCases( rampModule ).map( measure ), + }; +} + +function runIsolated( sourceRoot: string, label: string ) { + const result = spawnSync( + process.execPath, + [ + ...process.execArgv, + SCRIPT_PATH, + '--source-root', + sourceRoot, + '--label', + label, + '--json', + ], + { encoding: 'utf8' } + ); + + if ( result.error ) { + throw result.error; + } + if ( result.status !== 0 ) { + throw new Error( + `Benchmark failed for ${ label }:\n${ result.stderr }` + ); + } + return JSON.parse( result.stdout ) as BenchmarkReport; +} + +function compare( baseline: BenchmarkReport, candidate: BenchmarkReport ) { + return candidate.results.map( ( candidateResult ) => { + const baselineResult = baseline.results.find( + ( result ) => result.name === candidateResult.name + ); + if ( ! baselineResult ) { + throw new Error( + `Baseline result is missing ${ candidateResult.name }.` + ); + } + return { + name: candidateResult.name, + baselineMedianMs: baselineResult.medianMs, + candidateMedianMs: candidateResult.medianMs, + medianDeltaPercent: + ( candidateResult.medianMs / baselineResult.medianMs - 1 ) * + 100, + baselineP95Ms: baselineResult.p95Ms, + candidateP95Ms: candidateResult.p95Ms, + }; + } ); +} + +function printContext( report: BenchmarkReport ) { + console.log( + `${ report.environment.node } ${ report.environment.platform }/${ report.environment.arch }; ${ report.measuredSamples } samples after ${ report.warmupSamples } warmups` + ); + console.log( + `fixtures: ${ report.fixtures + .map( ( { name } ) => name ) + .join( ', ' ) }` + ); +} + +function printReport( report: BenchmarkReport ) { + console.log( `Theme color ramp benchmark: ${ report.label }` ); + printContext( report ); + console.table( + report.results.map( ( result ) => ( { + case: result.name, + 'median ms/op': result.medianMs.toFixed( 3 ), + 'p95 ms/op': result.p95Ms.toFixed( 3 ), + } ) ) + ); +} + +function printComparison( + baseline: BenchmarkReport, + candidate: BenchmarkReport, + comparison: ReturnType< typeof compare > +) { + console.log( + `Theme color ramp benchmark: ${ baseline.label } vs ${ candidate.label }` + ); + printContext( candidate ); + console.table( + comparison.map( ( result ) => ( { + case: result.name, + [ `${ baseline.label } median` ]: + result.baselineMedianMs.toFixed( 3 ), + [ `${ candidate.label } median` ]: + result.candidateMedianMs.toFixed( 3 ), + delta: `${ + result.medianDeltaPercent >= 0 ? '+' : '' + }${ result.medianDeltaPercent.toFixed( 1 ) }%`, + [ `${ baseline.label } p95` ]: result.baselineP95Ms.toFixed( 3 ), + [ `${ candidate.label } p95` ]: result.candidateP95Ms.toFixed( 3 ), + } ) ) + ); +} + +async function main() { + const { values } = parseArgs( { + options: { + 'source-root': { type: 'string' }, + label: { type: 'string' }, + 'baseline-root': { type: 'string' }, + 'baseline-label': { type: 'string' }, + json: { type: 'boolean' }, + help: { type: 'boolean', short: 'h' }, + }, + } ); + + if ( values.help ) { + console.log( HELP ); + return; + } + + const sourceRoot = resolve( + values[ 'source-root' ] ?? DEFAULT_SOURCE_ROOT + ); + const label = values.label ?? 'candidate'; + if ( values[ 'baseline-root' ] ) { + const baseline = runIsolated( + resolve( values[ 'baseline-root' ] ), + values[ 'baseline-label' ] ?? 'baseline' + ); + const candidate = runIsolated( sourceRoot, label ); + const comparison = compare( baseline, candidate ); + if ( values.json ) { + console.log( + JSON.stringify( { baseline, candidate, comparison }, null, 2 ) + ); + } else { + printComparison( baseline, candidate, comparison ); + } + return; + } + + const report = await runBenchmark( sourceRoot, label ); + if ( values.json ) { + console.log( JSON.stringify( report, null, 2 ) ); + } else { + printReport( report ); + } +} + +main().catch( ( error ) => { + console.error( error instanceof Error ? error.message : error ); + process.exitCode = 1; +} ); +/* eslint-enable no-console */ From 7afeee55da0bafbc8f466fd01235240e2d4f7fcb Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Tue, 1 Sep 2026 19:57:00 +0200 Subject: [PATCH 04/44] Theme: Keep active fills darker and accessible --- .../internal/_wpds-token-fallbacks.scss | 4 +- packages/theme/CHANGELOG.md | 2 +- packages/theme/prebuilt/css/design-tokens.css | 8 +- .../prebuilt/js/design-token-fallbacks.mjs | 4 +- .../src/color-ramps/lib/default-ramps.ts | 8 +- .../theme/src/color-ramps/lib/ramp-configs.ts | 3 +- .../test/__snapshots__/index.test.ts.snap | 242 +++++++++--------- .../theme/src/color-ramps/test/index.test.ts | 15 ++ packages/theme/tokens/color.json | 16 +- 9 files changed, 158 insertions(+), 144 deletions(-) diff --git a/packages/base-styles/internal/_wpds-token-fallbacks.scss b/packages/base-styles/internal/_wpds-token-fallbacks.scss index ddbf0d01a017d2..290fccbccee08c 100644 --- a/packages/base-styles/internal/_wpds-token-fallbacks.scss +++ b/packages/base-styles/internal/_wpds-token-fallbacks.scss @@ -88,8 +88,8 @@ $fallbacks: ( "--wpds-color-foreground-interactive-error": "#be1616", "--wpds-color-foreground-interactive-error-active": "#470000", "--wpds-color-foreground-interactive-error-disabled": "#8d8d8d", - "--wpds-color-foreground-interactive-error-strong": "#f2efef", - "--wpds-color-foreground-interactive-error-strong-active": "#f2efef", + "--wpds-color-foreground-interactive-error-strong": "#eae6e6", + "--wpds-color-foreground-interactive-error-strong-active": "#eae6e6", "--wpds-color-foreground-interactive-error-strong-disabled": "#8d8d8d", "--wpds-color-foreground-interactive-neutral": "#646464", "--wpds-color-foreground-interactive-neutral-active": "#1e1e1e", diff --git a/packages/theme/CHANGELOG.md b/packages/theme/CHANGELOG.md index 3cc949bdd4943a..2f134992fafc0b 100644 --- a/packages/theme/CHANGELOG.md +++ b/packages/theme/CHANGELOG.md @@ -58,7 +58,7 @@ ### Enhancements -- Generate foreground scales with five perception-aware steps, separating resting and active interactive colors while preserving WCAG contrast floors. ([#82294](https://github.com/WordPress/gutenberg/pull/82294)) +- Generate foreground scales with five perception-aware steps, separate resting and active interactive colors, and keep active fills darker while preserving WCAG contrast floors. ([#82294](https://github.com/WordPress/gutenberg/pull/82294)) - Add a Lightning CSS visitor plugin that injects design system token fallbacks, available as `@wordpress/theme/lightningcss-plugins/lightningcss-ds-token-fallbacks`, with an optional peer range of `>=1.33.0 <2.0.0`. ([#80401](https://github.com/WordPress/gutenberg/pull/80401)) ### Bug Fixes diff --git a/packages/theme/prebuilt/css/design-tokens.css b/packages/theme/prebuilt/css/design-tokens.css index a28e98271d1e32..0403c137627ef4 100644 --- a/packages/theme/prebuilt/css/design-tokens.css +++ b/packages/theme/prebuilt/css/design-tokens.css @@ -148,9 +148,9 @@ /* Foreground color for interactive elements with brand tone and normal emphasis, in their disabled state. */ --wpds-color-foreground-interactive-brand-disabled: #8d8d8d; /* Foreground color for interactive elements with brand tone and strong emphasis. */ - --wpds-color-foreground-interactive-brand-strong: #eff0f2; + --wpds-color-foreground-interactive-brand-strong: #e7e8ea; /* Foreground color for interactive elements with brand tone and strong emphasis that are hovered, focused, or active. */ - --wpds-color-foreground-interactive-brand-strong-active: #eff0f2; + --wpds-color-foreground-interactive-brand-strong-active: #e7e8ea; /* Foreground color for interactive elements with brand tone and strong emphasis, in their disabled state. */ --wpds-color-foreground-interactive-brand-strong-disabled: #8d8d8d; /* Foreground color for interactive elements with error tone and normal emphasis. */ @@ -160,9 +160,9 @@ /* Foreground color for interactive elements with error tone and normal emphasis, in their disabled state. */ --wpds-color-foreground-interactive-error-disabled: #8d8d8d; /* Foreground color for interactive elements with error tone and strong emphasis. */ - --wpds-color-foreground-interactive-error-strong: #f2efef; + --wpds-color-foreground-interactive-error-strong: #eae6e6; /* Foreground color for interactive elements with error tone and strong emphasis that are hovered, focused, or active. */ - --wpds-color-foreground-interactive-error-strong-active: #f2efef; + --wpds-color-foreground-interactive-error-strong-active: #eae6e6; /* Foreground color for interactive elements with error tone and strong emphasis, in their disabled state. */ --wpds-color-foreground-interactive-error-strong-disabled: #8d8d8d; /* Foreground color for interactive elements with neutral tone and normal emphasis. */ diff --git a/packages/theme/prebuilt/js/design-token-fallbacks.mjs b/packages/theme/prebuilt/js/design-token-fallbacks.mjs index d53e3a92b25a9b..23535e8e909508 100644 --- a/packages/theme/prebuilt/js/design-token-fallbacks.mjs +++ b/packages/theme/prebuilt/js/design-token-fallbacks.mjs @@ -86,8 +86,8 @@ export default { '--wpds-color-foreground-interactive-error': '#be1616', '--wpds-color-foreground-interactive-error-active': '#470000', '--wpds-color-foreground-interactive-error-disabled': '#8d8d8d', - '--wpds-color-foreground-interactive-error-strong': '#f2efef', - '--wpds-color-foreground-interactive-error-strong-active': '#f2efef', + '--wpds-color-foreground-interactive-error-strong': '#eae6e6', + '--wpds-color-foreground-interactive-error-strong-active': '#eae6e6', '--wpds-color-foreground-interactive-error-strong-disabled': '#8d8d8d', '--wpds-color-foreground-interactive-neutral': '#646464', '--wpds-color-foreground-interactive-neutral-active': '#1e1e1e', diff --git a/packages/theme/src/color-ramps/lib/default-ramps.ts b/packages/theme/src/color-ramps/lib/default-ramps.ts index c0f2c48e04e64d..044df57607a700 100644 --- a/packages/theme/src/color-ramps/lib/default-ramps.ts +++ b/packages/theme/src/color-ramps/lib/default-ramps.ts @@ -57,7 +57,7 @@ export const DEFAULT_RAMPS: Record< bgFillInverted2: '#1a1e27', bgFillInverted1: '#13009f', fgFillInverted: '#eff0f2', - surface1: '#ecf0fa', + surface1: '#ebf0fc', }, direction: 'darker', }, @@ -190,11 +190,11 @@ export const DEFAULT_RAMPS: Record< stroke2: '#d99a91', stroke1: '#e0afa7', bgFillDark: '#231c1b', - fgFillDark: '#f2efef', + fgFillDark: '#f3efef', bgFillInverted2: '#231c1b', bgFillInverted1: '#640000', - fgFillInverted: '#f2efef', - surface1: '#fcece9', + fgFillInverted: '#f3efef', + surface1: '#feebe8', }, direction: 'darker', }, diff --git a/packages/theme/src/color-ramps/lib/ramp-configs.ts b/packages/theme/src/color-ramps/lib/ramp-configs.ts index 7eb1ed72340a93..29be4c952b5170 100644 --- a/packages/theme/src/color-ramps/lib/ramp-configs.ts +++ b/packages/theme/src/color-ramps/lib/ramp-configs.ts @@ -169,7 +169,7 @@ const BG_RAMP_STEPS: RampConfig[ 'steps' ] = { bgFill2: { contrast: { reference: 'bgFill1', - followDirection: 'main', + followDirection: 'darker', target: 1.2, }, }, @@ -273,7 +273,6 @@ const BG_RAMP_STEPS: RampConfig[ 'steps' ] = { // Preserve the 4.5:1 WCAG floor after 8-bit sRGB serialization. target: 4.55, }, - lightness: lightnessConstraintForegroundHighContrast, taperChromaOptions: FG_TAPER_CHROMA, }, fgFillInverted: { diff --git a/packages/theme/src/color-ramps/test/__snapshots__/index.test.ts.snap b/packages/theme/src/color-ramps/test/__snapshots__/index.test.ts.snap index c1672aff99d9ea..b80a69704763d0 100644 --- a/packages/theme/src/color-ramps/test/__snapshots__/index.test.ts.snap +++ b/packages/theme/src/color-ramps/test/__snapshots__/index.test.ts.snap @@ -1545,11 +1545,11 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "direction": "lighter", "ramp": { "bgFill1": "#d1d1d1", - "bgFill2": "#e6e6e6", + "bgFill2": "#bebebe", "bgFillDark": "#000", "bgFillInverted1": "#dbdbdb", "bgFillInverted2": "#f0f0f0", - "fgFill": "#1e1e1e", + "fgFill": "#4d4d4d", "fgFillDark": "#f0f0f0", "fgFillInverted": "#1e1e1e", "fgSurface1": "#454545", @@ -1588,11 +1588,11 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "direction": "lighter", "ramp": { "bgFill1": "#d1d1d1", - "bgFill2": "#e6e6e6", + "bgFill2": "#bebebe", "bgFillDark": "#000", "bgFillInverted1": "#dbdbdb", "bgFillInverted2": "#f0f0f0", - "fgFill": "#1e1e1e", + "fgFill": "#4d4d4d", "fgFillDark": "#f0f0f0", "fgFillInverted": "#1e1e1e", "fgSurface1": "#464646", @@ -1631,11 +1631,11 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "direction": "lighter", "ramp": { "bgFill1": "#d1d1d1", - "bgFill2": "#e6e6e6", + "bgFill2": "#bebebe", "bgFillDark": "#000", "bgFillInverted1": "#dbdbdb", "bgFillInverted2": "#f0f0f0", - "fgFill": "#1e1e1e", + "fgFill": "#4d4d4d", "fgFillDark": "#f0f0f0", "fgFillInverted": "#1e1e1e", "fgSurface1": "#4f4f4f", @@ -1674,11 +1674,11 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "direction": "lighter", "ramp": { "bgFill1": "#d1d1d1", - "bgFill2": "#e6e6e6", + "bgFill2": "#bebebe", "bgFillDark": "#000", "bgFillInverted1": "#dbdbdb", "bgFillInverted2": "#f0f0f0", - "fgFill": "#1e1e1e", + "fgFill": "#4d4d4d", "fgFillDark": "#f0f0f0", "fgFillInverted": "#1e1e1e", "fgSurface1": "#606060", @@ -1717,11 +1717,11 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "direction": "lighter", "ramp": { "bgFill1": "#d2d2d2", - "bgFill2": "#e6e6e6", + "bgFill2": "#bebebe", "bgFillDark": "#000", "bgFillInverted1": "#dbdbdb", "bgFillInverted2": "#f0f0f0", - "fgFill": "#1e1e1e", + "fgFill": "#4d4d4d", "fgFillDark": "#f0f0f0", "fgFillInverted": "#1e1e1e", "fgSurface1": "#7b7b7b", @@ -1764,7 +1764,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFillDark": "#050505", "bgFillInverted1": "#1e1e1e", "bgFillInverted2": "#050505", - "fgFill": "#1e1e1e", + "fgFill": "#666", "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface1": "#656565", @@ -1807,7 +1807,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#1e1e1e", + "fgFill": "#666", "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface1": "#7e7e7e", @@ -1850,7 +1850,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#1e1e1e", + "fgFill": "#666", "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface1": "#969696", @@ -1893,7 +1893,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#1e1e1e", + "fgFill": "#666", "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", @@ -1934,13 +1934,13 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "direction": "lighter", "ramp": { "bgFill1": "#3858e9", - "bgFill2": "#4367f9", + "bgFill2": "#2e49d9", "bgFillDark": "#000", "bgFillInverted1": "#c8dcff", "bgFillInverted2": "#eff0f2", "fgFill": "#fefeff", "fgFillDark": "#eff0f2", - "fgFillInverted": "#1a1e27", + "fgFillInverted": "#1a1e28", "fgSurface1": "#1e2dbf", "fgSurface2": "#314ede", "fgSurface3": "#8eb0ff", @@ -1976,13 +1976,13 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "direction": "lighter", "ramp": { "bgFill1": "#3858e9", - "bgFill2": "#4367f9", + "bgFill2": "#2e49d9", "bgFillDark": "#000", "bgFillInverted1": "#c8dcff", "bgFillInverted2": "#eff0f2", "fgFill": "#fefeff", "fgFillDark": "#eff0f2", - "fgFillInverted": "#1a1e27", + "fgFillInverted": "#1a1e28", "fgSurface1": "#1f2ec0", "fgSurface2": "#324fe0", "fgSurface3": "#8eb0ff", @@ -2018,13 +2018,13 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "direction": "lighter", "ramp": { "bgFill1": "#3858e9", - "bgFill2": "#4367f9", + "bgFill2": "#2e49d9", "bgFillDark": "#000", "bgFillInverted1": "#c8dcff", "bgFillInverted2": "#eff0f2", "fgFill": "#fefeff", "fgFillDark": "#eff0f2", - "fgFillInverted": "#1a1e27", + "fgFillInverted": "#1a1e28", "fgSurface1": "#2439ca", "fgSurface2": "#3858e9", "fgSurface3": "#8eb0ff", @@ -2060,7 +2060,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "direction": "lighter", "ramp": { "bgFill1": "#3858e9", - "bgFill2": "#4367f9", + "bgFill2": "#2e49d9", "bgFillDark": "#000", "bgFillInverted1": "#c8dcff", "bgFillInverted2": "#eff0f2", @@ -2102,13 +2102,13 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "direction": "lighter", "ramp": { "bgFill1": "#3858e9", - "bgFill2": "#4367f9", + "bgFill2": "#2e49d9", "bgFillDark": "#000", "bgFillInverted1": "#c8dcff", "bgFillInverted2": "#eff0f2", "fgFill": "#fefeff", "fgFillDark": "#eff0f2", - "fgFillInverted": "#1a1e27", + "fgFillInverted": "#1a1e28", "fgSurface1": "#466bfd", "fgSurface2": "#6a91ff", "fgSurface3": "#99b8ff", @@ -2187,10 +2187,10 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "ramp": { "bgFill1": "#3858e9", "bgFill2": "#2e49d9", - "bgFillDark": "#1a1e27", + "bgFillDark": "#1a1e28", "bgFillInverted1": "#13009f", - "bgFillInverted2": "#1a1e27", - "fgFill": "#eff0f2", + "bgFillInverted2": "#1a1e28", + "fgFill": "#e7e8ea", "fgFillDark": "#eff0f2", "fgFillInverted": "#eff0f2", "fgSurface1": "#4b71ff", @@ -2229,10 +2229,10 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "ramp": { "bgFill1": "#3858e9", "bgFill2": "#2e49d9", - "bgFillDark": "#1a1e27", + "bgFillDark": "#1a1e28", "bgFillInverted1": "#13009f", - "bgFillInverted2": "#1a1e27", - "fgFill": "#eff0f2", + "bgFillInverted2": "#1a1e28", + "fgFill": "#e7e8ea", "fgFillDark": "#eff0f2", "fgFillInverted": "#eff0f2", "fgSurface1": "#6a91ff", @@ -2271,10 +2271,10 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "ramp": { "bgFill1": "#3858e9", "bgFill2": "#2e49d9", - "bgFillDark": "#1a1e27", + "bgFillDark": "#1a1e28", "bgFillInverted1": "#13009f", - "bgFillInverted2": "#1a1e27", - "fgFill": "#eff0f2", + "bgFillInverted2": "#1a1e28", + "fgFill": "#e7e8ea", "fgFillDark": "#eff0f2", "fgFillInverted": "#eff0f2", "fgSurface1": "#8daeff", @@ -2314,11 +2314,11 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "direction": "lighter", "ramp": { "bgFill1": "#0090ff", - "bgFill2": "#37a3ff", + "bgFill2": "#007fed", "bgFillDark": "#000", "bgFillInverted1": "#b8e0ff", "bgFillInverted2": "#eef0f2", - "fgFill": "#1a1f24", + "fgFill": "#12161b", "fgFillDark": "#eef0f2", "fgFillInverted": "#1a1f24", "fgSurface1": "#004192", @@ -2356,11 +2356,11 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "direction": "lighter", "ramp": { "bgFill1": "#0090ff", - "bgFill2": "#37a3ff", + "bgFill2": "#007fed", "bgFillDark": "#000", "bgFillInverted1": "#b8e0ff", "bgFillInverted2": "#eef0f2", - "fgFill": "#1a1f24", + "fgFill": "#12161b", "fgFillDark": "#eef0f2", "fgFillInverted": "#1a1f24", "fgSurface1": "#004394", @@ -2398,11 +2398,11 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "direction": "lighter", "ramp": { "bgFill1": "#0090ff", - "bgFill2": "#37a3ff", + "bgFill2": "#007fed", "bgFillDark": "#000", "bgFillInverted1": "#b8e0ff", "bgFillInverted2": "#eef0f2", - "fgFill": "#1a1f24", + "fgFill": "#12161b", "fgFillDark": "#eef0f2", "fgFillInverted": "#1a1f24", "fgSurface1": "#004ba3", @@ -2440,11 +2440,11 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "direction": "lighter", "ramp": { "bgFill1": "#0090ff", - "bgFill2": "#37a3ff", + "bgFill2": "#007fed", "bgFillDark": "#000", "bgFillInverted1": "#b8e0ff", "bgFillInverted2": "#eef0f2", - "fgFill": "#1a1f24", + "fgFill": "#12161b", "fgFillDark": "#eef0f2", "fgFillInverted": "#1a1f24", "fgSurface1": "#005dc2", @@ -2482,11 +2482,11 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "direction": "lighter", "ramp": { "bgFill1": "#0090ff", - "bgFill2": "#37a3ff", + "bgFill2": "#007fed", "bgFillDark": "#000", "bgFillInverted1": "#b8e0ff", "bgFillInverted2": "#eef0f2", - "fgFill": "#1a1f24", + "fgFill": "#12161b", "fgFillDark": "#eef0f2", "fgFillInverted": "#1a1f24", "fgSurface1": "#007be8", @@ -2694,7 +2694,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "direction": "lighter", "ramp": { "bgFill1": "#4ab866", - "bgFill2": "#5dca77", + "bgFill2": "#37a756", "bgFillDark": "#000", "bgFillInverted1": "#86f29c", "bgFillInverted2": "#ecf2ec", @@ -2736,7 +2736,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "direction": "lighter", "ramp": { "bgFill1": "#4ab866", - "bgFill2": "#5dca77", + "bgFill2": "#37a756", "bgFillDark": "#000", "bgFillInverted1": "#86f29c", "bgFillInverted2": "#ecf2ec", @@ -2778,7 +2778,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "direction": "lighter", "ramp": { "bgFill1": "#4ab866", - "bgFill2": "#5dca77", + "bgFill2": "#37a756", "bgFillDark": "#000", "bgFillInverted1": "#86f29c", "bgFillInverted2": "#ecf2ec", @@ -2793,7 +2793,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "stroke2": "#466a4c", "stroke3": "#4ab866", "stroke4": "#73df8b", - "surface1": "#041006", + "surface1": "#051007", "surface2": "#081b0c", "surface3": "#0d2312", "surface4": "#0f2815", @@ -2820,7 +2820,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "direction": "lighter", "ramp": { "bgFill1": "#4ab866", - "bgFill2": "#5dca77", + "bgFill2": "#37a756", "bgFillDark": "#000", "bgFillInverted1": "#86f29c", "bgFillInverted2": "#ecf2ec", @@ -2862,7 +2862,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "direction": "lighter", "ramp": { "bgFill1": "#4ab866", - "bgFill2": "#5dca77", + "bgFill2": "#37a756", "bgFillDark": "#000", "bgFillInverted1": "#86f29c", "bgFillInverted2": "#ecf2ec", @@ -3074,11 +3074,11 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "direction": "lighter", "ramp": { "bgFill1": "#f0d148", - "bgFill2": "#ffe762", + "bgFill2": "#dcbd2e", "bgFillDark": "#000", "bgFillInverted1": "#f9da53", "bgFillInverted2": "#faf1ca", - "fgFill": "#1f1e1b", + "fgFill": "#4f4d47", "fgFillDark": "#faf1ca", "fgFillInverted": "#1f1e1b", "fgSurface1": "#544300", @@ -3116,11 +3116,11 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "direction": "lighter", "ramp": { "bgFill1": "#f0d148", - "bgFill2": "#ffe762", + "bgFill2": "#dcbd2e", "bgFillDark": "#000", "bgFillInverted1": "#f9da53", "bgFillInverted2": "#faf1ca", - "fgFill": "#1f1e1b", + "fgFill": "#4f4d47", "fgFillDark": "#faf1ca", "fgFillInverted": "#1f1e1b", "fgSurface1": "#564400", @@ -3158,11 +3158,11 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "direction": "lighter", "ramp": { "bgFill1": "#f0d148", - "bgFill2": "#ffe762", + "bgFill2": "#dcbd2e", "bgFillDark": "#000", "bgFillInverted1": "#f9da53", "bgFillInverted2": "#faf1ca", - "fgFill": "#1f1e1b", + "fgFill": "#4f4d47", "fgFillDark": "#faf1ca", "fgFillInverted": "#1f1e1b", "fgSurface1": "#604c00", @@ -3200,14 +3200,14 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "direction": "lighter", "ramp": { "bgFill1": "#f0d148", - "bgFill2": "#ffe762", + "bgFill2": "#dcbd2e", "bgFillDark": "#000", "bgFillInverted1": "#f9da53", "bgFillInverted2": "#faf1ca", - "fgFill": "#1f1e1b", + "fgFill": "#4f4d47", "fgFillDark": "#faf1ca", "fgFillInverted": "#1f1e1b", - "fgSurface1": "#745e01", + "fgSurface1": "#745e00", "fgSurface2": "#957a00", "fgSurface3": "#f0d148", "fgSurface4": "#fff075", @@ -3220,7 +3220,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "surface3": "#39331b", "surface4": "#3d371d", "surface5": "#423b20", - "surface6": "#4c4526", + "surface6": "#4d4523", }, "warnings": undefined, }, @@ -3242,11 +3242,11 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "direction": "lighter", "ramp": { "bgFill1": "#f0d148", - "bgFill2": "#ffe762", + "bgFill2": "#dcbd2e", "bgFillDark": "#000", "bgFillInverted1": "#f9da53", "bgFillInverted2": "#faf1ca", - "fgFill": "#1f1e1b", + "fgFill": "#4f4d47", "fgFillDark": "#faf1ca", "fgFillInverted": "#1f1e1b", "fgSurface1": "#947901", @@ -3288,7 +3288,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFillDark": "#060605", "bgFillInverted1": "#281d00", "bgFillInverted2": "#060605", - "fgFill": "#1f1e1b", + "fgFill": "#4f4e48", "fgFillDark": "#faf1c9", "fgFillInverted": "#faf1c9", "fgSurface1": "#7a6300", @@ -3330,7 +3330,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFillDark": "#1f1e1b", "bgFillInverted1": "#392c00", "bgFillInverted2": "#1f1e1b", - "fgFill": "#1f1e1b", + "fgFill": "#4f4e48", "fgFillDark": "#faf1c9", "fgFillInverted": "#faf1c9", "fgSurface1": "#977c00", @@ -3373,7 +3373,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFillDark": "#1f1e1b", "bgFillInverted1": "#392c00", "bgFillInverted2": "#1f1e1b", - "fgFill": "#1f1e1b", + "fgFill": "#4f4e48", "fgFillDark": "#faf1c9", "fgFillInverted": "#faf1c9", "fgSurface1": "#b39400", @@ -3415,7 +3415,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFillDark": "#1f1e1b", "bgFillInverted1": "#392c00", "bgFillInverted2": "#1f1e1b", - "fgFill": "#1f1e1b", + "fgFill": "#4f4e48", "fgFillDark": "#faf1c9", "fgFillInverted": "#faf1c9", "fgSurface1": "#ccae0e", @@ -3455,11 +3455,11 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "direction": "lighter", "ramp": { "bgFill1": "#f0b849", - "bgFill2": "#ffcd61", + "bgFill2": "#dda633", "bgFillDark": "#000", "bgFillInverted1": "#ffd670", "bgFillInverted2": "#f5efe5", - "fgFill": "#1f1e1b", + "fgFill": "#45423d", "fgFillDark": "#f5efe5", "fgFillInverted": "#1f1e1b", "fgSurface1": "#603e00", @@ -3497,11 +3497,11 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "direction": "lighter", "ramp": { "bgFill1": "#f0b849", - "bgFill2": "#ffcd61", + "bgFill2": "#dda633", "bgFillDark": "#000", "bgFillInverted1": "#ffd670", "bgFillInverted2": "#f5efe5", - "fgFill": "#1f1e1b", + "fgFill": "#45423d", "fgFillDark": "#f5efe5", "fgFillInverted": "#1f1e1b", "fgSurface1": "#613f00", @@ -3539,11 +3539,11 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "direction": "lighter", "ramp": { "bgFill1": "#f0b849", - "bgFill2": "#ffcd61", + "bgFill2": "#dda633", "bgFillDark": "#000", "bgFillInverted1": "#ffd670", "bgFillInverted2": "#f5efe5", - "fgFill": "#1f1e1b", + "fgFill": "#45423d", "fgFillDark": "#f5efe5", "fgFillInverted": "#1f1e1b", "fgSurface1": "#6c4700", @@ -3581,11 +3581,11 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "direction": "lighter", "ramp": { "bgFill1": "#f0b849", - "bgFill2": "#ffcd61", + "bgFill2": "#dda633", "bgFillDark": "#000", "bgFillInverted1": "#ffd670", "bgFillInverted2": "#f5efe5", - "fgFill": "#1f1e1b", + "fgFill": "#45423d", "fgFillDark": "#f5efe5", "fgFillInverted": "#1f1e1b", "fgSurface1": "#835700", @@ -3623,11 +3623,11 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "direction": "lighter", "ramp": { "bgFill1": "#f0b849", - "bgFill2": "#ffcd61", + "bgFill2": "#dda633", "bgFillDark": "#000", "bgFillInverted1": "#ffd670", "bgFillInverted2": "#f5efe5", - "fgFill": "#1f1e1b", + "fgFill": "#45423d", "fgFillDark": "#f5efe5", "fgFillInverted": "#1f1e1b", "fgSurface1": "#a57100", @@ -3711,7 +3711,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFillDark": "#1f1e1b", "bgFillInverted1": "#422800", "bgFillInverted2": "#1f1e1b", - "fgFill": "#1f1e1b", + "fgFill": "#45423d", "fgFillDark": "#f5efe5", "fgFillInverted": "#f5efe5", "fgSurface1": "#a87300", @@ -3754,7 +3754,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFillDark": "#1f1e1b", "bgFillInverted1": "#422800", "bgFillInverted2": "#1f1e1b", - "fgFill": "#1f1e1b", + "fgFill": "#45423d", "fgFillDark": "#f5efe5", "fgFillInverted": "#f5efe5", "fgSurface1": "#c38d00", @@ -3796,7 +3796,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFillDark": "#1f1e1b", "bgFillInverted1": "#422800", "bgFillInverted2": "#1f1e1b", - "fgFill": "#1f1e1b", + "fgFill": "#45423d", "fgFillDark": "#f5efe5", "fgFillInverted": "#f5efe5", "fgSurface1": "#dea633", @@ -3836,7 +3836,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "direction": "lighter", "ramp": { "bgFill1": "#cc1818", - "bgFill2": "#dd3029", + "bgFill2": "#b90000", "bgFillDark": "#000", "bgFillInverted1": "#ffd0c7", "bgFillInverted2": "#f2efef", @@ -3878,7 +3878,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "direction": "lighter", "ramp": { "bgFill1": "#cc1818", - "bgFill2": "#dd3029", + "bgFill2": "#b90000", "bgFillDark": "#000", "bgFillInverted1": "#ffd0c7", "bgFillInverted2": "#f2efef", @@ -3920,7 +3920,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "direction": "lighter", "ramp": { "bgFill1": "#cc1818", - "bgFill2": "#dd3029", + "bgFill2": "#b90000", "bgFillDark": "#000", "bgFillInverted1": "#ffd0c7", "bgFillInverted2": "#f2efef", @@ -3962,7 +3962,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "direction": "lighter", "ramp": { "bgFill1": "#cc1818", - "bgFill2": "#dd3029", + "bgFill2": "#b90000", "bgFillDark": "#000", "bgFillInverted1": "#ffd0c7", "bgFillInverted2": "#f2efef", @@ -4004,7 +4004,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "direction": "lighter", "ramp": { "bgFill1": "#cc1818", - "bgFill2": "#dd3029", + "bgFill2": "#b90000", "bgFillDark": "#000", "bgFillInverted1": "#ffd0c7", "bgFillInverted2": "#f2efef", @@ -4092,7 +4092,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFillDark": "#231c1b", "bgFillInverted1": "#640000", "bgFillInverted2": "#231c1b", - "fgFill": "#f2efef", + "fgFill": "#e9e6e6", "fgFillDark": "#f2efef", "fgFillInverted": "#f2efef", "fgSurface1": "#e63a32", @@ -4134,7 +4134,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFillDark": "#231c1b", "bgFillInverted1": "#640000", "bgFillInverted2": "#231c1b", - "fgFill": "#f2efef", + "fgFill": "#e9e6e6", "fgFillDark": "#f2efef", "fgFillInverted": "#f2efef", "fgSurface1": "#ff6052", @@ -4176,7 +4176,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFillDark": "#231c1b", "bgFillInverted1": "#640000", "bgFillInverted2": "#231c1b", - "fgFill": "#f2efef", + "fgFill": "#e9e6e6", "fgFillDark": "#f2efef", "fgFillInverted": "#f2efef", "fgSurface1": "#ff9182", @@ -15602,7 +15602,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#f0f0f0", + "fgFill": "#cbcbcb", "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", @@ -15638,7 +15638,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#f0f0f0", + "fgFill": "#cbcbcb", "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", @@ -15674,7 +15674,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#f0f0f0", + "fgFill": "#cbcbcb", "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", @@ -15710,7 +15710,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#f0f0f0", + "fgFill": "#cbcbcb", "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", @@ -15746,7 +15746,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#f0f0f0", + "fgFill": "#cbcbcb", "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", @@ -15782,7 +15782,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#f0f0f0", + "fgFill": "#cbcbcb", "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", @@ -15818,7 +15818,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#f0f0f0", + "fgFill": "#cbcbcb", "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", @@ -15854,7 +15854,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#f0f0f0", + "fgFill": "#cbcbcb", "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", @@ -15890,7 +15890,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#f0f0f0", + "fgFill": "#cbcbcb", "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", @@ -15926,7 +15926,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#f0f0f0", + "fgFill": "#cbcbcb", "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", @@ -15962,7 +15962,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#f0f0f0", + "fgFill": "#cbcbcb", "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", @@ -15998,7 +15998,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#f0f0f0", + "fgFill": "#cbcbcb", "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", @@ -16034,7 +16034,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#f0f0f0", + "fgFill": "#cbcbcb", "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", @@ -16070,7 +16070,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#f0f0f0", + "fgFill": "#cbcbcb", "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", @@ -16106,7 +16106,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#f0f0f0", + "fgFill": "#cbcbcb", "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", @@ -16142,7 +16142,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#f0f0f0", + "fgFill": "#cbcbcb", "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", @@ -16178,7 +16178,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#f0f0f0", + "fgFill": "#cbcbcb", "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", @@ -16214,7 +16214,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#f0f0f0", + "fgFill": "#cbcbcb", "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", @@ -16250,7 +16250,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#f0f0f0", + "fgFill": "#cbcbcb", "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", @@ -16286,7 +16286,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#f0f0f0", + "fgFill": "#cbcbcb", "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", @@ -16322,7 +16322,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#f0f0f0", + "fgFill": "#cbcbcb", "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", @@ -16358,7 +16358,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#f0f0f0", + "fgFill": "#cbcbcb", "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", @@ -16394,7 +16394,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#f0f0f0", + "fgFill": "#cbcbcb", "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", @@ -16430,7 +16430,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#f0f0f0", + "fgFill": "#cbcbcb", "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", @@ -16466,7 +16466,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#f0f0f0", + "fgFill": "#cbcbcb", "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", @@ -16502,7 +16502,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#f0f0f0", + "fgFill": "#cbcbcb", "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", @@ -16538,7 +16538,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#f0f0f0", + "fgFill": "#cbcbcb", "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", @@ -16574,7 +16574,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#f0f0f0", + "fgFill": "#cbcbcb", "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", @@ -16610,7 +16610,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#f0f0f0", + "fgFill": "#cbcbcb", "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", @@ -16646,7 +16646,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#f0f0f0", + "fgFill": "#cbcbcb", "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", @@ -16682,7 +16682,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#f0f0f0", + "fgFill": "#cbcbcb", "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", @@ -16718,7 +16718,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#f0f0f0", + "fgFill": "#cbcbcb", "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", @@ -16754,7 +16754,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#f0f0f0", + "fgFill": "#cbcbcb", "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", @@ -16790,7 +16790,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#f0f0f0", + "fgFill": "#cbcbcb", "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", @@ -16826,7 +16826,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#f0f0f0", + "fgFill": "#cbcbcb", "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", @@ -16862,7 +16862,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#f0f0f0", + "fgFill": "#cbcbcb", "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", diff --git a/packages/theme/src/color-ramps/test/index.test.ts b/packages/theme/src/color-ramps/test/index.test.ts index 7258b563db2612..52177e4e322c49 100644 --- a/packages/theme/src/color-ramps/test/index.test.ts +++ b/packages/theme/src/color-ramps/test/index.test.ts @@ -79,6 +79,21 @@ function getPerceptualContrastMagnitude( return Math.abs( contrastAPCA( background, foreground ) ); } +function expectAccessibleFillStates( ramp: ReturnType< typeof buildRamp > ) { + expect( getLuminance( ramp.ramp.bgFill2 ) ).toBeLessThan( + getLuminance( ramp.ramp.bgFill1 ) + ); + expect( + getContrast( ramp.ramp.bgFill1, ramp.ramp.bgFill2 ) + ).toBeGreaterThanOrEqual( 1.2 ); + expect( + getContrast( ramp.ramp.bgFill1, ramp.ramp.fgFill ) + ).toBeGreaterThanOrEqual( 4.5 ); + expect( + getContrast( ramp.ramp.bgFill2, ramp.ramp.fgFill ) + ).toBeGreaterThanOrEqual( 4.5 ); +} + function getForegroundConstraintReferences( stepIndex: number, ramp: ReturnType< typeof buildBgRamp >, diff --git a/packages/theme/tokens/color.json b/packages/theme/tokens/color.json index 55a370a63f4824..cd475e104456a5 100644 --- a/packages/theme/tokens/color.json +++ b/packages/theme/tokens/color.json @@ -167,8 +167,8 @@ "surface1": { "$value": { "colorSpace": "srgb", - "components": [ 0.925, 0.941, 0.98 ], - "hex": "#ecf0fa" + "components": [ 0.922, 0.941, 0.988 ], + "hex": "#ebf0fc" } } }, @@ -791,8 +791,8 @@ "fgFillDark": { "$value": { "colorSpace": "srgb", - "components": [ 0.949, 0.937, 0.937 ], - "hex": "#f2efef" + "components": [ 0.953, 0.937, 0.937 ], + "hex": "#f3efef" } }, "bgFillInverted2": { @@ -812,15 +812,15 @@ "fgFillInverted": { "$value": { "colorSpace": "srgb", - "components": [ 0.949, 0.937, 0.937 ], - "hex": "#f2efef" + "components": [ 0.953, 0.937, 0.937 ], + "hex": "#f3efef" } }, "surface1": { "$value": { "colorSpace": "srgb", - "components": [ 0.988, 0.925, 0.914 ], - "hex": "#fcece9" + "components": [ 0.996, 0.922, 0.91 ], + "hex": "#feebe8" } } }, From 221bb35c4ebe5f2157ff4d72a829db482b7c3657 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Tue, 1 Sep 2026 22:48:31 +0200 Subject: [PATCH 05/44] Theme: Add perceptual ramp comparison --- packages/theme/benchmark/README.md | 12 + .../benchmark/perceptual-ramp-experiments.ts | 126 ++++ .../stories/perceptual-ramp-experiment.ts | 612 ++++++++++++++++++ .../stories/perceptual-ramp-pilot.module.css | 236 +++++++ .../stories/perceptual-ramp-pilot.story.tsx | 600 +++++++++++++++++ .../test/perceptual-ramp-experiment.test.ts | 216 +++++++ 6 files changed, 1802 insertions(+) create mode 100644 packages/theme/benchmark/perceptual-ramp-experiments.ts create mode 100644 packages/theme/src/color-ramps/stories/perceptual-ramp-experiment.ts create mode 100644 packages/theme/src/color-ramps/stories/perceptual-ramp-pilot.module.css create mode 100644 packages/theme/src/color-ramps/stories/perceptual-ramp-pilot.story.tsx create mode 100644 packages/theme/src/color-ramps/stories/test/perceptual-ramp-experiment.test.ts diff --git a/packages/theme/benchmark/README.md b/packages/theme/benchmark/README.md index c3c95345f0551a..63775c18fda52a 100644 --- a/packages/theme/benchmark/README.md +++ b/packages/theme/benchmark/README.md @@ -26,3 +26,15 @@ Both worktrees need their dependencies installed. Add `--json` to save machine-readable results. Compare runs made with the same Node.js version and similar system load. The benchmark reports measurements but does not enforce a timing threshold because local and CI timings vary. + +## Perceptual ramp experiments + +The Storybook pilot also has a benchmark that compares the anchored production +candidate, the all-APCA experiment, and the role-specific hybrid. Each sample +builds the neutral, brand, and error ramps for all six pilot seed combinations. + +```sh +npm run benchmark:perceptual-ramps --workspace @wordpress/theme +``` + +Add `-- --json` for machine-readable output. diff --git a/packages/theme/benchmark/perceptual-ramp-experiments.ts b/packages/theme/benchmark/perceptual-ramp-experiments.ts new file mode 100644 index 00000000000000..c72e42a5bfee8a --- /dev/null +++ b/packages/theme/benchmark/perceptual-ramp-experiments.ts @@ -0,0 +1,126 @@ +/* eslint-disable no-console -- This command writes benchmark output. */ +import { performance } from 'node:perf_hooks'; +import { parseArgs } from 'node:util'; +import { + EXPERIMENTAL_RAMP_METHODS, + buildExperimentalThemeRamps, + type ExperimentalRampMethod, +} from '../src/color-ramps/stories/perceptual-ramp-experiment'; + +type BenchmarkResult = { + method: ExperimentalRampMethod; + medianMs: number; + p95Ms: number; + checksum: number; +}; + +const WARMUP_SAMPLES = 8; +const MEASURED_SAMPLES = 30; +const FIXTURES = [ + { + background: '#fcfcfc', + primary: '#3858e9', + }, + { + background: '#1e1e1e', + primary: '#3858e9', + }, + { + background: '#4f386e', + primary: '#608010', + }, + { + background: '#777777', + primary: '#d63638', + }, + { + background: '#fcfcfc', + primary: '#ffd700', + }, + { + background: '#1e1e1e', + primary: '#00ffff', + }, +] as const; + +const { values } = parseArgs( { + options: { + json: { type: 'boolean', default: false }, + }, + strict: true, +} ); + +function updateChecksum( checksum: number, value: string ) { + for ( const character of value ) { + checksum = + ( checksum * 31 + ( character.codePointAt( 0 ) ?? 0 ) ) % + 2_147_483_647; + } + return checksum; +} + +function runMethod( method: ExperimentalRampMethod ) { + let checksum = 0; + const runSample = () => { + const start = performance.now(); + for ( const fixture of FIXTURES ) { + const ramps = buildExperimentalThemeRamps( { method, ...fixture } ); + checksum = updateChecksum( + checksum, + `${ ramps.background.ramp.fgSurface5 }${ ramps.primary.ramp.bgFill2 }${ ramps.error.ramp.stroke3 }` + ); + } + return ( performance.now() - start ) / FIXTURES.length; + }; + + for ( let sample = 0; sample < WARMUP_SAMPLES; sample++ ) { + runSample(); + } + + const samples = Array.from( { length: MEASURED_SAMPLES }, runSample ).sort( + ( first, second ) => first - second + ); + const middle = Math.floor( samples.length / 2 ); + const medianMs = + samples.length % 2 === 0 + ? ( samples[ middle - 1 ] + samples[ middle ] ) / 2 + : samples[ middle ]; + + return { + method, + medianMs, + p95Ms: samples[ Math.ceil( samples.length * 0.95 ) - 1 ], + checksum, + } satisfies BenchmarkResult; +} + +const results = EXPERIMENTAL_RAMP_METHODS.map( runMethod ); +const report = { + environment: { + node: process.version, + platform: process.platform, + arch: process.arch, + }, + warmupSamples: WARMUP_SAMPLES, + measuredSamples: MEASURED_SAMPLES, + fixtures: FIXTURES, + results, +}; + +if ( values.json ) { + console.log( JSON.stringify( report, null, 2 ) ); +} else { + console.log( 'Perceptual ramp experiment benchmark' ); + console.log( + `${ report.environment.node } ${ report.environment.platform }/${ report.environment.arch }; ${ MEASURED_SAMPLES } samples after ${ WARMUP_SAMPLES } warmups` + ); + console.log( `${ FIXTURES.length } ramp triplets per sample` ); + console.table( + results.map( ( result ) => ( { + method: result.method, + 'median ms/ramp triplet': result.medianMs.toFixed( 3 ), + 'p95 ms/ramp triplet': result.p95Ms.toFixed( 3 ), + } ) ) + ); +} +/* eslint-enable no-console */ diff --git a/packages/theme/src/color-ramps/stories/perceptual-ramp-experiment.ts b/packages/theme/src/color-ramps/stories/perceptual-ramp-experiment.ts new file mode 100644 index 00000000000000..6c5bcd72353db1 --- /dev/null +++ b/packages/theme/src/color-ramps/stories/perceptual-ramp-experiment.ts @@ -0,0 +1,612 @@ +import { + ColorSpace, + contrastAPCA, + deltaEOK2, + get, + OKLab, + OKLCH, + OKLCH_sRGB as OklchSrgb, + sRGB, + to, + type PlainColorObject, +} from 'colorjs.io/fn'; +import { buildAccentRamp, buildBgRamp } from '../index.ts'; +import { buildForegroundScale } from '../lib/build-foreground-scale'; +import { clampToGamut, getColorString, getContrast } from '../lib/color-utils'; +import { + DEFAULT_SEED_COLORS, + UNIVERSAL_CONTRAST_TOPUP, +} from '../lib/constants'; +import { ACCENT_RAMP_CONFIG, BG_RAMP_CONFIG } from '../lib/ramp-configs'; +import { taperChroma } from '../lib/taper-chroma'; +import type { Ramp, RampResult } from '../lib/types'; +import { solveWithBisect } from '../lib/utils'; + +ColorSpace.register( sRGB ); +ColorSpace.register( OKLab ); +ColorSpace.register( OKLCH ); +ColorSpace.register( OklchSrgb ); + +export const EXPERIMENTAL_RAMP_METHODS = [ + 'anchored', + 'apca-all', + 'role-hybrid', +] as const; + +export type ExperimentalRampMethod = + ( typeof EXPERIMENTAL_RAMP_METHODS )[ number ]; + +export type ExperimentalThemeRamps = { + background: RampResult; + primary: RampResult; + error: RampResult; +}; + +type RampFamily = 'neutral' | 'accent'; +type PerceptualMetric = 'apca' | 'delta-e'; +type ChromaMode = 'absolute' | 'relative' | 'tapered'; +type GetColorAtLightness = ( lightness: number ) => PlainColorObject; + +const SURFACE_STEPS = [ 'surface4', 'surface5', 'surface6' ] as const; +const STROKE_STEPS = [ 'stroke1', 'stroke2', 'stroke3', 'stroke4' ] as const; +const SURFACE_PROGRESS = [ 1 / 3, 2 / 3, 1 ] as const; +const STROKE_PROGRESS = [ 0, 1 / 3, 2 / 3, 1 ] as const; +const MINIMUM_ACTIVE_FILL_DELTA_E = 0.055; +const MINIMUM_ACTIVE_FILL_APCA = 12; +const SERIALIZED_FILL_WCAG_TARGET = BG_RAMP_CONFIG.steps.fgFill.contrast.target; +const SERIALIZED_ACTIVE_FILL_WCAG_TARGET = + BG_RAMP_CONFIG.steps.bgFill2.contrast.target + UNIVERSAL_CONTRAST_TOPUP; +const SERIALIZED_STROKE_WCAG_TARGET = + BG_RAMP_CONFIG.steps.stroke3.contrast.target + UNIVERSAL_CONTRAST_TOPUP; + +function getMetric( + metric: PerceptualMetric, + reference: string | PlainColorObject, + color: string | PlainColorObject +) { + return metric === 'apca' + ? Math.abs( contrastAPCA( reference, color ) ) + : deltaEOK2( reference, color ); +} + +function prepareMetricReference( + metric: PerceptualMetric, + reference: string | PlainColorObject +) { + return to( clampToGamut( reference ), metric === 'apca' ? sRGB : OKLab ); +} + +function createColorAtLightness( + seed: string | PlainColorObject, + mode: ChromaMode, + taperOptions = BG_RAMP_CONFIG.steps.surface4.taperChromaOptions +): GetColorAtLightness { + const parsedSeed = clampToGamut( seed ); + + if ( mode === 'relative' ) { + const relativeSeed = to( parsedSeed, OklchSrgb ); + const chroma = get( relativeSeed, [ OklchSrgb, 'c' ] ); + const hue = get( relativeSeed, [ OklchSrgb, 'h' ] ); + return ( lightness ) => + to( + { + space: OklchSrgb, + coords: [ lightness, chroma, hue ], + alpha: parsedSeed.alpha, + }, + OKLCH + ); + } + + if ( mode === 'tapered' ) { + return ( lightness ) => { + const tapered = taperChroma( parsedSeed, lightness, taperOptions ); + if ( 'l' in tapered && 'c' in tapered ) { + return clampToGamut( { + space: OKLCH, + coords: [ + tapered.l, + tapered.c, + get( parsedSeed, [ OKLCH, 'h' ] ), + ], + alpha: parsedSeed.alpha, + } ); + } + return clampToGamut( tapered ); + }; + } + + return ( lightness ) => + clampToGamut( { + space: OKLCH, + coords: [ + lightness, + get( parsedSeed, [ OKLCH, 'c' ] ), + get( parsedSeed, [ OKLCH, 'h' ] ), + ], + alpha: parsedSeed.alpha, + } ); +} + +function getClosestBoundary( + target: number, + first: { color: PlainColorObject; value: number }, + second: { color: PlainColorObject; value: number } +) { + return Math.abs( first.value - target ) <= Math.abs( second.value - target ) + ? first.color + : second.color; +} + +function findColorAtMetric( { + metric, + reference, + getColorAtLightness, + startLightness, + endLightness, + target, +}: { + metric: PerceptualMetric; + reference: string | PlainColorObject; + getColorAtLightness: GetColorAtLightness; + startLightness: number; + endLightness: number; + target: number; +} ) { + const startColor = getColorAtLightness( startLightness ); + const endColor = getColorAtLightness( endLightness ); + const startValue = getMetric( metric, reference, startColor ); + const endValue = getMetric( metric, reference, endColor ); + const lower = { color: startColor, value: startValue }; + const upper = { color: endColor, value: endValue }; + const minimumValue = Math.min( startValue, endValue ); + const maximumValue = Math.max( startValue, endValue ); + + if ( target <= minimumValue || target >= maximumValue ) { + return getClosestBoundary( target, lower, upper ); + } + + const startDelta = startValue - target; + const endDelta = endValue - target; + if ( Math.sign( startDelta ) === Math.sign( endDelta ) ) { + return getClosestBoundary( target, lower, upper ); + } + + return solveWithBisect( + getColorAtLightness, + ( color ) => getMetric( metric, reference, color ) - target, + startLightness, + startDelta, + endLightness, + endDelta + ); +} + +function getRampChromaMode( + method: Exclude< ExperimentalRampMethod, 'anchored' >, + family: RampFamily +): ChromaMode { + if ( family === 'neutral' ) { + return 'tapered'; + } + return method === 'role-hybrid' ? 'relative' : 'absolute'; +} + +function rebuildProgression( { + ramp, + referenceName, + steps, + progress, + metric, + seed, + mode, + taperOptions, +}: { + ramp: Record< keyof Ramp, string >; + referenceName: keyof Ramp; + steps: readonly ( keyof Ramp )[]; + progress: readonly number[]; + metric: PerceptualMetric; + seed: string; + mode: ChromaMode; + taperOptions?: Parameters< typeof taperChroma >[ 2 ]; +} ) { + const nextRamp = { ...ramp }; + const reference = prepareMetricReference( metric, ramp[ referenceName ] ); + const firstStep = steps[ 0 ]; + const lastStep = steps.at( -1 )!; + const startLightness = get( ramp[ firstStep ], [ OKLCH, 'l' ] ); + const endLightness = get( ramp[ lastStep ], [ OKLCH, 'l' ] ); + const seedForScale = mode === 'absolute' ? ramp[ lastStep ] : seed; + const getColorAtLightness = createColorAtLightness( + seedForScale, + mode, + taperOptions + ); + const startValue = getMetric( + metric, + reference, + getColorAtLightness( startLightness ) + ); + const endValue = getMetric( + metric, + reference, + getColorAtLightness( endLightness ) + ); + + steps.forEach( ( step, index ) => { + const target = + startValue + ( endValue - startValue ) * progress[ index ]; + nextRamp[ step ] = getColorString( + findColorAtMetric( { + metric, + reference, + getColorAtLightness, + startLightness, + endLightness, + target, + } ) + ); + } ); + + return nextRamp; +} + +function findColorMeetingWcag( { + color, + references, + target, + seed, + mode, +}: { + color: string; + references: readonly string[]; + target: number; + seed: string; + mode: ChromaMode; +} ) { + if ( + references.every( + ( reference ) => getContrast( reference, color ) >= target + ) + ) { + return color; + } + + const getColorAtLightness = createColorAtLightness( seed, mode ); + const endpoints = [ 0, 1 ].map( ( lightness ) => { + const endpoint = getColorAtLightness( lightness ); + return { + color: endpoint, + lightness, + margin: Math.min( + ...references.map( ( reference ) => + Math.log( getContrast( reference, endpoint ) / target ) + ) + ), + }; + } ); + const strongest = endpoints.reduce( ( best, candidate ) => + candidate.margin > best.margin ? candidate : best + ); + + if ( strongest.margin < 0 ) { + return getColorString( strongest.color ); + } + const colorLightness = get( color, [ OKLCH, 'l' ] ); + const weakColor = getColorAtLightness( colorLightness ); + const weakMargin = Math.min( + ...references.map( ( reference ) => + Math.log( getContrast( reference, weakColor ) / target ) + ) + ); + if ( weakMargin >= 0 ) { + return getColorString( weakColor ); + } + + return getColorString( + solveWithBisect( + getColorAtLightness, + ( candidate ) => + Math.min( + ...references.map( ( reference ) => + Math.log( getContrast( reference, candidate ) / target ) + ) + ), + colorLightness, + weakMargin, + strongest.lightness, + strongest.margin + ) + ); +} + +function darkenUntilContrast( { + color, + reference, + seed, + mode, + target, +}: { + color: string; + reference: string; + seed: string; + mode: ChromaMode; + target: number; +} ) { + if ( getContrast( reference, color ) >= target ) { + return color; + } + + const getColorAtLightness = createColorAtLightness( seed, mode ); + const endpoint = getColorAtLightness( 0 ); + const endpointDelta = Math.log( + getContrast( reference, endpoint ) / target + ); + if ( endpointDelta < 0 ) { + return getColorString( endpoint ); + } + const colorLightness = get( color, [ OKLCH, 'l' ] ); + const weakColor = getColorAtLightness( colorLightness ); + const colorDelta = Math.log( getContrast( reference, weakColor ) / target ); + if ( colorDelta >= 0 ) { + return getColorString( weakColor ); + } + + return getColorString( + solveWithBisect( + getColorAtLightness, + ( candidate ) => + Math.log( getContrast( reference, candidate ) / target ), + colorLightness, + colorDelta, + 0, + endpointDelta + ) + ); +} + +function rebuildElevationPair( { + ramp, + metric, + seed, + mode, +}: { + ramp: Record< keyof Ramp, string >; + metric: PerceptualMetric; + seed: string; + mode: ChromaMode; +} ) { + const nextRamp = { ...ramp }; + const reference = ramp.surface2; + const metricReference = prepareMetricReference( metric, reference ); + const target = Math.max( + getMetric( metric, metricReference, ramp.surface1 ), + getMetric( metric, metricReference, ramp.surface3 ) + ); + + for ( const step of [ 'surface1', 'surface3' ] as const ) { + const original = ramp[ step ]; + const getColorAtLightness = createColorAtLightness( + mode === 'absolute' ? original : seed, + mode, + BG_RAMP_CONFIG.steps[ step ].taperChromaOptions + ); + nextRamp[ step ] = getColorString( + findColorAtMetric( { + metric, + reference: metricReference, + getColorAtLightness, + startLightness: get( reference, [ OKLCH, 'l' ] ), + endLightness: get( original, [ OKLCH, 'l' ] ), + target, + } ) + ); + } + + return nextRamp; +} + +function rebuildActiveFill( { + ramp, + method, + seed, + mode, +}: { + ramp: Record< keyof Ramp, string >; + method: Exclude< ExperimentalRampMethod, 'anchored' >; + seed: string; + mode: ChromaMode; +} ) { + const nextRamp = { ...ramp }; + const metric: PerceptualMetric = method === 'apca-all' ? 'apca' : 'delta-e'; + const metricReference = prepareMetricReference( metric, ramp.bgFill1 ); + const currentMetric = getMetric( metric, metricReference, ramp.bgFill2 ); + const target = Math.max( + currentMetric, + metric === 'apca' + ? MINIMUM_ACTIVE_FILL_APCA + : MINIMUM_ACTIVE_FILL_DELTA_E + ); + const getColorAtLightness = createColorAtLightness( + mode === 'absolute' ? ramp.bgFill2 : seed, + mode + ); + const candidate = getColorString( + findColorAtMetric( { + metric, + reference: metricReference, + getColorAtLightness, + startLightness: get( ramp.bgFill1, [ OKLCH, 'l' ] ), + endLightness: 0, + target, + } ) + ); + nextRamp.bgFill2 = darkenUntilContrast( { + color: candidate, + reference: ramp.bgFill1, + seed: mode === 'absolute' ? ramp.bgFill2 : seed, + mode, + // Leave enough room for 8-bit sRGB serialization. + target: SERIALIZED_ACTIVE_FILL_WCAG_TARGET, + } ); + nextRamp.fgFill = findColorMeetingWcag( { + color: ramp.fgFill, + references: [ nextRamp.bgFill1, nextRamp.bgFill2 ], + target: SERIALIZED_FILL_WCAG_TARGET, + seed: ramp.fgFill, + mode: 'tapered', + } ); + + return nextRamp; +} + +function getStrokeReferences( + ramp: Record< keyof Ramp, string >, + backgroundRamp: RampResult +) { + return [ + ramp.surface1, + ramp.surface2, + ramp.surface3, + backgroundRamp.ramp.surface1, + backgroundRamp.ramp.surface2, + backgroundRamp.ramp.surface3, + ]; +} + +function rebuildRamp( { + ramp, + backgroundRamp, + method, + family, + seed, +}: { + ramp: RampResult; + backgroundRamp: RampResult; + method: Exclude< ExperimentalRampMethod, 'anchored' >; + family: RampFamily; + seed: string; +} ) { + const metric: PerceptualMetric = method === 'apca-all' ? 'apca' : 'delta-e'; + const mode = getRampChromaMode( method, family ); + let nextRamp = rebuildElevationPair( { + ramp: ramp.ramp, + metric, + seed, + mode, + } ); + nextRamp = rebuildProgression( { + ramp: nextRamp, + referenceName: 'surface2', + steps: SURFACE_STEPS, + progress: SURFACE_PROGRESS, + metric, + seed, + mode, + taperOptions: BG_RAMP_CONFIG.steps.surface4.taperChromaOptions, + } ); + nextRamp = rebuildProgression( { + ramp: nextRamp, + referenceName: 'surface3', + steps: STROKE_STEPS, + progress: STROKE_PROGRESS, + metric, + seed, + mode, + taperOptions: BG_RAMP_CONFIG.steps.stroke3.taperChromaOptions, + } ); + nextRamp.stroke3 = findColorMeetingWcag( { + color: nextRamp.stroke3, + references: getStrokeReferences( nextRamp, backgroundRamp ), + target: SERIALIZED_STROKE_WCAG_TARGET, + seed: nextRamp.stroke3, + mode, + } ); + nextRamp = rebuildActiveFill( { + ramp: nextRamp, + method, + seed, + mode, + } ); + + const intermediateResult: RampResult = { + ...ramp, + ramp: nextRamp, + warnings: undefined, + }; + const config = family === 'neutral' ? BG_RAMP_CONFIG : ACCENT_RAMP_CONFIG; + + return buildForegroundScale( + intermediateResult, + backgroundRamp, + config.foregroundScale + ); +} + +function getNeutralSeed( ramp: RampResult ) { + return ramp.ramp.surface2; +} + +function buildExperimentalBackground( + method: ExperimentalRampMethod, + background: string +) { + const anchored = buildBgRamp( background ); + if ( method === 'anchored' ) { + return anchored; + } + return rebuildRamp( { + ramp: anchored, + backgroundRamp: anchored, + method, + family: 'neutral', + seed: getNeutralSeed( anchored ), + } ); +} + +function buildExperimentalAccent( + method: ExperimentalRampMethod, + seed: string, + backgroundRamp: RampResult +) { + const anchored = buildAccentRamp( seed, backgroundRamp ); + if ( method === 'anchored' ) { + return anchored; + } + return rebuildRamp( { + ramp: anchored, + backgroundRamp, + method, + family: 'accent', + seed: anchored.ramp.bgFill1, + } ); +} + +/** + * Builds complete private comparison ramps. These methods are Storybook-only + * and do not change ThemeProvider or the package's public entrypoint. + */ +export function buildExperimentalThemeRamps( { + method, + background, + primary, + error = DEFAULT_SEED_COLORS.error, +}: { + method: ExperimentalRampMethod; + background: string; + primary: string; + error?: string; +} ): ExperimentalThemeRamps { + const backgroundRamp = buildExperimentalBackground( method, background ); + + return { + background: backgroundRamp, + primary: buildExperimentalAccent( method, primary, backgroundRamp ), + error: buildExperimentalAccent( method, error, backgroundRamp ), + }; +} + +export function getExperimentalChroma( color: string ) { + return get( color, [ OklchSrgb, 'c' ] ); +} diff --git a/packages/theme/src/color-ramps/stories/perceptual-ramp-pilot.module.css b/packages/theme/src/color-ramps/stories/perceptual-ramp-pilot.module.css new file mode 100644 index 00000000000000..247365c6234e54 --- /dev/null +++ b/packages/theme/src/color-ramps/stories/perceptual-ramp-pilot.module.css @@ -0,0 +1,236 @@ +.page { + display: grid; + gap: 32px; + font-family: var(--wpds-typography-font-family-body); +} + +.page h1, +.page h2, +.page h3, +.page p { + margin: 0; +} + +.introduction, +.seed-combination, +.seed-combination > header, +.scale-section, +.component-comparison { + display: grid; + gap: 12px; +} + +.seed-combination { + gap: 24px; + padding: 20px; + border: 1px solid #dcdcde; + border-radius: 8px; +} + +.seeds, +.seed, +.scale-heading, +.button-row { + display: flex; + flex-wrap: wrap; + align-items: center; +} + +.seeds { + gap: 16px; +} + +.seed { + gap: 6px; + font-size: 12px; +} + +.seed-swatch { + box-sizing: border-box; + width: 20px; + height: 20px; + border: 1px solid #757575; + border-radius: 4px; +} + +.seed code, +.scale-table code { + font-family: monospace; +} + +.scale-heading { + justify-content: space-between; + gap: 8px; +} + +.scale-heading h3 { + font-size: 16px; +} + +.table-navigation { + display: flex; + justify-content: flex-end; + gap: 8px; +} + +.table-navigation button { + padding: 4px 8px; + border: 1px solid #949494; + border-radius: 4px; + background: #fff; + font: inherit; + cursor: var(--wpds-cursor-control); +} + +.table-navigation button:focus-visible { + outline: 2px solid currentColor; + outline-offset: 2px; +} + +.table-scroll { + padding-block-end: 8px; + overflow-x: auto; + overscroll-behavior-inline: contain; +} + +.scale-table { + width: 100%; + min-width: 1560px; + table-layout: fixed; + border-collapse: separate; + border-spacing: 2px; +} + +.scale-table th, +.scale-table td { + text-align: start; + vertical-align: top; +} + +.scale-table thead th { + padding: 5px 6px; + background: #f6f7f7; + font-size: 10px; + text-align: center; +} + +.scale-table thead th:first-child { + width: 220px; +} + +.scale-table tbody th { + padding: 8px; + border: 1px solid #dcdcde; + background: #fff; +} + +.scale-table td { + box-sizing: border-box; + height: 88px; + padding: 6px; + font-size: 9px; + vertical-align: bottom; +} + +.scale-table td code { + font-size: 9px; + font-weight: 600; +} + +.method-summary { + display: grid; + gap: 3px; + font-size: 10px; + font-weight: 400; +} + +.method-summary strong { + font-size: 12px; +} + +.status { + justify-self: start; + padding: 2px 5px; + border-radius: 999px; + background: #e7f5e7; + color: #1d5f1d; + font-weight: 600; +} + +.status[data-pass="false"] { + background: #fcf0f1; + color: #8a2424; +} + +.component-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); + gap: 12px; +} + +.component-panel { + display: grid; + gap: 12px; + padding: 14px; + border: 1px solid var(--pilot-border); + border-radius: 6px; + background: var(--pilot-bg); + color: var(--pilot-fg); + font-size: 13px; +} + +.weak-text { + color: var(--pilot-fg-weak); +} + +.button-row { + gap: 8px; +} + +.button-row button { + min-height: 32px; + padding: 5px 9px; + border: 0; + border-radius: 4px; + font: inherit; + cursor: var(--wpds-cursor-control); +} + +.button-row button:focus-visible { + outline: 2px solid var(--pilot-focus); + outline-offset: 2px; +} + +.component-note { + font-size: 11px; +} + +.details, +.empty { + padding: 14px; + border: 1px solid #dcdcde; + border-radius: 6px; +} + +.details summary { + cursor: var(--wpds-cursor-control); + font-weight: 600; +} + +.details dl, +.details dl > div { + display: grid; + gap: 8px; +} + +.details dl > div { + grid-template-columns: minmax(220px, 1fr) 3fr; +} + +.details dt { + font-weight: 600; +} + +.details dd { + margin: 0; +} diff --git a/packages/theme/src/color-ramps/stories/perceptual-ramp-pilot.story.tsx b/packages/theme/src/color-ramps/stories/perceptual-ramp-pilot.story.tsx new file mode 100644 index 00000000000000..14d8d230d339df --- /dev/null +++ b/packages/theme/src/color-ramps/stories/perceptual-ramp-pilot.story.tsx @@ -0,0 +1,600 @@ +import type { Meta, StoryObj } from '@storybook/react-vite'; +import { useRef } from '@wordpress/element'; +import { contrastAPCA, deltaEOK2 } from 'colorjs.io/fn'; +import { checkAccessibleCombinations } from '..'; +import { getContrast } from '../lib/color-utils'; +import { DEFAULT_SEED_COLORS } from '../lib/constants'; +import type { Ramp, RampResult } from '../lib/types'; +import { + EXPERIMENTAL_RAMP_METHODS, + buildExperimentalThemeRamps, + getExperimentalChroma, + type ExperimentalRampMethod, + type ExperimentalThemeRamps, +} from './perceptual-ramp-experiment'; +import styles from './perceptual-ramp-pilot.module.css'; + +const SAMPLE_COMBINATIONS = [ + { + label: 'Default light', + background: DEFAULT_SEED_COLORS.background, + primary: DEFAULT_SEED_COLORS.primary, + }, + { + label: 'Default dark', + background: '#1e1e1e', + primary: DEFAULT_SEED_COLORS.primary, + }, + { + label: 'Ectoplasm stress case', + background: '#4f386e', + primary: '#608010', + }, + { + label: 'Middle-gray polarity boundary', + background: '#777777', + primary: '#d63638', + }, + { + label: 'Yellow gamut edge', + background: '#fcfcfc', + primary: '#ffd700', + }, + { + label: 'Cyan gamut edge', + background: '#1e1e1e', + primary: '#00ffff', + }, +] as const; + +const METHOD_DETAILS: Record< + ExperimentalRampMethod, + { label: string; description: string } +> = { + anchored: { + label: 'Anchored production candidate', + description: + 'Current draft algorithm. APCA shapes the five foreground steps and WCAG ratios shape the other lanes.', + }, + 'apca-all': { + label: 'APCA for every lane', + description: + 'Uses APCA as the spacing coordinate for surfaces, strokes, fills, and foregrounds. WCAG remains a hard gate.', + }, + 'role-hybrid': { + label: 'Role-specific hybrid', + description: + 'Uses APCA for foreground readability, OKLab color difference for surfaces and states, and relative chroma for accents.', + }, +}; + +const RAMP_STEPS = [ + { name: 'surface1', label: 'SF1', group: 'Surfaces' }, + { name: 'surface2', label: 'SF2', group: 'Surfaces' }, + { name: 'surface3', label: 'SF3', group: 'Surfaces' }, + { name: 'surface4', label: 'SF4', group: 'Surfaces' }, + { name: 'surface5', label: 'SF5', group: 'Surfaces' }, + { name: 'surface6', label: 'SF6', group: 'Surfaces' }, + { name: 'bgFill1', label: 'BGF1', group: 'Fills' }, + { name: 'bgFill2', label: 'BGF2', group: 'Fills' }, + { name: 'fgFill', label: 'FGF', group: 'Fills' }, + { name: 'stroke1', label: 'ST1', group: 'Strokes' }, + { name: 'stroke2', label: 'ST2', group: 'Strokes' }, + { name: 'stroke3', label: 'ST3', group: 'Strokes' }, + { name: 'stroke4', label: 'ST4', group: 'Strokes' }, + { name: 'fgSurface1', label: 'FGS1', group: 'Foregrounds' }, + { name: 'fgSurface2', label: 'FGS2', group: 'Foregrounds' }, + { name: 'fgSurface3', label: 'FGS3', group: 'Foregrounds' }, + { name: 'fgSurface4', label: 'FGS4', group: 'Foregrounds' }, + { name: 'fgSurface5', label: 'FGS5', group: 'Foregrounds' }, +] as const satisfies readonly { + name: keyof Ramp; + label: string; + group: string; +}[]; + +const SCALE_DETAILS = [ + { name: 'background', label: 'Neutral', seed: 'background' }, + { name: 'primary', label: 'Brand', seed: 'primary' }, + { name: 'error', label: 'Error', seed: 'error' }, +] as const; + +type PilotArgs = { + approaches: ExperimentalRampMethod[]; +}; + +const meta = { + title: 'Design System/Theme/Theme Provider/Perceptual Ramp Pilot', + parameters: { + controls: { expanded: true }, + docs: { canvas: { sourceState: 'hidden' } }, + }, + argTypes: { + approaches: { + control: { + type: 'inline-check', + labels: Object.fromEntries( + EXPERIMENTAL_RAMP_METHODS.map( ( method ) => [ + method, + METHOD_DETAILS[ method ].label, + ] ) + ), + }, + description: 'Approaches shown in each comparison table.', + options: EXPERIMENTAL_RAMP_METHODS, + }, + }, +} satisfies Meta< PilotArgs >; + +export default meta; + +type Story = StoryObj< PilotArgs >; +type ScaleName = ( typeof SCALE_DETAILS )[ number ][ 'name' ]; + +const rampCache = new Map< string, ExperimentalThemeRamps >(); + +function getRamps( + method: ExperimentalRampMethod, + background: string, + primary: string +) { + const key = `${ method }|${ background }|${ primary }`; + const cached = rampCache.get( key ); + if ( cached ) { + return cached; + } + + const ramps = buildExperimentalThemeRamps( { + method, + background, + primary, + error: DEFAULT_SEED_COLORS.error, + } ); + rampCache.set( key, ramps ); + return ramps; +} + +function getSwatchTextColor( color: string ) { + return getContrast( color, '#000' ) >= getContrast( color, '#fff' ) + ? '#000' + : '#fff'; +} + +function getCrossRampFailures( ramp: RampResult, backgroundRamp: RampResult ) { + let failures = 0; + for ( const surface of [ 'surface1', 'surface2', 'surface3' ] as const ) { + for ( const foreground of [ + 'fgSurface3', + 'fgSurface4', + 'fgSurface5', + ] as const ) { + if ( + getContrast( + backgroundRamp.ramp[ surface ], + ramp.ramp[ foreground ] + ) < 4.5 + ) { + failures++; + } + } + if ( + getContrast( backgroundRamp.ramp[ surface ], ramp.ramp.stroke3 ) < 3 + ) { + failures++; + } + } + return failures; +} + +function getScaleMetrics( + ramp: RampResult, + backgroundRamp: RampResult, + isAccent: boolean, + seed: string +) { + const displayBackground = backgroundRamp.ramp.surface2; + const currentForegroundContrast = Math.abs( + contrastAPCA( displayBackground, ramp.ramp.fgSurface4 ) + ); + const activeForegroundContrast = Math.abs( + contrastAPCA( displayBackground, ramp.ramp.fgSurface5 ) + ); + const failures = + checkAccessibleCombinations( { bgRamp: ramp } ).length + + ( isAccent ? getCrossRampFailures( ramp, backgroundRamp ) : 0 ); + const seedChroma = getExperimentalChroma( seed ); + const chromaDrift = isAccent + ? [ 'surface4', 'surface5', 'stroke2', 'stroke3' ].reduce( + ( total, step ) => + total + + Math.abs( + getExperimentalChroma( + ramp.ramp[ step as keyof Ramp ] + ) - seedChroma + ), + 0 + ) / 4 + : undefined; + + return { + failures, + fillDifference: deltaEOK2( ramp.ramp.bgFill1, ramp.ramp.bgFill2 ), + foregroundDifference: deltaEOK2( + ramp.ramp.fgSurface4, + ramp.ramp.fgSurface5 + ), + foregroundApcaInterval: + activeForegroundContrast - currentForegroundContrast, + chromaDrift, + }; +} + +function Seed( { label, color }: { label: string; color: string } ) { + return ( + + + ); +} + +function MethodSummary( { + method, + ramp, + backgroundRamp, + isAccent, + seed, +}: { + method: ExperimentalRampMethod; + ramp: RampResult; + backgroundRamp: RampResult; + isAccent: boolean; + seed: string; +} ) { + const metrics = getScaleMetrics( ramp, backgroundRamp, isAccent, seed ); + + return ( +
+ { METHOD_DETAILS[ method ].label } + + { metrics.failures === 0 + ? 'WCAG gates pass' + : `${ metrics.failures } WCAG failures` } + + Fill ΔE { metrics.fillDifference.toFixed( 3 ) } + FGS4→5 ΔE { metrics.foregroundDifference.toFixed( 3 ) } + + FGS4→5 ΔLc { metrics.foregroundApcaInterval.toFixed( 1 ) } + + { metrics.chromaDrift === undefined ? null : ( + + Mean relative chroma drift{ ' ' } + { metrics.chromaDrift.toFixed( 3 ) } + + ) } +
+ ); +} + +function ScaleTable( { + label, + seed, + scaleName, + methods, + rampsByMethod, +}: { + label: string; + seed: string; + scaleName: ScaleName; + methods: ExperimentalRampMethod[]; + rampsByMethod: Map< ExperimentalRampMethod, ExperimentalThemeRamps >; +} ) { + const groups = Array.from( + new Set( RAMP_STEPS.map( ( step ) => step.group ) ) + ); + const scrollContainer = useRef< HTMLDivElement >( null ); + const scrollTable = ( direction: -1 | 1 ) => { + const container = scrollContainer.current; + if ( ! container ) { + return; + } + container.scrollBy( { + behavior: 'auto', + left: direction * container.clientWidth * 0.75, + } ); + }; + + return ( +
+
+

{ label }

+ +
+
+ + +
+
+ + + + + { groups.map( ( group ) => ( + + ) ) } + + + { RAMP_STEPS.map( ( step ) => ( + + ) ) } + + + + { methods.map( ( method ) => { + const methodRamps = rampsByMethod.get( method )!; + const ramp = methodRamps[ scaleName ]; + return ( + + + { RAMP_STEPS.map( ( step ) => { + const color = ramp.ramp[ step.name ]; + return ( + + ); + } ) } + + ); + } ) } + +
+ Approach + step.group === group + ).length + } + key={ group } + scope="colgroup" + > + { group } +
+ { step.label } +
+ + + { color } +
+
+
+ ); +} + +function ComponentComparison( { + methods, + rampsByMethod, + label, +}: { + methods: ExperimentalRampMethod[]; + rampsByMethod: Map< ExperimentalRampMethod, ExperimentalThemeRamps >; + label: string; +} ) { + return ( +
+

Representative states

+
+ { methods.map( ( method ) => { + const ramps = rampsByMethod.get( method )!; + const neutral = ramps.background.ramp; + const brand = ramps.primary.ramp; + const error = ramps.error.ramp; + return ( +
+ { METHOD_DETAILS[ method ].label } +

+ Supporting text and hierarchy +

+
+ + +
+
+ + +
+
+ ); + } ) } +
+

+ { label } uses the same generated values as the tables above. +

+
+ ); +} + +function SeedCombination( { + label, + background, + primary, + methods, +}: { + label: string; + background: string; + primary: string; + methods: ExperimentalRampMethod[]; +} ) { + const rampsByMethod = new Map( + methods.map( ( method ) => [ + method, + getRamps( method, background, primary ), + ] ) + ); + const seeds = { + background, + primary, + error: DEFAULT_SEED_COLORS.error, + }; + + return ( +
+
+

{ label }

+
+ + + +
+
+ { SCALE_DETAILS.map( ( scale ) => ( + + ) ) } + +
+ ); +} + +export const Comparison: Story = { + args: { + approaches: [ ...EXPERIMENTAL_RAMP_METHODS ], + }, + render: ( { approaches } ) => { + const methods = EXPERIMENTAL_RAMP_METHODS.filter( ( method ) => + approaches.includes( method ) + ); + + return ( +
+
+

Perceptual ramp comparison

+

+ Compare each complete neutral, brand, and error ramp by + step. WCAG ratios remain hard gates in every approach. +

+
+ { methods.length === 0 ? ( +

+ Select at least one approach. +

+ ) : ( + SAMPLE_COMBINATIONS.map( ( combination ) => ( + + ) ) + ) } +
+ Approach definitions +
+ { EXPERIMENTAL_RAMP_METHODS.map( ( method ) => ( +
+
{ METHOD_DETAILS[ method ].label }
+
+ { METHOD_DETAILS[ method ].description } +
+
+ ) ) } +
+
+
+ ); + }, +}; diff --git a/packages/theme/src/color-ramps/stories/test/perceptual-ramp-experiment.test.ts b/packages/theme/src/color-ramps/stories/test/perceptual-ramp-experiment.test.ts new file mode 100644 index 00000000000000..2bf8e6314db2f0 --- /dev/null +++ b/packages/theme/src/color-ramps/stories/test/perceptual-ramp-experiment.test.ts @@ -0,0 +1,216 @@ +import { + ColorSpace, + contrastAPCA, + deltaEOK2, + get, + getLuminance, + OKLab, + OKLCH, + OKLCH_sRGB as OklchSrgb, + sRGB, +} from 'colorjs.io/fn'; +import { describe, expect, it } from 'vitest'; +import { checkAccessibleCombinations } from '../..'; +import { getContrast } from '../../lib/color-utils'; +import { DEFAULT_SEED_COLORS } from '../../lib/constants'; +import { + EXPERIMENTAL_RAMP_METHODS, + buildExperimentalThemeRamps, +} from '../perceptual-ramp-experiment'; + +ColorSpace.register( sRGB ); +ColorSpace.register( OKLab ); +ColorSpace.register( OKLCH ); +ColorSpace.register( OklchSrgb ); + +const SAMPLE_COMBINATIONS = [ + { + background: DEFAULT_SEED_COLORS.background, + primary: DEFAULT_SEED_COLORS.primary, + }, + { background: '#1e1e1e', primary: DEFAULT_SEED_COLORS.primary }, + { background: '#4f386e', primary: '#608010' }, + { background: '#777777', primary: '#d63638' }, + { background: '#fcfcfc', primary: '#ffd700' }, + { background: '#1e1e1e', primary: '#00ffff' }, +] as const; + +const FOREGROUND_STEPS = [ + 'fgSurface1', + 'fgSurface2', + 'fgSurface3', + 'fgSurface4', + 'fgSurface5', +] as const; +const STROKE_STEPS = [ 'stroke1', 'stroke2', 'stroke3', 'stroke4' ] as const; + +function getForegroundContrasts( + background: string, + ramp: ReturnType< typeof buildExperimentalThemeRamps >[ 'background' ] +) { + return FOREGROUND_STEPS.map( ( step ) => + Math.abs( contrastAPCA( background, ramp.ramp[ step ] ) ) + ); +} + +describe( 'perceptual ramp experiment', () => { + it.each( EXPERIMENTAL_RAMP_METHODS )( + 'builds complete %s ramps for every representative seed pair', + ( method ) => { + for ( const seeds of SAMPLE_COMBINATIONS ) { + const ramps = buildExperimentalThemeRamps( { + method, + ...seeds, + } ); + + for ( const ramp of Object.values( ramps ) ) { + expect( Object.keys( ramp.ramp ) ).toHaveLength( 23 ); + expect( Object.values( ramp.ramp ) ).not.toContain( + undefined + ); + } + } + } + ); + + it.each( EXPERIMENTAL_RAMP_METHODS )( + 'keeps every configured WCAG pair accessible in the %s ramps', + ( method ) => { + for ( const seeds of SAMPLE_COMBINATIONS ) { + const { background, primary, error } = + buildExperimentalThemeRamps( { method, ...seeds } ); + + for ( const ramp of [ background, primary, error ] ) { + expect( + checkAccessibleCombinations( { bgRamp: ramp } ) + ).toEqual( [] ); + } + + for ( const ramp of [ primary, error ] ) { + for ( const surface of [ + 'surface1', + 'surface2', + 'surface3', + ] as const ) { + for ( const foreground of [ + 'fgSurface3', + 'fgSurface4', + 'fgSurface5', + ] as const ) { + expect( + getContrast( + background.ramp[ surface ], + ramp.ramp[ foreground ] + ) + ).toBeGreaterThanOrEqual( 4.5 ); + } + expect( + getContrast( + background.ramp[ surface ], + ramp.ramp.stroke3 + ) + ).toBeGreaterThanOrEqual( 3 ); + } + } + } + } + ); + + it.each( EXPERIMENTAL_RAMP_METHODS )( + 'orders foreground and stroke strength and keeps active fills darker in the %s ramps', + ( method ) => { + for ( const seeds of SAMPLE_COMBINATIONS ) { + const ramps = buildExperimentalThemeRamps( { + method, + ...seeds, + } ); + const displayBackground = ramps.background.ramp.surface2; + + for ( const ramp of Object.values( ramps ) ) { + const contrasts = getForegroundContrasts( + displayBackground, + ramp + ); + expect( contrasts ).toEqual( + [ ...contrasts ].sort( ( a, b ) => a - b ) + ); + const strokeContrasts = STROKE_STEPS.map( ( step ) => + Math.abs( + contrastAPCA( + ramp.ramp.surface3, + ramp.ramp[ step ] + ) + ) + ); + expect( strokeContrasts ).toEqual( + [ ...strokeContrasts ].sort( ( a, b ) => a - b ) + ); + expect( getLuminance( ramp.ramp.bgFill2 ) ).toBeLessThan( + getLuminance( ramp.ramp.bgFill1 ) + ); + expect( + getContrast( ramp.ramp.bgFill1, ramp.ramp.bgFill2 ) + ).toBeGreaterThanOrEqual( 1.2 ); + } + } + } + ); + + it( 'keeps more accent chroma in the role-specific hybrid than in APCA-all', () => { + for ( const seeds of SAMPLE_COMBINATIONS.slice( -2 ) ) { + const apca = buildExperimentalThemeRamps( { + method: 'apca-all', + ...seeds, + } ).primary.ramp; + const hybrid = buildExperimentalThemeRamps( { + method: 'role-hybrid', + ...seeds, + } ).primary.ramp; + const seedChroma = get( seeds.primary, [ OklchSrgb, 'c' ] ); + const steps = [ + 'surface4', + 'surface5', + 'stroke2', + 'stroke3', + ] as const; + const totalDrift = ( ramp: typeof apca ) => + steps.reduce( + ( drift, step ) => + drift + + Math.abs( + get( ramp[ step ], [ OklchSrgb, 'c' ] ) - seedChroma + ), + 0 + ); + + expect( totalDrift( hybrid ) ).toBeLessThanOrEqual( + totalDrift( apca ) + ); + } + } ); + + it( 'gives the hybrid a measurable active-state difference', () => { + for ( const seeds of SAMPLE_COMBINATIONS ) { + const ramps = buildExperimentalThemeRamps( { + method: 'role-hybrid', + ...seeds, + } ); + + for ( const ramp of Object.values( ramps ) ) { + expect( + deltaEOK2( ramp.ramp.bgFill1, ramp.ramp.bgFill2 ) + ).toBeGreaterThanOrEqual( 0.045 ); + } + } + } ); + + it.each( EXPERIMENTAL_RAMP_METHODS )( + 'returns deterministic output for the %s method', + ( method ) => { + const seeds = SAMPLE_COMBINATIONS[ 2 ]; + expect( + buildExperimentalThemeRamps( { method, ...seeds } ) + ).toEqual( buildExperimentalThemeRamps( { method, ...seeds } ) ); + } + ); +} ); From ad46cfca7f9365b3b4723603d03f9e2e51ae1992 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Wed, 2 Sep 2026 11:43:31 +0200 Subject: [PATCH 06/44] Theme: Add pinned-seed ramp comparison --- .../benchmark/perceptual-ramp-experiments.ts | 4 +- .../src/color-ramps/stories/index.story.tsx | 6 +- .../stories/perceptual-ramp-experiment.ts | 198 +++++++++++++++++- .../stories/perceptual-ramp-pilot.module.css | 21 ++ .../stories/perceptual-ramp-pilot.story.tsx | 67 +++++- .../src/color-ramps/stories/ramp-table.tsx | 11 +- .../test/perceptual-ramp-experiment.test.ts | 62 ++++-- .../theme-example-application.story.tsx | 2 +- 8 files changed, 339 insertions(+), 32 deletions(-) diff --git a/packages/theme/benchmark/perceptual-ramp-experiments.ts b/packages/theme/benchmark/perceptual-ramp-experiments.ts index c72e42a5bfee8a..8101ba17a6e8c2 100644 --- a/packages/theme/benchmark/perceptual-ramp-experiments.ts +++ b/packages/theme/benchmark/perceptual-ramp-experiments.ts @@ -26,8 +26,8 @@ const FIXTURES = [ primary: '#3858e9', }, { - background: '#4f386e', - primary: '#608010', + background: '#413256', + primary: '#a3b745', }, { background: '#777777', diff --git a/packages/theme/src/color-ramps/stories/index.story.tsx b/packages/theme/src/color-ramps/stories/index.story.tsx index b3e326d93dc6c4..3ce6fd87de8cf7 100644 --- a/packages/theme/src/color-ramps/stories/index.story.tsx +++ b/packages/theme/src/color-ramps/stories/index.story.tsx @@ -241,9 +241,9 @@ const SAMPLE_COMBINATIONS = [ }, { label: 'Ectoplasm', - source: 'WordPress admin scheme', - background: '#4f386e', - primary: '#646c3e', + source: 'Theme exploration', + background: '#413256', + primary: '#a3b745', }, { label: 'Ocean', diff --git a/packages/theme/src/color-ramps/stories/perceptual-ramp-experiment.ts b/packages/theme/src/color-ramps/stories/perceptual-ramp-experiment.ts index 6c5bcd72353db1..89100697ecdb7f 100644 --- a/packages/theme/src/color-ramps/stories/perceptual-ramp-experiment.ts +++ b/packages/theme/src/color-ramps/stories/perceptual-ramp-experiment.ts @@ -20,7 +20,11 @@ import { import { ACCENT_RAMP_CONFIG, BG_RAMP_CONFIG } from '../lib/ramp-configs'; import { taperChroma } from '../lib/taper-chroma'; import type { Ramp, RampResult } from '../lib/types'; -import { solveWithBisect } from '../lib/utils'; +import { buildRamp } from '../lib/index'; +import { + clampAccentScaleReferenceLightness, + solveWithBisect, +} from '../lib/utils'; ColorSpace.register( sRGB ); ColorSpace.register( OKLab ); @@ -31,6 +35,7 @@ export const EXPERIMENTAL_RAMP_METHODS = [ 'anchored', 'apca-all', 'role-hybrid', + 'pinned-role-hybrid', ] as const; export type ExperimentalRampMethod = @@ -48,6 +53,14 @@ type ChromaMode = 'absolute' | 'relative' | 'tapered'; type GetColorAtLightness = ( lightness: number ) => PlainColorObject; const SURFACE_STEPS = [ 'surface4', 'surface5', 'surface6' ] as const; +const PINNED_SURFACE_STEPS = [ + 'surface1', + 'surface3', + 'surface4', + 'surface5', + 'surface6', +] as const; +const PINNED_SURFACE_PROGRESS = [ 0.15, 0.3, 0.55, 0.75, 1 ] as const; const STROKE_STEPS = [ 'stroke1', 'stroke2', 'stroke3', 'stroke4' ] as const; const SURFACE_PROGRESS = [ 1 / 3, 2 / 3, 1 ] as const; const STROKE_PROGRESS = [ 0, 1 / 3, 2 / 3, 1 ] as const; @@ -58,6 +71,8 @@ const SERIALIZED_ACTIVE_FILL_WCAG_TARGET = BG_RAMP_CONFIG.steps.bgFill2.contrast.target + UNIVERSAL_CONTRAST_TOPUP; const SERIALIZED_STROKE_WCAG_TARGET = BG_RAMP_CONFIG.steps.stroke3.contrast.target + UNIVERSAL_CONTRAST_TOPUP; +const SERIALIZED_SURFACE_FOREGROUND_WCAG_TARGET = + 4.5 + UNIVERSAL_CONTRAST_TOPUP; function getMetric( metric: PerceptualMetric, @@ -189,7 +204,9 @@ function getRampChromaMode( if ( family === 'neutral' ) { return 'tapered'; } - return method === 'role-hybrid' ? 'relative' : 'absolute'; + return method === 'role-hybrid' || method === 'pinned-role-hybrid' + ? 'relative' + : 'absolute'; } function rebuildProgression( { @@ -407,6 +424,137 @@ function rebuildElevationPair( { return nextRamp; } +function rebuildPinnedSurfaceLane( { + ramp, + direction, + seed, + mode, +}: { + ramp: Record< keyof Ramp, string >; + direction: RampResult[ 'direction' ]; + seed: string; + mode: ChromaMode; +} ) { + const getColorAtLightness = createColorAtLightness( seed, mode ); + const foregroundEndpoint = getColorAtLightness( + direction === 'lighter' ? 1 : 0 + ); + const constrainedSurfaces = [ + 'surface1', + 'surface2', + 'surface3', + 'surface4', + 'surface5', + ] as const; + + if ( + constrainedSurfaces.every( + ( step ) => + getContrast( ramp[ step ], foregroundEndpoint ) >= + SERIALIZED_SURFACE_FOREGROUND_WCAG_TARGET + ) + ) { + return ramp; + } + + const reference = prepareMetricReference( 'delta-e', ramp.surface2 ); + const startLightness = get( ramp.surface2, [ OKLCH, 'l' ] ); + const endLightness = direction === 'lighter' ? 0 : 1; + const maximumDifference = Math.max( + ...PINNED_SURFACE_STEPS.map( ( step ) => + getMetric( 'delta-e', reference, ramp[ step ] ) + ) + ); + const nextRamp = { ...ramp }; + + PINNED_SURFACE_STEPS.forEach( ( step, index ) => { + nextRamp[ step ] = getColorString( + findColorAtMetric( { + metric: 'delta-e', + reference, + getColorAtLightness, + startLightness, + endLightness, + target: maximumDifference * PINNED_SURFACE_PROGRESS[ index ], + } ) + ); + } ); + + return nextRamp; +} + +function findColorAtWcagContrast( { + reference, + getColorAtLightness, + startLightness, + endLightness, + target, +}: { + reference: string; + getColorAtLightness: GetColorAtLightness; + startLightness: number; + endLightness: number; + target: number; +} ) { + const startColor = getColorAtLightness( startLightness ); + const startDelta = Math.log( + getContrast( reference, startColor ) / target + ); + + if ( startDelta >= 0 ) { + return startColor; + } + + const endColor = getColorAtLightness( endLightness ); + const endDelta = Math.log( getContrast( reference, endColor ) / target ); + if ( endDelta < 0 ) { + return endColor; + } + + return solveWithBisect( + getColorAtLightness, + ( color ) => Math.log( getContrast( reference, color ) / target ), + startLightness, + startDelta, + endLightness, + endDelta + ); +} + +function createRoomForDarkerActiveFill( { + ramp, + seed, + mode, +}: { + ramp: Record< keyof Ramp, string >; + seed: string; + mode: ChromaMode; +} ) { + if ( + getContrast( ramp.bgFill1, ramp.bgFill2 ) >= + SERIALIZED_ACTIVE_FILL_WCAG_TARGET + ) { + return ramp; + } + + const getColorAtLightness = createColorAtLightness( seed, mode ); + const nextRamp = { ...ramp }; + nextRamp.bgFill1 = getColorString( + findColorAtWcagContrast( { + reference: ramp.surface2, + getColorAtLightness, + startLightness: get( ramp.surface2, [ OKLCH, 'l' ] ), + endLightness: 1, + target: + BG_RAMP_CONFIG.steps.bgFill1.contrast.target + + UNIVERSAL_CONTRAST_TOPUP, + } ) + ); + nextRamp.bgFill2 = nextRamp.bgFill1; + + return nextRamp; +} + function rebuildActiveFill( { ramp, method, @@ -506,6 +654,14 @@ function rebuildRamp( { mode, taperOptions: BG_RAMP_CONFIG.steps.surface4.taperChromaOptions, } ); + if ( method === 'pinned-role-hybrid' ) { + nextRamp = rebuildPinnedSurfaceLane( { + ramp: nextRamp, + direction: ramp.direction, + seed, + mode, + } ); + } nextRamp = rebuildProgression( { ramp: nextRamp, referenceName: 'surface3', @@ -529,6 +685,21 @@ function rebuildRamp( { seed, mode, } ); + if ( method === 'pinned-role-hybrid' && family === 'neutral' ) { + const fillRamp = createRoomForDarkerActiveFill( { + ramp: nextRamp, + seed, + mode, + } ); + if ( fillRamp !== nextRamp ) { + nextRamp = rebuildActiveFill( { + ramp: fillRamp, + method, + seed, + mode, + } ); + } + } const intermediateResult: RampResult = { ...ramp, @@ -552,7 +723,12 @@ function buildExperimentalBackground( method: ExperimentalRampMethod, background: string ) { - const anchored = buildBgRamp( background ); + const anchored = + method === 'pinned-role-hybrid' + ? buildRamp( background, BG_RAMP_CONFIG, { + rescaleToFitContrastTargets: false, + } ) + : buildBgRamp( background ); if ( method === 'anchored' ) { return anchored; } @@ -570,7 +746,21 @@ function buildExperimentalAccent( seed: string, backgroundRamp: RampResult ) { - const anchored = buildAccentRamp( seed, backgroundRamp ); + const anchored = + method === 'pinned-role-hybrid' + ? buildRamp( seed, ACCENT_RAMP_CONFIG, { + backgroundRamp, + mainDirection: backgroundRamp.direction, + pinLightness: { + stepName: 'surface2', + value: clampAccentScaleReferenceLightness( + get( backgroundRamp.ramp.surface2, [ OKLCH, 'l' ] ), + backgroundRamp.direction + ), + }, + rescaleToFitContrastTargets: false, + } ) + : buildAccentRamp( seed, backgroundRamp ); if ( method === 'anchored' ) { return anchored; } diff --git a/packages/theme/src/color-ramps/stories/perceptual-ramp-pilot.module.css b/packages/theme/src/color-ramps/stories/perceptual-ramp-pilot.module.css index 247365c6234e54..21424051d9b266 100644 --- a/packages/theme/src/color-ramps/stories/perceptual-ramp-pilot.module.css +++ b/packages/theme/src/color-ramps/stories/perceptual-ramp-pilot.module.css @@ -125,6 +125,7 @@ } .scale-table td { + position: relative; box-sizing: border-box; height: 88px; padding: 6px; @@ -162,6 +163,26 @@ color: #8a2424; } +.anchor-status { + justify-self: start; + font-weight: 600; +} + +.anchor-status[data-preserved="true"] { + color: #1d5f1d; +} + +.seed-anchor { + position: absolute; + inset-block-start: 0; + inset-inline: 0; + box-sizing: border-box; + padding: 3px 5px; + border-block-end: 1px dashed currentColor; + font-size: 8px; + font-weight: 600; +} + .component-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); diff --git a/packages/theme/src/color-ramps/stories/perceptual-ramp-pilot.story.tsx b/packages/theme/src/color-ramps/stories/perceptual-ramp-pilot.story.tsx index 14d8d230d339df..4ee1eee4540d1b 100644 --- a/packages/theme/src/color-ramps/stories/perceptual-ramp-pilot.story.tsx +++ b/packages/theme/src/color-ramps/stories/perceptual-ramp-pilot.story.tsx @@ -1,6 +1,6 @@ import type { Meta, StoryObj } from '@storybook/react-vite'; import { useRef } from '@wordpress/element'; -import { contrastAPCA, deltaEOK2 } from 'colorjs.io/fn'; +import { contrastAPCA, deltaEOK2, get, OKLCH } from 'colorjs.io/fn'; import { checkAccessibleCombinations } from '..'; import { getContrast } from '../lib/color-utils'; import { DEFAULT_SEED_COLORS } from '../lib/constants'; @@ -27,8 +27,23 @@ const SAMPLE_COMBINATIONS = [ }, { label: 'Ectoplasm stress case', - background: '#4f386e', - primary: '#608010', + background: '#413256', + primary: '#a3b745', + }, + { + label: 'Blue admin scheme', + background: '#3876a8', + primary: '#437aa8', + }, + { + label: 'Ocean admin scheme', + background: '#5f787f', + primary: '#567958', + }, + { + label: 'Sunrise admin scheme', + background: '#cc4541', + primary: '#ad631e', }, { label: 'Middle-gray polarity boundary', @@ -66,6 +81,11 @@ const METHOD_DETAILS: Record< description: 'Uses APCA for foreground readability, OKLab color difference for surfaces and states, and relative chroma for accents.', }, + 'pinned-role-hybrid': { + label: 'Pinned-seed role hybrid', + description: + 'Pins the supplied color at its semantic anchor, then redirects neighboring surfaces and fills when the original polarity has no contrast headroom.', + }, }; const RAMP_STEPS = [ @@ -192,6 +212,7 @@ function getScaleMetrics( isAccent: boolean, seed: string ) { + const anchor = isAccent ? ramp.ramp.bgFill1 : ramp.ramp.surface2; const displayBackground = backgroundRamp.ramp.surface2; const currentForegroundContrast = Math.abs( contrastAPCA( displayBackground, ramp.ramp.fgSurface4 ) @@ -218,6 +239,9 @@ function getScaleMetrics( return { failures, + anchorDifference: deltaEOK2( seed, anchor ), + anchorLightnessShift: + get( anchor, [ OKLCH, 'l' ] ) - get( seed, [ OKLCH, 'l' ] ), fillDifference: deltaEOK2( ramp.ramp.bgFill1, ramp.ramp.bgFill2 ), foregroundDifference: deltaEOK2( ramp.ramp.fgSurface4, @@ -268,6 +292,21 @@ function MethodSummary( { ? 'WCAG gates pass' : `${ metrics.failures } WCAG failures` }
+ + { metrics.anchorDifference <= 0.002 + ? 'Seed preserved' + : `Seed moved ΔE ${ metrics.anchorDifference.toFixed( + 3 + ) }` } + + Anchor ΔL { metrics.anchorLightnessShift.toFixed( 3 ) } + + Foreground polarity:{ ' ' } + { ramp.direction === 'lighter' ? 'light' : 'dark' } + Fill ΔE { metrics.fillDifference.toFixed( 3 ) } FGS4→5 ΔE { metrics.foregroundDifference.toFixed( 3 ) } @@ -367,6 +406,10 @@ function ScaleTable( { { methods.map( ( method ) => { const methodRamps = rampsByMethod.get( method )!; const ramp = methodRamps[ scaleName ]; + const anchorStep = + scaleName === 'background' + ? 'surface2' + : 'bgFill1'; return ( @@ -395,6 +438,23 @@ function ScaleTable( { } } title={ `${ step.name }: ${ color }` } > + { step.name === anchorStep ? ( + + Input seed + + ) : null } { color } ); @@ -566,6 +626,7 @@ export const Comparison: Story = {

Compare each complete neutral, brand, and error ramp by step. WCAG ratios remain hard gates in every approach. + Use the Approaches control to hide or show table rows.

{ methods.length === 0 ? ( diff --git a/packages/theme/src/color-ramps/stories/ramp-table.tsx b/packages/theme/src/color-ramps/stories/ramp-table.tsx index af6dc6f92ec3a3..67c9dba5fa1b87 100644 --- a/packages/theme/src/color-ramps/stories/ramp-table.tsx +++ b/packages/theme/src/color-ramps/stories/ramp-table.tsx @@ -1,13 +1,18 @@ import { forwardRef } from '@wordpress/element'; -import { toGamut } from 'colorjs.io/fn'; +import { ColorSpace, deltaEOK2, OKLab, sRGB, toGamut } from 'colorjs.io/fn'; import type { ThemeProviderColorRampName, ThemeProviderColorWarning, } from '../../theme-provider-color-warnings'; import colorTokenAliases from '../../prebuilt/ts/color-tokens'; -import { getColorString, getContrast } from '../lib/color-utils'; +import { getContrast } from '../lib/color-utils'; import type { Ramp } from '../lib/types'; +ColorSpace.register( sRGB ); +ColorSpace.register( OKLab ); + +const MEANINGFUL_SEED_DELTA_E = 0.002; + // TODO: show token groups better const RAMP_TOKENS_ORDER: { tokenName: keyof Ramp; abbr: string }[] = [ { tokenName: 'surface1', abbr: 'SF1' }, @@ -114,7 +119,7 @@ export function hasColorWarningForRamp( } function isSeedAdjusted( seed: string, generatedAnchor: string ) { - return getColorString( seed ) !== getColorString( generatedAnchor ); + return deltaEOK2( seed, generatedAnchor ) > MEANINGFUL_SEED_DELTA_E; } function getSeedLabelColor( seed: string ) { diff --git a/packages/theme/src/color-ramps/stories/test/perceptual-ramp-experiment.test.ts b/packages/theme/src/color-ramps/stories/test/perceptual-ramp-experiment.test.ts index 2bf8e6314db2f0..f1787d68397e3e 100644 --- a/packages/theme/src/color-ramps/stories/test/perceptual-ramp-experiment.test.ts +++ b/packages/theme/src/color-ramps/stories/test/perceptual-ramp-experiment.test.ts @@ -24,12 +24,20 @@ ColorSpace.register( OKLCH ); ColorSpace.register( OklchSrgb ); const SAMPLE_COMBINATIONS = [ - { - background: DEFAULT_SEED_COLORS.background, - primary: DEFAULT_SEED_COLORS.primary, - }, - { background: '#1e1e1e', primary: DEFAULT_SEED_COLORS.primary }, + { background: '#fcfcfc', primary: '#3858e9' }, + { background: '#1e1e1e', primary: '#3858e9' }, + { background: '#222524', primary: '#3858e9' }, { background: '#4f386e', primary: '#608010' }, + { background: '#f8f8f8', primary: '#3858e9' }, + { background: '#25292b', primary: '#3858e9' }, + { background: '#eaeeed', primary: '#007cba' }, + { background: '#3876a8', primary: '#437aa8' }, + { background: '#5b534d', primary: '#916745' }, + { background: '#413256', primary: '#a3b745' }, + { background: '#5f787f', primary: '#567958' }, + { background: '#3d4042', primary: '#cf4339' }, + { background: '#cc4541', primary: '#ad631e' }, + // Synthetic stress cases for the polarity boundary and high-chroma gamut edges. { background: '#777777', primary: '#d63638' }, { background: '#fcfcfc', primary: '#ffd700' }, { background: '#1e1e1e', primary: '#00ffff' }, @@ -116,6 +124,25 @@ describe( 'perceptual ramp experiment', () => { } ); + it( 'keeps supplied seeds at their anchors in the pinned-seed role hybrid', () => { + for ( const seeds of SAMPLE_COMBINATIONS ) { + const ramps = buildExperimentalThemeRamps( { + method: 'pinned-role-hybrid', + ...seeds, + } ); + + expect( + deltaEOK2( seeds.background, ramps.background.ramp.surface2 ) + ).toBeLessThanOrEqual( 0.002 ); + expect( + deltaEOK2( seeds.primary, ramps.primary.ramp.bgFill1 ) + ).toBeLessThanOrEqual( 0.002 ); + expect( + deltaEOK2( DEFAULT_SEED_COLORS.error, ramps.error.ramp.bgFill1 ) + ).toBeLessThanOrEqual( 0.002 ); + } + } ); + it.each( EXPERIMENTAL_RAMP_METHODS )( 'orders foreground and stroke strength and keeps active fills darker in the %s ramps', ( method ) => { @@ -189,20 +216,23 @@ describe( 'perceptual ramp experiment', () => { } } ); - it( 'gives the hybrid a measurable active-state difference', () => { - for ( const seeds of SAMPLE_COMBINATIONS ) { - const ramps = buildExperimentalThemeRamps( { - method: 'role-hybrid', - ...seeds, - } ); + it.each( [ 'role-hybrid', 'pinned-role-hybrid' ] as const )( + 'gives the %s a measurable active-state difference', + ( method ) => { + for ( const seeds of SAMPLE_COMBINATIONS ) { + const ramps = buildExperimentalThemeRamps( { + method, + ...seeds, + } ); - for ( const ramp of Object.values( ramps ) ) { - expect( - deltaEOK2( ramp.ramp.bgFill1, ramp.ramp.bgFill2 ) - ).toBeGreaterThanOrEqual( 0.045 ); + for ( const ramp of Object.values( ramps ) ) { + expect( + deltaEOK2( ramp.ramp.bgFill1, ramp.ramp.bgFill2 ) + ).toBeGreaterThanOrEqual( 0.045 ); + } } } - } ); + ); it.each( EXPERIMENTAL_RAMP_METHODS )( 'returns deterministic output for the %s method', diff --git a/storybook/stories/design-system/theme-example-application.story.tsx b/storybook/stories/design-system/theme-example-application.story.tsx index 86a4c83ff81f00..b71460cd15a9c9 100644 --- a/storybook/stories/design-system/theme-example-application.story.tsx +++ b/storybook/stories/design-system/theme-example-application.story.tsx @@ -35,7 +35,7 @@ const SIDEBAR_THEME_PRESETS = [ { id: 'ectoplasm', title: 'Ectoplasm', - colors: { primary: '#646c3e', background: '#4f386e' }, + colors: { primary: '#a3b745', background: '#413256' }, }, ] as const; From e629221d84933a398a4531245e68f2130ef2b476 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Wed, 2 Sep 2026 12:25:33 +0200 Subject: [PATCH 07/44] Theme: Compare representative token states --- .../stories/perceptual-ramp-pilot.module.css | 64 +++++- .../stories/perceptual-ramp-pilot.story.tsx | 199 ++++++++++++++---- 2 files changed, 212 insertions(+), 51 deletions(-) diff --git a/packages/theme/src/color-ramps/stories/perceptual-ramp-pilot.module.css b/packages/theme/src/color-ramps/stories/perceptual-ramp-pilot.module.css index 21424051d9b266..34ae68f60eea53 100644 --- a/packages/theme/src/color-ramps/stories/perceptual-ramp-pilot.module.css +++ b/packages/theme/src/color-ramps/stories/perceptual-ramp-pilot.module.css @@ -185,7 +185,7 @@ .component-grid { display: grid; - grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); + grid-template-columns: repeat(auto-fit, minmax(min(100%, 360px), 1fr)); gap: 12px; } @@ -204,24 +204,74 @@ color: var(--pilot-fg-weak); } +.state-list, +.state-sample { + display: grid; +} + +.state-list { + gap: 14px; +} + +.state-sample { + gap: 6px; +} + +.state-label { + color: var(--pilot-fg-weak); + font-size: 11px; + font-weight: 600; +} + .button-row { - gap: 8px; + gap: 6px; } -.button-row button { +.state-button { + display: inline-flex; + box-sizing: border-box; + align-items: center; + justify-content: center; + min-width: 58px; min-height: 32px; padding: 5px 9px; - border: 0; - border-radius: 4px; + border: 1px solid transparent; + border-radius: var(--wpds-border-radius-sm); font: inherit; - cursor: var(--wpds-cursor-control); + font-size: 11px; + font-weight: var(--wpds-typography-font-weight-emphasis); } -.button-row button:focus-visible { +.state-button[data-state="focus"] { outline: 2px solid var(--pilot-focus); outline-offset: 2px; } +.tabs-row { + display: flex; + flex-wrap: wrap; + align-items: stretch; + border-block-end: 1px solid var(--pilot-border); +} + +.tab-state { + display: inline-flex; + box-sizing: border-box; + align-items: center; + justify-content: center; + min-height: 40px; + margin-block-end: -1px; + padding: 8px 10px; + border-block-end: 2px solid transparent; + font-size: 11px; +} + +.tab-state[data-state="focus"] { + border-radius: var(--wpds-border-radius-sm); + outline: 2px solid var(--pilot-focus); + outline-offset: -4px; +} + .component-note { font-size: 11px; } diff --git a/packages/theme/src/color-ramps/stories/perceptual-ramp-pilot.story.tsx b/packages/theme/src/color-ramps/stories/perceptual-ramp-pilot.story.tsx index 4ee1eee4540d1b..ffd6de6fc256be 100644 --- a/packages/theme/src/color-ramps/stories/perceptual-ramp-pilot.story.tsx +++ b/packages/theme/src/color-ramps/stories/perceptual-ramp-pilot.story.tsx @@ -487,6 +487,53 @@ function ComponentComparison( { const neutral = ramps.background.ramp; const brand = ramps.primary.ramp; const error = ramps.error.ramp; + const buttonExamples = [ + { + label: 'Brand button', + restingBackground: brand.bgFill1, + activeBackground: brand.bgFill2, + foreground: brand.fgFill, + }, + { + label: 'Neutral button', + restingBackground: neutral.bgFillInverted1, + activeBackground: neutral.bgFillInverted2, + foreground: neutral.fgFillInverted, + }, + { + label: 'Error button', + restingBackground: error.bgFill1, + activeBackground: error.bgFill2, + foreground: error.fgFill, + }, + ] as const; + const tabExamples = [ + { + label: 'Resting', + state: 'resting', + foreground: neutral.fgSurface4, + }, + { + label: 'Hover', + state: 'hover', + foreground: neutral.fgSurface5, + }, + { + label: 'Focus', + state: 'focus', + foreground: neutral.fgSurface5, + }, + { + label: 'Active', + state: 'active', + foreground: neutral.fgSurface5, + }, + { + label: 'Disabled', + state: 'disabled', + foreground: neutral.fgSurface2, + }, + ] as const; return (
@@ -505,54 +552,118 @@ function ComponentComparison( {

Supporting text and hierarchy

-
- - -
-
- - +
+ { buttonExamples.map( ( example ) => ( +
+ + { example.label } + +
+ { [ + { + label: 'Resting', + state: 'resting', + background: + example.restingBackground, + foreground: + example.foreground, + }, + { + label: 'Hover', + state: 'hover', + background: + example.activeBackground, + foreground: + example.foreground, + }, + { + label: 'Focus', + state: 'focus', + background: + example.activeBackground, + foreground: + example.foreground, + }, + { + label: 'Active', + state: 'active', + background: + example.activeBackground, + foreground: + example.foreground, + }, + { + label: 'Disabled', + state: 'disabled', + background: + neutral.surface5, + foreground: + neutral.fgSurface2, + }, + ].map( ( state ) => ( + + { state.label } + + ) ) } +
+
+ ) ) } +
+ + Neutral tabs + +
+ { tabExamples.map( ( example ) => ( + + { example.label } + + ) ) } +
+
); } ) }

- { label } uses the same generated values as the tables above. + These static samples map the generated ramps for { label } to + the semantic tokens used by WordPress UI. Focus also includes + the generated focus stroke. Identical hover and active samples + mean those states use the same semantic token.

); @@ -612,7 +723,7 @@ function SeedCombination( { export const Comparison: Story = { args: { - approaches: [ ...EXPERIMENTAL_RAMP_METHODS ], + approaches: [ 'anchored', 'pinned-role-hybrid' ], }, render: ( { approaches } ) => { const methods = EXPERIMENTAL_RAMP_METHODS.filter( ( method ) => From 60979bf354a56e2c2615eadc36c99d49a7f7f885 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Wed, 2 Sep 2026 15:03:59 +0200 Subject: [PATCH 08/44] Theme: Add role-hybrid comparison row --- .../src/color-ramps/stories/perceptual-ramp-pilot.story.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/theme/src/color-ramps/stories/perceptual-ramp-pilot.story.tsx b/packages/theme/src/color-ramps/stories/perceptual-ramp-pilot.story.tsx index ffd6de6fc256be..0c2abccb376a97 100644 --- a/packages/theme/src/color-ramps/stories/perceptual-ramp-pilot.story.tsx +++ b/packages/theme/src/color-ramps/stories/perceptual-ramp-pilot.story.tsx @@ -723,7 +723,7 @@ function SeedCombination( { export const Comparison: Story = { args: { - approaches: [ 'anchored', 'pinned-role-hybrid' ], + approaches: [ 'anchored', 'role-hybrid', 'pinned-role-hybrid' ], }, render: ( { approaches } ) => { const methods = EXPERIMENTAL_RAMP_METHODS.filter( ( method ) => From 8d63eda95105782f185412260990b6625f5df505 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Wed, 2 Sep 2026 16:18:50 +0200 Subject: [PATCH 09/44] Theme: Add constrained perceptual ramp comparison --- .../stories/perceptual-ramp-experiment.ts | 294 ++++++- .../stories/perceptual-ramp-pilot.module.css | 8 +- .../stories/perceptual-ramp-pilot.story.tsx | 112 ++- .../test/perceptual-ramp-experiment.test.ts | 111 +++ .../stories/trunk-production-fixtures.ts | 806 ++++++++++++++++++ 5 files changed, 1300 insertions(+), 31 deletions(-) create mode 100644 packages/theme/src/color-ramps/stories/trunk-production-fixtures.ts diff --git a/packages/theme/src/color-ramps/stories/perceptual-ramp-experiment.ts b/packages/theme/src/color-ramps/stories/perceptual-ramp-experiment.ts index 89100697ecdb7f..7b3a2b74e9cbc3 100644 --- a/packages/theme/src/color-ramps/stories/perceptual-ramp-experiment.ts +++ b/packages/theme/src/color-ramps/stories/perceptual-ramp-experiment.ts @@ -6,6 +6,7 @@ import { OKLab, OKLCH, OKLCH_sRGB as OklchSrgb, + OKLrab, sRGB, to, type PlainColorObject, @@ -30,9 +31,11 @@ ColorSpace.register( sRGB ); ColorSpace.register( OKLab ); ColorSpace.register( OKLCH ); ColorSpace.register( OklchSrgb ); +ColorSpace.register( OKLrab ); export const EXPERIMENTAL_RAMP_METHODS = [ 'anchored', + 'constrained-perceptual', 'apca-all', 'role-hybrid', 'pinned-role-hybrid', @@ -143,6 +146,82 @@ function createColorAtLightness( } ); } +function getPerceptualLightness( color: string | PlainColorObject ) { + return get( color, [ OKLrab, 'l' ] ); +} + +function createColorAtPerceptualLightness( + seed: string | PlainColorObject, + mode: ChromaMode, + taperOptions?: Parameters< typeof taperChroma >[ 2 ] +): GetColorAtLightness { + const getColorAtLightness = createColorAtLightness( + seed, + mode, + taperOptions + ); + + return ( perceptualLightness ) => { + const oklab = to( + { + space: OKLrab, + coords: [ perceptualLightness, 0, 0 ], + alpha: 1, + }, + OKLab + ); + return getColorAtLightness( get( oklab, [ OKLab, 'l' ] ) ); + }; +} + +function createAnchoredColorForStepAtPerceptualLightness( { + ramp, + step, + anchor, + startLightness, + endLightness, +}: { + ramp: Record< keyof Ramp, string >; + step: keyof Ramp; + anchor: string; + startLightness: number; + endLightness: number; +} ) { + const stepColor = to( clampToGamut( ramp[ step ] ), OKLCH ); + const getTargetColor = createColorAtPerceptualLightness( + stepColor, + 'absolute' + ); + const anchorColor = to( clampToGamut( anchor ), OKLCH ); + const anchorChroma = get( anchorColor, [ OKLCH, 'c' ] ); + const stepChroma = get( stepColor, [ OKLCH, 'c' ] ); + const distance = endLightness - startLightness; + + return ( perceptualLightness: number ) => { + const targetColor = to( getTargetColor( perceptualLightness ), OKLCH ); + const progress = + distance === 0 + ? 1 + : Math.min( + 1, + Math.max( + 0, + ( perceptualLightness - startLightness ) / distance + ) + ); + + return clampToGamut( { + space: OKLCH, + coords: [ + get( targetColor, [ OKLCH, 'l' ] ), + anchorChroma + ( stepChroma - anchorChroma ) * progress, + get( stepColor, [ OKLCH, 'h' ] ), + ], + alpha: targetColor.alpha, + } ); + }; +} + function getClosestBoundary( target: number, first: { color: PlainColorObject; value: number }, @@ -338,6 +417,186 @@ function findColorMeetingWcag( { ); } +function rebuildConstrainedSurfaces( { ramp }: { ramp: RampResult } ) { + const nextRamp = { ...ramp.ramp }; + const surfaceReference = prepareMetricReference( + 'delta-e', + ramp.ramp.surface2 + ); + const elevationTarget = Math.max( + getMetric( 'delta-e', surfaceReference, ramp.ramp.surface1 ), + getMetric( 'delta-e', surfaceReference, ramp.ramp.surface3 ) + ); + const surface2Lightness = getPerceptualLightness( ramp.ramp.surface2 ); + + for ( const step of [ 'surface1', 'surface3' ] as const ) { + const endLightness = step === 'surface1' ? 0 : 1; + const getColorAtLightness = createColorAtPerceptualLightness( + ramp.ramp[ step ], + 'absolute' + ); + nextRamp[ step ] = getColorString( + findColorAtMetric( { + metric: 'delta-e', + reference: surfaceReference, + getColorAtLightness, + startLightness: surface2Lightness, + endLightness, + target: elevationTarget, + } ) + ); + } + + const surface6Difference = getMetric( + 'delta-e', + surfaceReference, + ramp.ramp.surface6 + ); + const surface6Lightness = getPerceptualLightness( ramp.ramp.surface6 ); + for ( const [ index, step ] of SURFACE_STEPS.entries() ) { + if ( step === 'surface6' ) { + continue; + } + const getColorAtLightness = createColorAtPerceptualLightness( + ramp.ramp[ step ], + 'absolute' + ); + const minimumDifference = getMetric( + 'delta-e', + surfaceReference, + getColorAtLightness( surface2Lightness ) + ); + nextRamp[ step ] = getColorString( + findColorAtMetric( { + metric: 'delta-e', + reference: surfaceReference, + getColorAtLightness, + startLightness: surface2Lightness, + endLightness: surface6Lightness, + target: + minimumDifference + + ( surface6Difference - minimumDifference ) * + SURFACE_PROGRESS[ index ], + } ) + ); + } + + return nextRamp; +} + +function rebuildConstrainedStrokes( { + ramp, + backgroundRamp, +}: { + ramp: RampResult; + backgroundRamp: RampResult; +} ) { + const nextRamp = { ...ramp.ramp }; + const references = getStrokeReferences( nextRamp, backgroundRamp ); + + // ST3 is the accessibility boundary. Preserve the production color whenever + // it meets every required 3:1 contrast pair. + nextRamp.stroke3 = findColorMeetingWcag( { + color: nextRamp.stroke3, + references, + target: SERIALIZED_STROKE_WCAG_TARGET, + seed: nextRamp.stroke3, + mode: 'absolute', + } ); + + const stroke1Reference = prepareMetricReference( + 'delta-e', + nextRamp.stroke1 + ); + const stroke1To3Difference = getMetric( + 'delta-e', + stroke1Reference, + nextRamp.stroke3 + ); + const authoredStroke2Difference = getMetric( + 'delta-e', + stroke1Reference, + ramp.ramp.stroke2 + ); + const stroke1Lightness = getPerceptualLightness( nextRamp.stroke1 ); + const stroke3Lightness = getPerceptualLightness( nextRamp.stroke3 ); + const getStroke2AtLightness = + createAnchoredColorForStepAtPerceptualLightness( { + ramp: nextRamp, + step: 'stroke2', + anchor: nextRamp.stroke1, + startLightness: stroke1Lightness, + endLightness: stroke3Lightness, + } ); + nextRamp.stroke2 = getColorString( + findColorAtMetric( { + metric: 'delta-e', + reference: stroke1Reference, + getColorAtLightness: getStroke2AtLightness, + startLightness: stroke1Lightness, + endLightness: stroke3Lightness, + target: Math.min( + authoredStroke2Difference, + stroke1To3Difference * 0.95 + ), + } ) + ); + const stroke3Contrast = Math.abs( + contrastAPCA( nextRamp.surface3, nextRamp.stroke3 ) + ); + const stroke4Contrast = Math.abs( + contrastAPCA( nextRamp.surface3, nextRamp.stroke4 ) + ); + if ( + stroke4Contrast >= stroke3Contrast && + references.every( + ( reference ) => + getContrast( reference, nextRamp.stroke4 ) >= + SERIALIZED_STROKE_WCAG_TARGET + ) + ) { + return nextRamp; + } + + const stroke3Reference = prepareMetricReference( + 'delta-e', + nextRamp.stroke3 + ); + const minimumActiveDifference = getMetric( + 'delta-e', + stroke3Reference, + ramp.ramp.stroke4 + ); + const stroke4EndLightness = ramp.direction === 'lighter' ? 1 : 0; + const getStroke4AtLightness = + createAnchoredColorForStepAtPerceptualLightness( { + ramp: nextRamp, + step: 'stroke4', + anchor: nextRamp.stroke3, + startLightness: stroke3Lightness, + endLightness: stroke4EndLightness, + } ); + const stroke4Candidate = getColorString( + findColorAtMetric( { + metric: 'delta-e', + reference: stroke3Reference, + getColorAtLightness: getStroke4AtLightness, + startLightness: stroke3Lightness, + endLightness: stroke4EndLightness, + target: minimumActiveDifference, + } ) + ); + nextRamp.stroke4 = findColorMeetingWcag( { + color: stroke4Candidate, + references, + target: SERIALIZED_STROKE_WCAG_TARGET, + seed: stroke4Candidate, + mode: 'absolute', + } ); + + return nextRamp; +} + function darkenUntilContrast( { color, reference, @@ -636,6 +895,39 @@ function rebuildRamp( { family: RampFamily; seed: string; } ) { + const config = family === 'neutral' ? BG_RAMP_CONFIG : ACCENT_RAMP_CONFIG; + + if ( method === 'constrained-perceptual' ) { + let nextRamp = rebuildConstrainedSurfaces( { + ramp, + } ); + const surfaceResult: RampResult = { + ...ramp, + ramp: nextRamp, + warnings: undefined, + }; + nextRamp = rebuildConstrainedStrokes( { + ramp: surfaceResult, + backgroundRamp, + } ); + nextRamp = rebuildActiveFill( { + ramp: nextRamp, + method, + seed, + mode: getRampChromaMode( method, family ), + } ); + + return buildForegroundScale( + { + ...ramp, + ramp: nextRamp, + warnings: undefined, + }, + backgroundRamp, + config.foregroundScale + ); + } + const metric: PerceptualMetric = method === 'apca-all' ? 'apca' : 'delta-e'; const mode = getRampChromaMode( method, family ); let nextRamp = rebuildElevationPair( { @@ -706,8 +998,6 @@ function rebuildRamp( { ramp: nextRamp, warnings: undefined, }; - const config = family === 'neutral' ? BG_RAMP_CONFIG : ACCENT_RAMP_CONFIG; - return buildForegroundScale( intermediateResult, backgroundRamp, diff --git a/packages/theme/src/color-ramps/stories/perceptual-ramp-pilot.module.css b/packages/theme/src/color-ramps/stories/perceptual-ramp-pilot.module.css index 34ae68f60eea53..7fd8ed4273ff7e 100644 --- a/packages/theme/src/color-ramps/stories/perceptual-ramp-pilot.module.css +++ b/packages/theme/src/color-ramps/stories/perceptual-ramp-pilot.module.css @@ -172,7 +172,8 @@ color: #1d5f1d; } -.seed-anchor { +.seed-anchor, +.unavailable-step { position: absolute; inset-block-start: 0; inset-inline: 0; @@ -183,6 +184,11 @@ font-weight: 600; } +.unavailable-step { + background: rgb(0 0 0 / 72%); + color: #fff; +} + .component-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 360px), 1fr)); diff --git a/packages/theme/src/color-ramps/stories/perceptual-ramp-pilot.story.tsx b/packages/theme/src/color-ramps/stories/perceptual-ramp-pilot.story.tsx index 0c2abccb376a97..b25f50c25679af 100644 --- a/packages/theme/src/color-ramps/stories/perceptual-ramp-pilot.story.tsx +++ b/packages/theme/src/color-ramps/stories/perceptual-ramp-pilot.story.tsx @@ -9,10 +9,20 @@ import { EXPERIMENTAL_RAMP_METHODS, buildExperimentalThemeRamps, getExperimentalChroma, - type ExperimentalRampMethod, type ExperimentalThemeRamps, } from './perceptual-ramp-experiment'; import styles from './perceptual-ramp-pilot.module.css'; +import { + TRUNK_PRODUCTION_COMMIT, + getTrunkProductionThemeRamps, +} from './trunk-production-fixtures'; + +const COMPARISON_RAMP_METHODS = [ + 'trunk-production', + ...EXPERIMENTAL_RAMP_METHODS, +] as const; + +type ComparisonRampMethod = ( typeof COMPARISON_RAMP_METHODS )[ number ]; const SAMPLE_COMBINATIONS = [ { @@ -63,14 +73,26 @@ const SAMPLE_COMBINATIONS = [ ] as const; const METHOD_DETAILS: Record< - ExperimentalRampMethod, + ComparisonRampMethod, { label: string; description: string } > = { + 'trunk-production': { + label: 'Trunk production', + description: `Exact fixed-sample output from trunk at ${ TRUNK_PRODUCTION_COMMIT.slice( + 0, + 11 + ) }. Trunk has four foreground steps.`, + }, anchored: { - label: 'Anchored production candidate', + label: 'Current production candidate', description: 'Current draft algorithm. APCA shapes the five foreground steps and WCAG ratios shape the other lanes.', }, + 'constrained-perceptual': { + label: 'Constrained perceptual surfaces and strokes', + description: + 'Uses Oklr lightness and OKLab color difference to space surfaces and strokes while retaining the production chroma policy and WCAG gates.', + }, 'apca-all': { label: 'APCA for every lane', description: @@ -120,7 +142,7 @@ const SCALE_DETAILS = [ ] as const; type PilotArgs = { - approaches: ExperimentalRampMethod[]; + approaches: ComparisonRampMethod[]; }; const meta = { @@ -134,14 +156,14 @@ const meta = { control: { type: 'inline-check', labels: Object.fromEntries( - EXPERIMENTAL_RAMP_METHODS.map( ( method ) => [ + COMPARISON_RAMP_METHODS.map( ( method ) => [ method, METHOD_DETAILS[ method ].label, ] ) ), }, description: 'Approaches shown in each comparison table.', - options: EXPERIMENTAL_RAMP_METHODS, + options: COMPARISON_RAMP_METHODS, }, }, } satisfies Meta< PilotArgs >; @@ -154,7 +176,7 @@ type ScaleName = ( typeof SCALE_DETAILS )[ number ][ 'name' ]; const rampCache = new Map< string, ExperimentalThemeRamps >(); function getRamps( - method: ExperimentalRampMethod, + method: ComparisonRampMethod, background: string, primary: string ) { @@ -164,12 +186,15 @@ function getRamps( return cached; } - const ramps = buildExperimentalThemeRamps( { - method, - background, - primary, - error: DEFAULT_SEED_COLORS.error, - } ); + const ramps = + method === 'trunk-production' + ? getTrunkProductionThemeRamps( background, primary ) + : buildExperimentalThemeRamps( { + method, + background, + primary, + error: DEFAULT_SEED_COLORS.error, + } ); rampCache.set( key, ramps ); return ramps; } @@ -273,7 +298,7 @@ function MethodSummary( { isAccent, seed, }: { - method: ExperimentalRampMethod; + method: ComparisonRampMethod; ramp: RampResult; backgroundRamp: RampResult; isAccent: boolean; @@ -308,10 +333,19 @@ function MethodSummary( { { ramp.direction === 'lighter' ? 'light' : 'dark' }
Fill ΔE { metrics.fillDifference.toFixed( 3 ) } - FGS4→5 ΔE { metrics.foregroundDifference.toFixed( 3 ) } - - FGS4→5 ΔLc { metrics.foregroundApcaInterval.toFixed( 1 ) } - + { method === 'trunk-production' ? ( + FGS5 not present on trunk + ) : ( + <> + + FGS4→5 ΔE { metrics.foregroundDifference.toFixed( 3 ) } + + + FGS4→5 ΔLc{ ' ' } + { metrics.foregroundApcaInterval.toFixed( 1 ) } + + + ) } { metrics.chromaDrift === undefined ? null : ( Mean relative chroma drift{ ' ' } @@ -332,8 +366,8 @@ function ScaleTable( { label: string; seed: string; scaleName: ScaleName; - methods: ExperimentalRampMethod[]; - rampsByMethod: Map< ExperimentalRampMethod, ExperimentalThemeRamps >; + methods: ComparisonRampMethod[]; + rampsByMethod: Map< ComparisonRampMethod, ExperimentalThemeRamps >; } ) { const groups = Array.from( new Set( RAMP_STEPS.map( ( step ) => step.group ) ) @@ -427,6 +461,9 @@ function ScaleTable( { { RAMP_STEPS.map( ( step ) => { const color = ramp.ramp[ step.name ]; + const unavailableOnTrunk = + method === 'trunk-production' && + step.name === 'fgSurface5'; return ( + { unavailableOnTrunk ? ( + + Not on trunk + + ) : null } { step.name === anchorStep ? ( ; + methods: ComparisonRampMethod[]; + rampsByMethod: Map< ComparisonRampMethod, ExperimentalThemeRamps >; label: string; } ) { return ( @@ -678,7 +726,7 @@ function SeedCombination( { label: string; background: string; primary: string; - methods: ExperimentalRampMethod[]; + methods: ComparisonRampMethod[]; } ) { const rampsByMethod = new Map( methods.map( ( method ) => [ @@ -723,10 +771,14 @@ function SeedCombination( { export const Comparison: Story = { args: { - approaches: [ 'anchored', 'role-hybrid', 'pinned-role-hybrid' ], + approaches: [ + 'trunk-production', + 'anchored', + 'constrained-perceptual', + ], }, render: ( { approaches } ) => { - const methods = EXPERIMENTAL_RAMP_METHODS.filter( ( method ) => + const methods = COMPARISON_RAMP_METHODS.filter( ( method ) => approaches.includes( method ) ); @@ -736,8 +788,12 @@ export const Comparison: Story = {

Perceptual ramp comparison

Compare each complete neutral, brand, and error ramp by - step. WCAG ratios remain hard gates in every approach. - Use the Approaches control to hide or show table rows. + step. The default rows show trunk at{ ' ' } + { TRUNK_PRODUCTION_COMMIT.slice( 0, 11 ) }, the current + branch, and the new constrained-perceptual experiment. + WCAG ratios remain hard gates for the current and + experimental algorithms. Use the Approaches control to + show older experiments.

{ methods.length === 0 ? ( @@ -756,7 +812,7 @@ export const Comparison: Story = {
Approach definitions
- { EXPERIMENTAL_RAMP_METHODS.map( ( method ) => ( + { COMPARISON_RAMP_METHODS.map( ( method ) => (
{ METHOD_DETAILS[ method ].label }
diff --git a/packages/theme/src/color-ramps/stories/test/perceptual-ramp-experiment.test.ts b/packages/theme/src/color-ramps/stories/test/perceptual-ramp-experiment.test.ts index f1787d68397e3e..d4af966db2dd44 100644 --- a/packages/theme/src/color-ramps/stories/test/perceptual-ramp-experiment.test.ts +++ b/packages/theme/src/color-ramps/stories/test/perceptual-ramp-experiment.test.ts @@ -7,6 +7,7 @@ import { OKLab, OKLCH, OKLCH_sRGB as OklchSrgb, + OKLrab, sRGB, } from 'colorjs.io/fn'; import { describe, expect, it } from 'vitest'; @@ -17,11 +18,16 @@ import { EXPERIMENTAL_RAMP_METHODS, buildExperimentalThemeRamps, } from '../perceptual-ramp-experiment'; +import { + TRUNK_PRODUCTION_COMMIT, + getTrunkProductionThemeRamps, +} from '../trunk-production-fixtures'; ColorSpace.register( sRGB ); ColorSpace.register( OKLab ); ColorSpace.register( OKLCH ); ColorSpace.register( OklchSrgb ); +ColorSpace.register( OKLrab ); const SAMPLE_COMBINATIONS = [ { background: '#fcfcfc', primary: '#3858e9' }, @@ -62,6 +68,44 @@ function getForegroundContrasts( } describe( 'perceptual ramp experiment', () => { + it( 'exposes the exact pinned trunk ramps for every Storybook seed pair', () => { + for ( const seeds of [ + { background: '#fcfcfc', primary: '#3858e9' }, + { background: '#1e1e1e', primary: '#3858e9' }, + { background: '#413256', primary: '#a3b745' }, + { background: '#3876a8', primary: '#437aa8' }, + { background: '#5f787f', primary: '#567958' }, + { background: '#cc4541', primary: '#ad631e' }, + { background: '#777777', primary: '#d63638' }, + { background: '#fcfcfc', primary: '#ffd700' }, + { background: '#1e1e1e', primary: '#00ffff' }, + ] as const ) { + const ramps = getTrunkProductionThemeRamps( + seeds.background, + seeds.primary + ); + + for ( const ramp of Object.values( ramps ) ) { + expect( Object.keys( ramp.ramp ) ).toHaveLength( 23 ); + expect( ramp.ramp.fgSurface5 ).toBe( ramp.ramp.fgSurface4 ); + } + } + + expect( TRUNK_PRODUCTION_COMMIT ).toBe( + '0b466a172502fa0d103ad4102dd5f24e22175049' + ); + expect( + getTrunkProductionThemeRamps( '#fcfcfc', '#3858e9' ).background.ramp + ).toMatchObject( { + surface2: '#fcfcfc', + stroke3: '#8d8d8d', + fgSurface4: '#1e1e1e', + } ); + expect( () => + getTrunkProductionThemeRamps( '#123456', '#abcdef' ) + ).toThrow( 'No trunk production fixture exists' ); + } ); + it.each( EXPERIMENTAL_RAMP_METHODS )( 'builds complete %s ramps for every representative seed pair', ( method ) => { @@ -81,6 +125,73 @@ describe( 'perceptual ramp experiment', () => { } ); + it( 'keeps constrained surfaces ordered and perceptually progressive', () => { + for ( const seeds of SAMPLE_COMBINATIONS ) { + const ramps = buildExperimentalThemeRamps( { + method: 'constrained-perceptual', + ...seeds, + } ); + + for ( const ramp of Object.values( ramps ) ) { + const lightness = ( step: keyof typeof ramp.ramp ) => + get( ramp.ramp[ step ], [ OKLrab, 'l' ] ); + expect( lightness( 'surface1' ) ).toBeLessThan( + lightness( 'surface2' ) + ); + expect( lightness( 'surface3' ) ).toBeGreaterThan( + lightness( 'surface2' ) + ); + + const emphasisLightness = [ + lightness( 'surface2' ), + lightness( 'surface4' ), + lightness( 'surface5' ), + lightness( 'surface6' ), + ]; + const orderedLightness = [ ...emphasisLightness ].sort( + ramp.direction === 'lighter' + ? ( first, second ) => first - second + : ( first, second ) => second - first + ); + expect( emphasisLightness ).toEqual( orderedLightness ); + + const distances = [ 'surface4', 'surface5', 'surface6' ].map( + ( step ) => + deltaEOK2( + ramp.ramp.surface2, + ramp.ramp[ step as keyof typeof ramp.ramp ] + ) + ); + expect( distances[ 0 ] ).toBeLessThan( distances[ 1 ] ); + expect( distances[ 1 ] ).toBeLessThan( distances[ 2 ] ); + } + } + } ); + + it( 'keeps constrained ST3 accessible and ST4 perceptually stronger', () => { + for ( const seeds of SAMPLE_COMBINATIONS ) { + const ramps = buildExperimentalThemeRamps( { + method: 'constrained-perceptual', + ...seeds, + } ); + + for ( const ramp of Object.values( ramps ) ) { + const stroke3Contrast = Math.abs( + contrastAPCA( ramp.ramp.surface3, ramp.ramp.stroke3 ) + ); + const stroke4Contrast = Math.abs( + contrastAPCA( ramp.ramp.surface3, ramp.ramp.stroke4 ) + ); + expect( stroke4Contrast ).toBeGreaterThanOrEqual( + stroke3Contrast + ); + expect( + getContrast( ramp.ramp.surface3, ramp.ramp.stroke3 ) + ).toBeGreaterThanOrEqual( 3 ); + } + } + } ); + it.each( EXPERIMENTAL_RAMP_METHODS )( 'keeps every configured WCAG pair accessible in the %s ramps', ( method ) => { diff --git a/packages/theme/src/color-ramps/stories/trunk-production-fixtures.ts b/packages/theme/src/color-ramps/stories/trunk-production-fixtures.ts new file mode 100644 index 00000000000000..9c7bbc312e1f81 --- /dev/null +++ b/packages/theme/src/color-ramps/stories/trunk-production-fixtures.ts @@ -0,0 +1,806 @@ +import type { RampResult } from '../lib/types'; + +export const TRUNK_PRODUCTION_COMMIT = + '0b466a172502fa0d103ad4102dd5f24e22175049'; + +export type TrunkProductionThemeRamps = { + background: RampResult; + primary: RampResult; + error: RampResult; +}; + +const TRUNK_PRODUCTION_FIXTURES: Record< string, TrunkProductionThemeRamps > = { + '#fcfcfc|#3858e9': { + background: { + ramp: { + surface2: '#fcfcfc', + bgFill1: '#555', + fgFill: '#f0f0f0', + bgFill2: '#484848', + surface6: '#d6d6d6', + surface5: '#e6e6e6', + surface4: '#ededed', + surface3: '#fff', + fgSurface4: '#1e1e1e', + fgSurface3: '#707070', + fgSurface2: '#8d8d8d', + fgSurface1: '#aeaeae', + stroke3: '#8d8d8d', + stroke4: '#6e6e6e', + stroke2: '#dbdbdb', + stroke1: '#f0f0f0', + bgFillDark: '#1e1e1e', + fgFillDark: '#f0f0f0', + bgFillInverted2: '#1e1e1e', + bgFillInverted1: '#2d2d2d', + fgFillInverted: '#f0f0f0', + surface1: '#f4f4f4', + fgSurface5: '#1e1e1e', + }, + direction: 'darker', + }, + primary: { + ramp: { + bgFill1: '#3858e9', + fgFill: '#eff0f2', + bgFill2: '#2e49d9', + surface2: '#f6f8fe', + surface6: '#c7d2ee', + surface5: '#dce3f4', + surface4: '#e6eaf4', + surface3: '#fff', + fgSurface4: '#0b0070', + fgSurface3: '#3858e9', + fgSurface2: '#5a82ff', + fgSurface1: '#86a9ff', + stroke3: '#3858e9', + stroke4: '#2337c8', + stroke2: '#9caacc', + stroke1: '#b0bbd6', + bgFillDark: '#1a1e27', + fgFillDark: '#eff0f2', + bgFillInverted2: '#1a1e27', + bgFillInverted1: '#13009f', + fgFillInverted: '#eff0f2', + surface1: '#ecf0fa', + fgSurface5: '#0b0070', + }, + direction: 'darker', + }, + error: { + ramp: { + bgFill1: '#cc1818', + fgFill: '#f2efef', + bgFill2: '#b90000', + surface2: '#fff6f5', + surface6: '#f3c9c2', + surface5: '#f7dbd7', + surface4: '#f6e6e3', + surface3: '#fff', + fgSurface4: '#470000', + fgSurface3: '#cc1818', + fgSurface2: '#f74c40', + fgSurface1: '#ff8879', + stroke3: '#cc1818', + stroke4: '#9d0000', + stroke2: '#d39c95', + stroke1: '#dab1aa', + bgFillDark: '#231c1b', + fgFillDark: '#f2efef', + bgFillInverted2: '#231c1b', + bgFillInverted1: '#640000', + fgFillInverted: '#f2efef', + surface1: '#fcece9', + fgSurface5: '#470000', + }, + direction: 'darker', + }, + }, + '#1e1e1e|#3858e9': { + background: { + ramp: { + surface2: '#1e1e1e', + bgFill1: '#959595', + fgFill: '#1e1e1e', + bgFill2: '#a5a5a5', + surface6: '#383838', + surface5: '#2d2d2d', + surface4: '#282828', + surface3: '#242424', + fgSurface4: '#f0f0f0', + fgSurface3: '#b4b4b4', + fgSurface2: '#6e6e6e', + fgSurface1: '#535353', + stroke3: '#6e6e6e', + stroke4: '#8c8c8c', + stroke2: '#343434', + stroke1: '#272727', + bgFillDark: '#000', + fgFillDark: '#f0f0f0', + bgFillInverted2: '#f0f0f0', + bgFillInverted1: '#dbdbdb', + fgFillInverted: '#1e1e1e', + surface1: '#171717', + fgSurface5: '#f0f0f0', + }, + direction: 'lighter', + }, + primary: { + ramp: { + bgFill1: '#3858e9', + fgFill: '#eff0f2', + bgFill2: '#4367f9', + surface2: '#0b174b', + surface6: '#1d307d', + surface5: '#162668', + surface4: '#122062', + surface3: '#111e52', + fgSurface4: '#e4f0ff', + fgSurface3: '#8eb0ff', + fgSurface2: '#3c5eef', + fgSurface1: '#273fcf', + stroke3: '#8eb0ff', + stroke4: '#c6daff', + stroke2: '#506ab4', + stroke1: '#435ca9', + bgFillDark: '#000', + fgFillDark: '#eff0f2', + bgFillInverted2: '#eff0f2', + bgFillInverted1: '#c8dcff', + fgFillInverted: '#1a1e27', + surface1: '#07113a', + fgSurface5: '#e4f0ff', + }, + direction: 'lighter', + }, + error: { + ramp: { + bgFill1: '#cc1818', + fgFill: '#f2efef', + bgFill2: '#dd3029', + surface2: '#36100c', + surface6: '#5e241f', + surface5: '#4e1d18', + surface4: '#481713', + surface3: '#3e1713', + fgSurface4: '#ffe6e0', + fgSurface3: '#ff8879', + fgSurface2: '#d32420', + fgSurface1: '#a00', + stroke3: '#ff8879', + stroke4: '#ffc3b9', + stroke2: '#9f5148', + stroke1: '#8c473f', + bgFillDark: '#000', + fgFillDark: '#f2efef', + bgFillInverted2: '#f2efef', + bgFillInverted1: '#ffd0c7', + fgFillInverted: '#231c1b', + surface1: '#290b08', + fgSurface5: '#ffe6e0', + }, + direction: 'lighter', + }, + }, + '#413256|#a3b745': { + background: { + ramp: { + surface2: '#413256', + bgFill1: '#a191bb', + fgFill: '#201c27', + bgFill2: '#b2a2cd', + surface6: '#5c447b', + surface5: '#513b6f', + surface4: '#4d376a', + surface3: '#483562', + fgSurface4: '#f0f0f1', + fgSurface3: '#b5b3ba', + fgSurface2: '#8a8691', + fgSurface1: '#6e6977', + stroke3: '#9181aa', + stroke4: '#b0a5c2', + stroke2: '#544569', + stroke1: '#48395c', + bgFillDark: '#000', + fgFillDark: '#f0f0f1', + bgFillInverted2: '#f0f0f1', + bgFillInverted1: '#e4d3ff', + fgFillInverted: '#201c27', + surface1: '#3e2b56', + fgSurface5: '#f0f0f1', + }, + direction: 'lighter', + }, + primary: { + ramp: { + bgFill1: '#a3b745', + fgFill: '#1e1e1b', + bgFill2: '#b5c959', + surface2: '#383e21', + surface6: '#4d552e', + surface5: '#444b28', + surface4: '#404724', + surface3: '#3d4326', + fgSurface4: '#e7fd8d', + fgSurface3: '#a3b745', + fgSurface2: '#819316', + fgSurface1: '#657500', + stroke3: '#a3b745', + stroke4: '#cadf6f', + stroke2: '#666e4b', + stroke1: '#5a613f', + bgFillDark: '#000', + fgFillDark: '#eef3e1', + bgFillInverted2: '#eef3e1', + bgFillInverted1: '#d0e575', + fgFillInverted: '#1e1e1b', + surface1: '#33391c', + fgSurface5: '#e7fd8d', + }, + direction: 'lighter', + }, + error: { + ramp: { + bgFill1: '#cc1818', + fgFill: '#f2efef', + bgFill2: '#dd3029', + surface2: '#602620', + surface6: '#87332b', + surface5: '#752e27', + surface4: '#702a23', + surface3: '#662b25', + fgSurface4: '#ffe6e0', + fgSurface3: '#ff8879', + fgSurface2: '#f4493e', + fgSurface1: '#d01f1d', + stroke3: '#ff8879', + stroke4: '#ffc3b9', + stroke2: '#9f5148', + stroke1: '#8c473f', + bgFillDark: '#000', + fgFillDark: '#f2efef', + bgFillInverted2: '#f2efef', + bgFillInverted1: '#ffd0c7', + fgFillInverted: '#231c1b', + surface1: '#5a201b', + fgSurface5: '#ffe6e0', + }, + direction: 'lighter', + }, + }, + '#3876a8|#437aa8': { + background: { + ramp: { + surface2: '#165888', + bgFill1: '#84c3f9', + fgFill: '#1b1f22', + bgFill2: '#9bd8ff', + surface6: '#386f9d', + surface5: '#2e6591', + surface4: '#326086', + surface3: '#2e5d83', + fgSurface4: '#fff', + fgSurface3: '#cbd0d4', + fgSurface2: '#a3abb3', + fgSurface1: '#808b95', + stroke3: '#8daecc', + stroke4: '#c0d4e5', + stroke2: '#476b8a', + stroke1: '#405e78', + bgFillDark: '#000', + fgFillDark: '#eef0f2', + bgFillInverted2: '#fff', + bgFillInverted1: '#cbeeff', + fgFillInverted: '#1b1f22', + surface1: '#245278', + fgSurface5: '#fff', + }, + direction: 'lighter', + }, + primary: { + ramp: { + bgFill1: '#437aa8', + fgFill: '#fdfeff', + bgFill2: '#5189b8', + surface2: '#2a4b67', + surface6: '#386285', + surface5: '#355876', + surface4: '#315371', + surface3: '#2f506c', + fgSurface4: '#d8f4ff', + fgSurface3: '#8ac3f5', + fgSurface2: '#689fd0', + fgSurface1: '#4980ae', + stroke3: '#8ac3f5', + stroke4: '#c9edff', + stroke2: '#527a9d', + stroke1: '#4e6c87', + bgFillDark: '#000', + fgFillDark: '#eef0f2', + bgFillInverted2: '#eef0f2', + bgFillInverted1: '#b0e1ff', + fgFillInverted: '#1b1e21', + surface1: '#254661', + fgSurface5: '#d8f4ff', + }, + direction: 'lighter', + }, + error: { + ramp: { + bgFill1: '#cc1818', + fgFill: '#f2efef', + bgFill2: '#dd3029', + surface2: '#752e27', + surface6: '#993d34', + surface5: '#8a362e', + surface4: '#85312a', + surface3: '#7a332c', + fgSurface4: '#ffe6e0', + fgSurface3: '#ff9f91', + fgSurface2: '#ff6153', + fgSurface1: '#e1342d', + stroke3: '#ff9f91', + stroke4: '#ffd9d1', + stroke2: '#ab5c52', + stroke1: '#985249', + bgFillDark: '#000', + fgFillDark: '#f2efef', + bgFillInverted2: '#f2efef', + bgFillInverted1: '#ffd0c7', + fgFillInverted: '#231c1b', + surface1: '#6f2922', + fgSurface5: '#ffe6e0', + }, + direction: 'lighter', + }, + }, + '#5f787f|#567958': { + background: { + ramp: { + surface2: '#40585f', + bgFill1: '#a7c1c9', + fgFill: '#1c1e1f', + bgFill2: '#b9d5dc', + surface6: '#527078', + surface5: '#47666e', + surface4: '#456168', + surface3: '#425d65', + fgSurface4: '#fff', + fgSurface3: '#c8d1d4', + fgSurface2: '#a3acae', + fgSurface1: '#838b8d', + stroke3: '#8cb0ba', + stroke4: '#b3d7e2', + stroke2: '#566b71', + stroke1: '#4c5e63', + bgFillDark: '#000', + fgFillDark: '#edf1f2', + bgFillInverted2: '#fff', + bgFillInverted1: '#d2edf5', + fgFillInverted: '#1c1e1f', + surface1: '#39535a', + fgSurface5: '#fff', + }, + direction: 'lighter', + }, + primary: { + ramp: { + bgFill1: '#567958', + fgFill: '#f2f7f2', + bgFill2: '#648766', + surface2: '#354f37', + surface6: '#456848', + surface5: '#3e5d40', + surface4: '#3a593c', + surface3: '#3a543b', + fgSurface4: '#d5fdd7', + fgSurface3: '#a3c8a4', + fgSurface2: '#80a582', + fgSurface1: '#628664', + stroke3: '#a3c8a4', + stroke4: '#ccf3ce', + stroke2: '#627e63', + stroke1: '#577158', + bgFillDark: '#000', + fgFillDark: '#edf2ed', + bgFillInverted2: '#edf2ed', + bgFillInverted1: '#bee5c0', + fgFillInverted: '#1c1f1c', + surface1: '#304a32', + fgSurface5: '#d5fdd7', + }, + direction: 'lighter', + }, + error: { + ramp: { + bgFill1: '#cc1818', + fgFill: '#f2efef', + bgFill2: '#dd3029', + surface2: '#752e27', + surface6: '#993d34', + surface5: '#8a362e', + surface4: '#85312a', + surface3: '#7a332c', + fgSurface4: '#ffe6e0', + fgSurface3: '#ff9f91', + fgSurface2: '#ff6153', + fgSurface1: '#e1342d', + stroke3: '#ff9f91', + stroke4: '#ffd9d1', + stroke2: '#ab5c52', + stroke1: '#985249', + bgFillDark: '#000', + fgFillDark: '#f2efef', + bgFillInverted2: '#f2efef', + bgFillInverted1: '#ffd0c7', + fgFillInverted: '#231c1b', + surface1: '#6f2922', + fgSurface5: '#ffe6e0', + }, + direction: 'lighter', + }, + }, + '#cc4541|#ad631e': { + background: { + ramp: { + surface2: '#a51b20', + bgFill1: '#ffa69c', + fgFill: '#231c1b', + bgFill2: '#ffc2ba', + surface6: '#b54842', + surface5: '#a5403c', + surface4: '#a03b36', + surface3: '#9c3633', + fgSurface4: '#fff', + fgSurface3: '#d6cecd', + fgSurface2: '#b5a7a5', + fgSurface1: '#9f8380', + stroke3: '#d39d97', + stroke4: '#eacac6', + stroke2: '#9e514b', + stroke1: '#8b4742', + bgFillDark: '#000', + fgFillDark: '#f2efef', + bgFillInverted2: '#fff', + bgFillInverted1: '#ffe3de', + fgFillInverted: '#231c1b', + surface1: '#8c302c', + fgSurface5: '#fff', + }, + direction: 'lighter', + }, + primary: { + ramp: { + bgFill1: '#ad631e', + fgFill: '#fffdfc', + bgFill2: '#bd7230', + surface2: '#613f24', + surface6: '#7e5331', + surface5: '#714a2b', + surface4: '#6d4627', + surface3: '#664429', + fgSurface4: '#ffe8cd', + fgSurface3: '#f9a96a', + fgSurface2: '#d38646', + fgSurface1: '#b26824', + stroke3: '#f9a96a', + stroke4: '#ffdfbe', + stroke2: '#946c4e', + stroke1: '#826148', + bgFillDark: '#000', + fgFillDark: '#f3efed', + bgFillInverted2: '#f3efed', + bgFillInverted1: '#ffd3a7', + fgFillInverted: '#211d1b', + surface1: '#5b3a1f', + fgSurface5: '#ffe8cd', + }, + direction: 'lighter', + }, + error: { + ramp: { + bgFill1: '#cc1818', + fgFill: '#f2efef', + bgFill2: '#dd3029', + surface2: '#752e27', + surface6: '#993d34', + surface5: '#8a362e', + surface4: '#85312a', + surface3: '#7a332c', + fgSurface4: '#ffe6e0', + fgSurface3: '#ff9f91', + fgSurface2: '#ff6153', + fgSurface1: '#e1342d', + stroke3: '#ff9f91', + stroke4: '#ffd9d1', + stroke2: '#ab5c52', + stroke1: '#985249', + bgFillDark: '#000', + fgFillDark: '#f2efef', + bgFillInverted2: '#f2efef', + bgFillInverted1: '#ffd0c7', + fgFillInverted: '#231c1b', + surface1: '#6f2922', + fgSurface5: '#ffe6e0', + }, + direction: 'lighter', + }, + }, + '#777777|#d63638': { + background: { + ramp: { + surface2: '#9b9b9b', + bgFill1: '#3b3b3b', + fgFill: '#f0f0f0', + bgFill2: '#2e2e2e', + surface6: '#808080', + surface5: '#8c8c8c', + surface4: '#919191', + surface3: '#a2a2a2', + fgSurface4: '#000', + fgSurface3: '#2e2e2e', + fgSurface2: '#494949', + fgSurface1: '#636363', + stroke3: '#494949', + stroke4: '#2d2d2d', + stroke2: '#848484', + stroke1: '#929292', + bgFillDark: '#000', + fgFillDark: '#f0f0f0', + bgFillInverted2: '#000', + bgFillInverted1: '#1b1b1b', + fgFillInverted: '#f0f0f0', + surface1: '#959595', + fgSurface5: '#000', + }, + direction: 'darker', + }, + primary: { + ramp: { + bgFill1: '#d63638', + fgFill: '#fbfafa', + bgFill2: '#c5222a', + surface2: '#de9a93', + surface6: '#db6d66', + surface5: '#e67c75', + surface4: '#ec827a', + surface3: '#e5a09a', + fgSurface4: '#3d0000', + fgSurface3: '#7d0002', + fgSurface2: '#ae0014', + fgSurface1: '#d13034', + stroke3: '#7d0002', + stroke4: '#410000', + stroke2: '#ab5f59', + stroke1: '#bc6b65', + bgFillDark: '#1e1818', + fgFillDark: '#f2efef', + bgFillInverted2: '#1e1818', + bgFillInverted1: '#5d0000', + fgFillInverted: '#f2efef', + surface1: '#d7938d', + fgSurface5: '#3d0000', + }, + direction: 'darker', + }, + error: { + ramp: { + bgFill1: '#cc1818', + fgFill: '#f2efef', + bgFill2: '#b90000', + surface2: '#e0998f', + surface6: '#df6b5e', + surface5: '#ea7a6d', + surface4: '#f08072', + surface3: '#e89f95', + fgSurface4: '#3d0000', + fgSurface3: '#7d0000', + fgSurface2: '#ae0000', + fgSurface1: '#d62723', + stroke3: '#7d0000', + stroke4: '#410000', + stroke2: '#ad5e54', + stroke1: '#c06a5f', + bgFillDark: '#1e1817', + fgFillDark: '#f2efef', + bgFillInverted2: '#1e1817', + bgFillInverted1: '#5d0000', + fgFillInverted: '#f2efef', + surface1: '#d99288', + fgSurface5: '#3d0000', + }, + direction: 'darker', + }, + }, + '#fcfcfc|#ffd700': { + background: { + ramp: { + surface2: '#fcfcfc', + bgFill1: '#555', + fgFill: '#f0f0f0', + bgFill2: '#484848', + surface6: '#d6d6d6', + surface5: '#e6e6e6', + surface4: '#ededed', + surface3: '#fff', + fgSurface4: '#1e1e1e', + fgSurface3: '#707070', + fgSurface2: '#8d8d8d', + fgSurface1: '#aeaeae', + stroke3: '#8d8d8d', + stroke4: '#6e6e6e', + stroke2: '#dbdbdb', + stroke1: '#f0f0f0', + bgFillDark: '#1e1e1e', + fgFillDark: '#f0f0f0', + bgFillInverted2: '#1e1e1e', + bgFillInverted1: '#2d2d2d', + fgFillInverted: '#f0f0f0', + surface1: '#f4f4f4', + fgSurface5: '#1e1e1e', + }, + direction: 'darker', + }, + primary: { + ramp: { + bgFill1: '#ffd700', + fgFill: '#1f1e1b', + bgFill2: '#eac300', + surface2: '#fff9c8', + surface6: '#e8d178', + surface5: '#f9e184', + surface4: '#ffe890', + surface3: '#fff', + fgSurface4: '#281d00', + fgSurface3: '#836a00', + fgSurface2: '#a58600', + fgSurface1: '#cba600', + stroke3: '#836a00', + stroke4: '#634f00', + stroke2: '#bfb179', + stroke1: '#d1c288', + bgFillDark: '#1f1e1b', + fgFillDark: '#fff1b1', + bgFillInverted2: '#1f1e1b', + bgFillInverted1: '#3a2c00', + fgFillInverted: '#fff1b1', + surface1: '#fff098', + fgSurface5: '#281d00', + }, + direction: 'darker', + }, + error: { + ramp: { + bgFill1: '#cc1818', + fgFill: '#f2efef', + bgFill2: '#b90000', + surface2: '#fff6f5', + surface6: '#f3c9c2', + surface5: '#f7dbd7', + surface4: '#f6e6e3', + surface3: '#fff', + fgSurface4: '#470000', + fgSurface3: '#cc1818', + fgSurface2: '#f74c40', + fgSurface1: '#ff8879', + stroke3: '#cc1818', + stroke4: '#9d0000', + stroke2: '#d39c95', + stroke1: '#dab1aa', + bgFillDark: '#231c1b', + fgFillDark: '#f2efef', + bgFillInverted2: '#231c1b', + bgFillInverted1: '#640000', + fgFillInverted: '#f2efef', + surface1: '#fcece9', + fgSurface5: '#470000', + }, + direction: 'darker', + }, + }, + '#1e1e1e|#00ffff': { + background: { + ramp: { + surface2: '#1e1e1e', + bgFill1: '#959595', + fgFill: '#1e1e1e', + bgFill2: '#a5a5a5', + surface6: '#383838', + surface5: '#2d2d2d', + surface4: '#282828', + surface3: '#242424', + fgSurface4: '#f0f0f0', + fgSurface3: '#b4b4b4', + fgSurface2: '#6e6e6e', + fgSurface1: '#535353', + stroke3: '#6e6e6e', + stroke4: '#8c8c8c', + stroke2: '#343434', + stroke1: '#272727', + bgFillDark: '#000', + fgFillDark: '#f0f0f0', + bgFillInverted2: '#f0f0f0', + bgFillInverted1: '#dbdbdb', + fgFillInverted: '#1e1e1e', + surface1: '#171717', + fgSurface5: '#f0f0f0', + }, + direction: 'lighter', + }, + primary: { + ramp: { + bgFill1: '#00e9e9', + fgFill: '#1c1f1f', + bgFill2: '#3ffefe', + surface2: '#0e2222', + surface6: '#1f3e3d', + surface5: '#193232', + surface4: '#142d2d', + surface3: '#142828', + fgSurface4: '#95ffff', + fgSurface3: '#00e9e9', + fgSurface2: '#007b7c', + fgSurface1: '#005e5e', + stroke3: '#00e9e9', + stroke4: '#fff', + stroke2: '#5f8e8d', + stroke1: '#567f7e', + bgFillDark: '#000', + fgFillDark: '#d9f7f7', + bgFillInverted2: '#d9f7f7', + bgFillInverted1: '#29f3f4', + fgFillInverted: '#1c1f1f', + surface1: '#0a1a1a', + fgSurface5: '#95ffff', + }, + direction: 'lighter', + }, + error: { + ramp: { + bgFill1: '#cc1818', + fgFill: '#f2efef', + bgFill2: '#dd3029', + surface2: '#36100c', + surface6: '#5e241f', + surface5: '#4e1d18', + surface4: '#481713', + surface3: '#3e1713', + fgSurface4: '#ffe6e0', + fgSurface3: '#ff8879', + fgSurface2: '#d32420', + fgSurface1: '#a00', + stroke3: '#ff8879', + stroke4: '#ffc3b9', + stroke2: '#9f5148', + stroke1: '#8c473f', + bgFillDark: '#000', + fgFillDark: '#f2efef', + bgFillInverted2: '#f2efef', + bgFillInverted1: '#ffd0c7', + fgFillInverted: '#231c1b', + surface1: '#290b08', + fgSurface5: '#ffe6e0', + }, + direction: 'lighter', + }, + }, +}; + +/** + * Return the exact theme ramps generated by trunk for the fixed Storybook + * samples. Trunk had four foreground steps, so fgSurface5 mirrors fgSurface4 + * only to keep the comparison data shape compatible with the current branch. + */ +export function getTrunkProductionThemeRamps( + background: string, + primary: string +): TrunkProductionThemeRamps { + const key = `${ background.toLowerCase() }|${ primary.toLowerCase() }`; + const ramps = TRUNK_PRODUCTION_FIXTURES[ key ]; + + if ( ! ramps ) { + throw new Error( `No trunk production fixture exists for ${ key }.` ); + } + + return ramps; +} From d638e9f3d1cb47f622d81dabfe00f0d67c3c90f2 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Thu, 3 Sep 2026 00:41:14 +0200 Subject: [PATCH 10/44] Theme: Promote perceptual color ramp algorithm --- .../internal/_wpds-token-fallbacks.scss | 34 +- packages/theme/CHANGELOG.md | 3 +- packages/theme/benchmark/README.md | 16 +- packages/theme/benchmark/color-ramps.ts | 8 + .../benchmark/perceptual-ramp-experiments.ts | 126 -- packages/theme/prebuilt/css/design-tokens.css | 28 +- .../prebuilt/js/design-token-fallbacks.mjs | 34 +- packages/theme/src/color-ramps/index.ts | 104 +- .../color-ramps/lib/build-foreground-scale.ts | 296 +++-- .../color-ramps/lib/build-perceptual-steps.ts | 428 +++++++ .../src/color-ramps/lib/default-ramps.ts | 38 +- packages/theme/src/color-ramps/lib/index.ts | 14 +- .../theme/src/color-ramps/lib/ramp-configs.ts | 46 +- packages/theme/src/color-ramps/lib/types.ts | 12 +- .../stories/perceptual-ramp-experiment.ts | 1092 ----------------- .../stories/perceptual-ramp-pilot.module.css | 313 ----- .../stories/perceptual-ramp-pilot.story.tsx | 828 ------------- .../test/perceptual-ramp-experiment.test.ts | 357 ------ .../stories/trunk-production-fixtures.ts | 806 ------------ .../test/__snapshots__/index.test.ts.snap | 612 ++++----- .../theme/src/color-ramps/test/index.test.ts | 3 + .../build-plugin-parity.test.ts.snap | 6 +- .../src/test/theme-provider.browser.test.tsx | 4 +- packages/theme/tokens/color.json | 144 +-- 24 files changed, 1268 insertions(+), 4084 deletions(-) delete mode 100644 packages/theme/benchmark/perceptual-ramp-experiments.ts create mode 100644 packages/theme/src/color-ramps/lib/build-perceptual-steps.ts delete mode 100644 packages/theme/src/color-ramps/stories/perceptual-ramp-experiment.ts delete mode 100644 packages/theme/src/color-ramps/stories/perceptual-ramp-pilot.module.css delete mode 100644 packages/theme/src/color-ramps/stories/perceptual-ramp-pilot.story.tsx delete mode 100644 packages/theme/src/color-ramps/stories/test/perceptual-ramp-experiment.test.ts delete mode 100644 packages/theme/src/color-ramps/stories/trunk-production-fixtures.ts diff --git a/packages/base-styles/internal/_wpds-token-fallbacks.scss b/packages/base-styles/internal/_wpds-token-fallbacks.scss index 290fccbccee08c..091d1b4b154f99 100644 --- a/packages/base-styles/internal/_wpds-token-fallbacks.scss +++ b/packages/base-styles/internal/_wpds-token-fallbacks.scss @@ -19,7 +19,7 @@ $fallbacks: ( "var(--wp-admin-theme-color, #3858e9)", "--wpds-color-background-interactive-brand-strong-active": "color-mix(in oklch, var(--wp-admin-theme-color, #3858e9) 93%, black)", - "--wpds-color-background-interactive-brand-strong-disabled": "#e6e6e6", + "--wpds-color-background-interactive-brand-strong-disabled": "#e3e3e3", "--wpds-color-background-interactive-brand-weak": "#0000", "--wpds-color-background-interactive-brand-weak-active": "color-mix(in oklch, var(--wp-admin-theme-color, #3858e9) 12%, white)", @@ -29,30 +29,30 @@ $fallbacks: ( "--wpds-color-background-interactive-error-disabled": "#0000", "--wpds-color-background-interactive-error-strong": "#cc1818", "--wpds-color-background-interactive-error-strong-active": "#b90000", - "--wpds-color-background-interactive-error-strong-disabled": "#e6e6e6", + "--wpds-color-background-interactive-error-strong-disabled": "#e3e3e3", "--wpds-color-background-interactive-error-weak": "#0000", "--wpds-color-background-interactive-error-weak-active": "#fae5e1", "--wpds-color-background-interactive-error-weak-disabled": "#0000", "--wpds-color-background-interactive-neutral-strong": "#2d2d2d", "--wpds-color-background-interactive-neutral-strong-active": "#1e1e1e", - "--wpds-color-background-interactive-neutral-strong-disabled": "#e6e6e6", + "--wpds-color-background-interactive-neutral-strong-disabled": "#e3e3e3", "--wpds-color-background-interactive-neutral-weak": "#0000", - "--wpds-color-background-interactive-neutral-weak-active": "#ededed", + "--wpds-color-background-interactive-neutral-weak-active": "#efefef", "--wpds-color-background-interactive-neutral-weak-disabled": "#0000", "--wpds-color-background-surface-brand": "color-mix(in oklch, var(--wp-admin-theme-color, #3858e9) 9%, white)", - "--wpds-color-background-surface-caution": "#fee995", + "--wpds-color-background-surface-caution": "#ffea96", "--wpds-color-background-surface-caution-weak": "#fff9ca", "--wpds-color-background-surface-error": "#fae5e1", "--wpds-color-background-surface-error-weak": "#fff6f5", - "--wpds-color-background-surface-info": "#deebfa", + "--wpds-color-background-surface-info": "#e0edfc", "--wpds-color-background-surface-info-weak": "#f3f9ff", "--wpds-color-background-surface-neutral": "#fcfcfc", "--wpds-color-background-surface-neutral-strong": "#fff", "--wpds-color-background-surface-neutral-weak": "#f4f4f4", "--wpds-color-background-surface-success": "#c2f8ca", "--wpds-color-background-surface-success-weak": "#ebffed", - "--wpds-color-background-surface-warning": "#fde6be", + "--wpds-color-background-surface-warning": "#ffe8c0", "--wpds-color-background-surface-warning-weak": "#fff7e1", "--wpds-color-background-thumb-brand": "var(--wp-admin-theme-color, #3858e9)", @@ -78,27 +78,27 @@ $fallbacks: ( "--wpds-color-foreground-content-warning": "#2e1900", "--wpds-color-foreground-content-warning-weak": "#936300", "--wpds-color-foreground-interactive-brand": - "color-mix(in oklch, var(--wp-admin-theme-color, #3858e9) 98%, black)", + "color-mix(in oklch, var(--wp-admin-theme-color, #3858e9) 88%, black)", "--wpds-color-foreground-interactive-brand-active": - "color-mix(in oklch, var(--wp-admin-theme-color, #3858e9) 52%, black)", + "color-mix(in oklch, var(--wp-admin-theme-color, #3858e9) 35%, black)", "--wpds-color-foreground-interactive-brand-disabled": "#8d8d8d", "--wpds-color-foreground-interactive-brand-strong": "#fff", "--wpds-color-foreground-interactive-brand-strong-active": "#fff", "--wpds-color-foreground-interactive-brand-strong-disabled": "#8d8d8d", - "--wpds-color-foreground-interactive-error": "#be1616", - "--wpds-color-foreground-interactive-error-active": "#470000", + "--wpds-color-foreground-interactive-error": "#980f0f", + "--wpds-color-foreground-interactive-error-active": "#260101", "--wpds-color-foreground-interactive-error-disabled": "#8d8d8d", - "--wpds-color-foreground-interactive-error-strong": "#eae6e6", - "--wpds-color-foreground-interactive-error-strong-active": "#eae6e6", + "--wpds-color-foreground-interactive-error-strong": "#eae6e5", + "--wpds-color-foreground-interactive-error-strong-active": "#eae6e5", "--wpds-color-foreground-interactive-error-strong-disabled": "#8d8d8d", - "--wpds-color-foreground-interactive-neutral": "#646464", - "--wpds-color-foreground-interactive-neutral-active": "#1e1e1e", + "--wpds-color-foreground-interactive-neutral": "#505050", + "--wpds-color-foreground-interactive-neutral-active": "#141414", "--wpds-color-foreground-interactive-neutral-disabled": "#8d8d8d", "--wpds-color-foreground-interactive-neutral-strong": "#f0f0f0", "--wpds-color-foreground-interactive-neutral-strong-active": "#f0f0f0", "--wpds-color-foreground-interactive-neutral-strong-disabled": "#8d8d8d", - "--wpds-color-foreground-interactive-neutral-weak": "#707070", - "--wpds-color-foreground-interactive-neutral-weak-active": "#1e1e1e", + "--wpds-color-foreground-interactive-neutral-weak": "#737373", + "--wpds-color-foreground-interactive-neutral-weak-active": "#141414", "--wpds-color-foreground-interactive-neutral-weak-disabled": "#8d8d8d", "--wpds-color-stroke-focus": "var(--wp-admin-theme-color, #3858e9)", "--wpds-color-stroke-interactive-brand": diff --git a/packages/theme/CHANGELOG.md b/packages/theme/CHANGELOG.md index 2f134992fafc0b..e1f24fdee3ab64 100644 --- a/packages/theme/CHANGELOG.md +++ b/packages/theme/CHANGELOG.md @@ -58,7 +58,7 @@ ### Enhancements -- Generate foreground scales with five perception-aware steps, separate resting and active interactive colors, and keep active fills darker while preserving WCAG contrast floors. ([#82294](https://github.com/WordPress/gutenberg/pull/82294)) +- Generate perception-aware color ramps with ordered surface and stroke progressions, five foreground steps, stronger active interactive colors, and unchanged WCAG contrast floors. ([#82294](https://github.com/WordPress/gutenberg/pull/82294)) - Add a Lightning CSS visitor plugin that injects design system token fallbacks, available as `@wordpress/theme/lightningcss-plugins/lightningcss-ds-token-fallbacks`, with an optional peer range of `>=1.33.0 <2.0.0`. ([#80401](https://github.com/WordPress/gutenberg/pull/80401)) ### Bug Fixes @@ -75,6 +75,7 @@ - Reuse seed-dependent calculations during color-ramp constraint searches. ([#82545](https://github.com/WordPress/gutenberg/pull/82545)) - Generate the default ramps before derived token artifacts so one build uses the current ramp algorithm throughout. ([#82525](https://github.com/WordPress/gutenberg/pull/82525)) - Cache relative luminance calculations used by color-ramp contrast checks. ([#82445](https://github.com/WordPress/gutenberg/pull/82445)) +- Generate the default ramps before derived token artifacts so one build uses the current ramp algorithm throughout. ([#82294](https://github.com/WordPress/gutenberg/pull/82294)) - Enforce NodeNext module resolution in the build project so future declaration imports are checked against the package's published ESM resolution rules. ([#82088](https://github.com/WordPress/gutenberg/pull/82088)) - Update the `@types/node` development dependency to v24, matching the Node.js version the repository builds and tests against ([#82616](https://github.com/WordPress/gutenberg/pull/82616)). - Declare the `@storybook/addon-docs` development dependency used by the package's story documentation ([#82676](https://github.com/WordPress/gutenberg/pull/82676)). diff --git a/packages/theme/benchmark/README.md b/packages/theme/benchmark/README.md index 63775c18fda52a..6dcd1b1796b310 100644 --- a/packages/theme/benchmark/README.md +++ b/packages/theme/benchmark/README.md @@ -22,19 +22,7 @@ npm run benchmark:color-ramps --workspace @wordpress/theme -- \ --label candidate ``` -Both worktrees need their dependencies installed. Add `--json` to save -machine-readable results. Compare runs made with the same Node.js version and +Both worktrees need their dependencies installed. Add `--json` for +machine-readable output. Compare runs made with the same Node.js version and similar system load. The benchmark reports measurements but does not enforce a timing threshold because local and CI timings vary. - -## Perceptual ramp experiments - -The Storybook pilot also has a benchmark that compares the anchored production -candidate, the all-APCA experiment, and the role-specific hybrid. Each sample -builds the neutral, brand, and error ramps for all six pilot seed combinations. - -```sh -npm run benchmark:perceptual-ramps --workspace @wordpress/theme -``` - -Add `-- --json` for machine-readable output. diff --git a/packages/theme/benchmark/color-ramps.ts b/packages/theme/benchmark/color-ramps.ts index 2ad6d092d39c11..ad0901f5e71af3 100644 --- a/packages/theme/benchmark/color-ramps.ts +++ b/packages/theme/benchmark/color-ramps.ts @@ -73,6 +73,14 @@ When --baseline-root is set, each checkout runs in a separate Node.js process before the script reports the percentage difference.`; async function loadRampModule( sourceRoot: string ): Promise< RampModule > { + // Historical revisions relied on the full Color.js entry point to register + // these spaces. Register them here so the same benchmark can execute both + // sides without changing the measured ramp calls. + const { ColorSpace, OKLab, OKLCH, sRGB } = await import( 'colorjs.io/fn' ); + ColorSpace.register( sRGB ); + ColorSpace.register( OKLab ); + ColorSpace.register( OKLCH ); + const modulePath = resolve( sourceRoot, 'packages/theme/src/color-ramps/index.ts' diff --git a/packages/theme/benchmark/perceptual-ramp-experiments.ts b/packages/theme/benchmark/perceptual-ramp-experiments.ts deleted file mode 100644 index 8101ba17a6e8c2..00000000000000 --- a/packages/theme/benchmark/perceptual-ramp-experiments.ts +++ /dev/null @@ -1,126 +0,0 @@ -/* eslint-disable no-console -- This command writes benchmark output. */ -import { performance } from 'node:perf_hooks'; -import { parseArgs } from 'node:util'; -import { - EXPERIMENTAL_RAMP_METHODS, - buildExperimentalThemeRamps, - type ExperimentalRampMethod, -} from '../src/color-ramps/stories/perceptual-ramp-experiment'; - -type BenchmarkResult = { - method: ExperimentalRampMethod; - medianMs: number; - p95Ms: number; - checksum: number; -}; - -const WARMUP_SAMPLES = 8; -const MEASURED_SAMPLES = 30; -const FIXTURES = [ - { - background: '#fcfcfc', - primary: '#3858e9', - }, - { - background: '#1e1e1e', - primary: '#3858e9', - }, - { - background: '#413256', - primary: '#a3b745', - }, - { - background: '#777777', - primary: '#d63638', - }, - { - background: '#fcfcfc', - primary: '#ffd700', - }, - { - background: '#1e1e1e', - primary: '#00ffff', - }, -] as const; - -const { values } = parseArgs( { - options: { - json: { type: 'boolean', default: false }, - }, - strict: true, -} ); - -function updateChecksum( checksum: number, value: string ) { - for ( const character of value ) { - checksum = - ( checksum * 31 + ( character.codePointAt( 0 ) ?? 0 ) ) % - 2_147_483_647; - } - return checksum; -} - -function runMethod( method: ExperimentalRampMethod ) { - let checksum = 0; - const runSample = () => { - const start = performance.now(); - for ( const fixture of FIXTURES ) { - const ramps = buildExperimentalThemeRamps( { method, ...fixture } ); - checksum = updateChecksum( - checksum, - `${ ramps.background.ramp.fgSurface5 }${ ramps.primary.ramp.bgFill2 }${ ramps.error.ramp.stroke3 }` - ); - } - return ( performance.now() - start ) / FIXTURES.length; - }; - - for ( let sample = 0; sample < WARMUP_SAMPLES; sample++ ) { - runSample(); - } - - const samples = Array.from( { length: MEASURED_SAMPLES }, runSample ).sort( - ( first, second ) => first - second - ); - const middle = Math.floor( samples.length / 2 ); - const medianMs = - samples.length % 2 === 0 - ? ( samples[ middle - 1 ] + samples[ middle ] ) / 2 - : samples[ middle ]; - - return { - method, - medianMs, - p95Ms: samples[ Math.ceil( samples.length * 0.95 ) - 1 ], - checksum, - } satisfies BenchmarkResult; -} - -const results = EXPERIMENTAL_RAMP_METHODS.map( runMethod ); -const report = { - environment: { - node: process.version, - platform: process.platform, - arch: process.arch, - }, - warmupSamples: WARMUP_SAMPLES, - measuredSamples: MEASURED_SAMPLES, - fixtures: FIXTURES, - results, -}; - -if ( values.json ) { - console.log( JSON.stringify( report, null, 2 ) ); -} else { - console.log( 'Perceptual ramp experiment benchmark' ); - console.log( - `${ report.environment.node } ${ report.environment.platform }/${ report.environment.arch }; ${ MEASURED_SAMPLES } samples after ${ WARMUP_SAMPLES } warmups` - ); - console.log( `${ FIXTURES.length } ramp triplets per sample` ); - console.table( - results.map( ( result ) => ( { - method: result.method, - 'median ms/ramp triplet': result.medianMs.toFixed( 3 ), - 'p95 ms/ramp triplet': result.p95Ms.toFixed( 3 ), - } ) ) - ); -} -/* eslint-enable no-console */ diff --git a/packages/theme/prebuilt/css/design-tokens.css b/packages/theme/prebuilt/css/design-tokens.css index 0403c137627ef4..62a07d70d120c9 100644 --- a/packages/theme/prebuilt/css/design-tokens.css +++ b/packages/theme/prebuilt/css/design-tokens.css @@ -28,7 +28,7 @@ /* Background color for interactive elements with brand tone and strong emphasis that are hovered, focused, or active. */ --wpds-color-background-interactive-brand-strong-active: #2e49d9; /* Background color for interactive elements with brand tone and strong emphasis, in their disabled state. */ - --wpds-color-background-interactive-brand-strong-disabled: #e6e6e6; + --wpds-color-background-interactive-brand-strong-disabled: #e3e3e3; /* Background color for interactive elements with brand tone and weak emphasis. */ --wpds-color-background-interactive-brand-weak: #0000; /* Background color for interactive elements with brand tone and weak emphasis that are hovered, focused, or active. */ @@ -46,7 +46,7 @@ /* Background color for interactive elements with error tone and strong emphasis that are hovered, focused, or active. */ --wpds-color-background-interactive-error-strong-active: #b90000; /* Background color for interactive elements with error tone and strong emphasis, in their disabled state. */ - --wpds-color-background-interactive-error-strong-disabled: #e6e6e6; + --wpds-color-background-interactive-error-strong-disabled: #e3e3e3; /* Background color for interactive elements with error tone and weak emphasis. */ --wpds-color-background-interactive-error-weak: #0000; /* Background color for interactive elements with error tone and weak emphasis that are hovered, focused, or active. */ @@ -130,39 +130,39 @@ /* Foreground color for content like text with info tone and weak emphasis. */ --wpds-color-foreground-content-info-weak: #006bd7; /* Foreground color for content like text with normal emphasis. */ - --wpds-color-foreground-content-neutral: #1e1e1e; + --wpds-color-foreground-content-neutral: #141414; /* Foreground color for content like text with weak emphasis. */ - --wpds-color-foreground-content-neutral-weak: #707070; + --wpds-color-foreground-content-neutral-weak: #737373; /* Foreground color for content like text with success tone and normal emphasis. */ - --wpds-color-foreground-content-success: #002900; + --wpds-color-foreground-content-success: #041708; /* Foreground color for content like text with success tone and weak emphasis. */ --wpds-color-foreground-content-success-weak: #008030; /* Foreground color for content like text with warning tone and normal emphasis. */ - --wpds-color-foreground-content-warning: #2e1900; + --wpds-color-foreground-content-warning: #1b1203; /* Foreground color for content like text with warning tone and weak emphasis. */ --wpds-color-foreground-content-warning-weak: #936300; /* Foreground color for interactive elements with brand tone and normal emphasis. */ - --wpds-color-foreground-interactive-brand: #3351e8; + --wpds-color-foreground-interactive-brand: #222eda; /* Foreground color for interactive elements with brand tone and normal emphasis that are hovered, focused, or active. */ - --wpds-color-foreground-interactive-brand-active: #0b0070; + --wpds-color-foreground-interactive-brand-active: #03063b; /* Foreground color for interactive elements with brand tone and normal emphasis, in their disabled state. */ --wpds-color-foreground-interactive-brand-disabled: #8d8d8d; /* Foreground color for interactive elements with brand tone and strong emphasis. */ - --wpds-color-foreground-interactive-brand-strong: #e7e8ea; + --wpds-color-foreground-interactive-brand-strong: #e7e8eb; /* Foreground color for interactive elements with brand tone and strong emphasis that are hovered, focused, or active. */ - --wpds-color-foreground-interactive-brand-strong-active: #e7e8ea; + --wpds-color-foreground-interactive-brand-strong-active: #e7e8eb; /* Foreground color for interactive elements with brand tone and strong emphasis, in their disabled state. */ --wpds-color-foreground-interactive-brand-strong-disabled: #8d8d8d; /* Foreground color for interactive elements with error tone and normal emphasis. */ - --wpds-color-foreground-interactive-error: #be1616; + --wpds-color-foreground-interactive-error: #980f0f; /* Foreground color for interactive elements with error tone and normal emphasis that are hovered, focused, or active. */ - --wpds-color-foreground-interactive-error-active: #470000; + --wpds-color-foreground-interactive-error-active: #260101; /* Foreground color for interactive elements with error tone and normal emphasis, in their disabled state. */ --wpds-color-foreground-interactive-error-disabled: #8d8d8d; /* Foreground color for interactive elements with error tone and strong emphasis. */ - --wpds-color-foreground-interactive-error-strong: #eae6e6; + --wpds-color-foreground-interactive-error-strong: #eae6e5; /* Foreground color for interactive elements with error tone and strong emphasis that are hovered, focused, or active. */ - --wpds-color-foreground-interactive-error-strong-active: #eae6e6; + --wpds-color-foreground-interactive-error-strong-active: #eae6e5; /* Foreground color for interactive elements with error tone and strong emphasis, in their disabled state. */ --wpds-color-foreground-interactive-error-strong-disabled: #8d8d8d; /* Foreground color for interactive elements with neutral tone and normal emphasis. */ diff --git a/packages/theme/prebuilt/js/design-token-fallbacks.mjs b/packages/theme/prebuilt/js/design-token-fallbacks.mjs index 23535e8e909508..2c64165bcdad3b 100644 --- a/packages/theme/prebuilt/js/design-token-fallbacks.mjs +++ b/packages/theme/prebuilt/js/design-token-fallbacks.mjs @@ -17,7 +17,7 @@ export default { 'var(--wp-admin-theme-color, #3858e9)', '--wpds-color-background-interactive-brand-strong-active': 'color-mix(in oklch, var(--wp-admin-theme-color, #3858e9) 93%, black)', - '--wpds-color-background-interactive-brand-strong-disabled': '#e6e6e6', + '--wpds-color-background-interactive-brand-strong-disabled': '#e3e3e3', '--wpds-color-background-interactive-brand-weak': '#0000', '--wpds-color-background-interactive-brand-weak-active': 'color-mix(in oklch, var(--wp-admin-theme-color, #3858e9) 12%, white)', @@ -27,30 +27,30 @@ export default { '--wpds-color-background-interactive-error-disabled': '#0000', '--wpds-color-background-interactive-error-strong': '#cc1818', '--wpds-color-background-interactive-error-strong-active': '#b90000', - '--wpds-color-background-interactive-error-strong-disabled': '#e6e6e6', + '--wpds-color-background-interactive-error-strong-disabled': '#e3e3e3', '--wpds-color-background-interactive-error-weak': '#0000', '--wpds-color-background-interactive-error-weak-active': '#fae5e1', '--wpds-color-background-interactive-error-weak-disabled': '#0000', '--wpds-color-background-interactive-neutral-strong': '#2d2d2d', '--wpds-color-background-interactive-neutral-strong-active': '#1e1e1e', - '--wpds-color-background-interactive-neutral-strong-disabled': '#e6e6e6', + '--wpds-color-background-interactive-neutral-strong-disabled': '#e3e3e3', '--wpds-color-background-interactive-neutral-weak': '#0000', - '--wpds-color-background-interactive-neutral-weak-active': '#ededed', + '--wpds-color-background-interactive-neutral-weak-active': '#efefef', '--wpds-color-background-interactive-neutral-weak-disabled': '#0000', '--wpds-color-background-surface-brand': 'color-mix(in oklch, var(--wp-admin-theme-color, #3858e9) 9%, white)', - '--wpds-color-background-surface-caution': '#fee995', + '--wpds-color-background-surface-caution': '#ffea96', '--wpds-color-background-surface-caution-weak': '#fff9ca', '--wpds-color-background-surface-error': '#fae5e1', '--wpds-color-background-surface-error-weak': '#fff6f5', - '--wpds-color-background-surface-info': '#deebfa', + '--wpds-color-background-surface-info': '#e0edfc', '--wpds-color-background-surface-info-weak': '#f3f9ff', '--wpds-color-background-surface-neutral': '#fcfcfc', '--wpds-color-background-surface-neutral-strong': '#fff', '--wpds-color-background-surface-neutral-weak': '#f4f4f4', '--wpds-color-background-surface-success': '#c2f8ca', '--wpds-color-background-surface-success-weak': '#ebffed', - '--wpds-color-background-surface-warning': '#fde6be', + '--wpds-color-background-surface-warning': '#ffe8c0', '--wpds-color-background-surface-warning-weak': '#fff7e1', '--wpds-color-background-thumb-brand': 'var(--wp-admin-theme-color, #3858e9)', @@ -76,27 +76,27 @@ export default { '--wpds-color-foreground-content-warning': '#2e1900', '--wpds-color-foreground-content-warning-weak': '#936300', '--wpds-color-foreground-interactive-brand': - 'color-mix(in oklch, var(--wp-admin-theme-color, #3858e9) 98%, black)', + 'color-mix(in oklch, var(--wp-admin-theme-color, #3858e9) 88%, black)', '--wpds-color-foreground-interactive-brand-active': - 'color-mix(in oklch, var(--wp-admin-theme-color, #3858e9) 52%, black)', + 'color-mix(in oklch, var(--wp-admin-theme-color, #3858e9) 35%, black)', '--wpds-color-foreground-interactive-brand-disabled': '#8d8d8d', '--wpds-color-foreground-interactive-brand-strong': '#fff', '--wpds-color-foreground-interactive-brand-strong-active': '#fff', '--wpds-color-foreground-interactive-brand-strong-disabled': '#8d8d8d', - '--wpds-color-foreground-interactive-error': '#be1616', - '--wpds-color-foreground-interactive-error-active': '#470000', + '--wpds-color-foreground-interactive-error': '#980f0f', + '--wpds-color-foreground-interactive-error-active': '#260101', '--wpds-color-foreground-interactive-error-disabled': '#8d8d8d', - '--wpds-color-foreground-interactive-error-strong': '#eae6e6', - '--wpds-color-foreground-interactive-error-strong-active': '#eae6e6', + '--wpds-color-foreground-interactive-error-strong': '#eae6e5', + '--wpds-color-foreground-interactive-error-strong-active': '#eae6e5', '--wpds-color-foreground-interactive-error-strong-disabled': '#8d8d8d', - '--wpds-color-foreground-interactive-neutral': '#646464', - '--wpds-color-foreground-interactive-neutral-active': '#1e1e1e', + '--wpds-color-foreground-interactive-neutral': '#505050', + '--wpds-color-foreground-interactive-neutral-active': '#141414', '--wpds-color-foreground-interactive-neutral-disabled': '#8d8d8d', '--wpds-color-foreground-interactive-neutral-strong': '#f0f0f0', '--wpds-color-foreground-interactive-neutral-strong-active': '#f0f0f0', '--wpds-color-foreground-interactive-neutral-strong-disabled': '#8d8d8d', - '--wpds-color-foreground-interactive-neutral-weak': '#707070', - '--wpds-color-foreground-interactive-neutral-weak-active': '#1e1e1e', + '--wpds-color-foreground-interactive-neutral-weak': '#737373', + '--wpds-color-foreground-interactive-neutral-weak-active': '#141414', '--wpds-color-foreground-interactive-neutral-weak-disabled': '#8d8d8d', '--wpds-color-stroke-focus': 'var(--wp-admin-theme-color, #3858e9)', '--wpds-color-stroke-interactive-brand': diff --git a/packages/theme/src/color-ramps/index.ts b/packages/theme/src/color-ramps/index.ts index 1f0d9b3b849534..ef4011e1843523 100644 --- a/packages/theme/src/color-ramps/index.ts +++ b/packages/theme/src/color-ramps/index.ts @@ -1,6 +1,17 @@ -import { get, OKLCH } from 'colorjs.io/fn'; +import { + ColorSpace, + contrastAPCA, + deltaEOK2, + get, + OKLab, + OKLCH, + sRGB, +} from 'colorjs.io/fn'; import { buildRamp } from './lib/index.ts'; -import { clampAccentScaleReferenceLightness } from './lib/utils.ts'; +import { + clampAccentScaleReferenceLightness, + computeBetterFgColorDirection, +} from './lib/utils.ts'; import { BG_RAMP_CONFIG, ACCENT_RAMP_CONFIG } from './lib/ramp-configs.ts'; import type { RampResult as InternalRampResult, @@ -8,9 +19,91 @@ import type { Ramp, } from './lib/types.ts'; import { getContrast } from './lib/color-utils.ts'; -import { CONTRAST_COMBINATIONS } from './lib/constants.ts'; +import { BLACK, CONTRAST_COMBINATIONS, WHITE } from './lib/constants.ts'; export { DEFAULT_SEED_COLORS } from './lib/constants.ts'; +ColorSpace.register( sRGB ); +ColorSpace.register( OKLab ); +ColorSpace.register( OKLCH ); + +// Only solve both polarities near the black/white WCAG crossover. This keeps +// the extra work and possible direction change away from decisive seeds. +const MAXIMUM_AMBIGUOUS_ENDPOINT_CONTRAST_DIFFERENCE = 1; +// Allow the alternate direction only when its semantic anchor remains nearly +// as faithful to the supplied seed as the initially preferred direction. +const MAXIMUM_ALTERNATE_POLARITY_DELTA_E_DRIFT = 0.02; +// Do not switch direction for APCA rounding noise when neither candidate can +// fit the full interaction-state interval. +const MINIMUM_ALTERNATE_POLARITY_APCA_IMPROVEMENT = 1; + +function getInteractionStateInterval( ramp: InternalRampResult ) { + const background = + ramp.ramp[ BG_RAMP_CONFIG.foregroundScale.perceptualReference ]; + return ( + Math.abs( contrastAPCA( background, ramp.ramp.fgSurface5 ) ) - + Math.abs( contrastAPCA( background, ramp.ramp.fgSurface4 ) ) + ); +} + +function passesBackgroundWcagGates( ramp: InternalRampResult ) { + return ( + ramp.warnings === undefined && + checkAccessibleCombinations( { bgRamp: ramp } ).length === 0 + ); +} + +function hasAmbiguousEndpointContrast( seed: string ) { + return ( + Math.abs( getContrast( seed, BLACK ) - getContrast( seed, WHITE ) ) <= + MAXIMUM_AMBIGUOUS_ENDPOINT_CONTRAST_DIFFERENCE + ); +} + +function selectBackgroundRampPolarity( + seed: string, + preferred: InternalRampResult +) { + const target = + BG_RAMP_CONFIG.foregroundScale.perceptualTargets.normalToActive; + const preferredInterval = getInteractionStateInterval( preferred ); + const preferredPasses = passesBackgroundWcagGates( preferred ); + if ( + ( preferredPasses && preferredInterval >= target ) || + ! hasAmbiguousEndpointContrast( seed ) + ) { + return preferred; + } + + const alternateDirection = computeBetterFgColorDirection( seed ).worse; + const alternate = buildRamp( seed, BG_RAMP_CONFIG, { + mainDirection: alternateDirection, + } ); + if ( ! passesBackgroundWcagGates( alternate ) ) { + return preferred; + } + + const preferredDrift = deltaEOK2( seed, preferred.ramp.surface2 ); + const alternateDrift = deltaEOK2( seed, alternate.ramp.surface2 ); + if ( + alternateDrift > + preferredDrift + MAXIMUM_ALTERNATE_POLARITY_DELTA_E_DRIFT + ) { + return preferred; + } + + const alternateInterval = getInteractionStateInterval( alternate ); + if ( + ! preferredPasses || + ( preferredInterval < target && alternateInterval >= target ) || + alternateInterval >= + preferredInterval + MINIMUM_ALTERNATE_POLARITY_APCA_IMPROVEMENT + ) { + return alternate; + } + + return preferred; +} + /** * Creates a background ramp. * @param seed The seed color for the background ramp. @@ -20,7 +113,10 @@ export function buildBgRamp( seed: string ) { throw new Error( 'Seed color must be a non-empty string' ); } - return buildRamp( seed, BG_RAMP_CONFIG ); + return selectBackgroundRampPolarity( + seed, + buildRamp( seed, BG_RAMP_CONFIG ) + ); } const STEP_TO_PIN = 'surface2'; diff --git a/packages/theme/src/color-ramps/lib/build-foreground-scale.ts b/packages/theme/src/color-ramps/lib/build-foreground-scale.ts index a0c04d2b699133..b34a6c8b2972c4 100644 --- a/packages/theme/src/color-ramps/lib/build-foreground-scale.ts +++ b/packages/theme/src/color-ramps/lib/build-foreground-scale.ts @@ -26,6 +26,8 @@ ColorSpace.register( OklchSrgb ); type GetColorForLightness = ( lightness: number ) => PlainColorObject; +const PERCEPTUAL_INTERVAL_SERIALIZATION_TOPUP = 0.25; + function getChromaPreservingColorForLightness( seed: PlainColorObject, lightness: number @@ -262,6 +264,77 @@ function serializeColorMeetingContrast( { return getColorString( strongColor ); } +function serializeColorMeetingPerceptualInterval( { + background, + normalColor, + strongColor, + strongEndpoint, + getColorAtLightness, + references, + wcagTarget, + perceptualTarget, +}: { + background: PlainColorObject; + normalColor: string; + strongColor: string; + strongEndpoint: PlainColorObject; + getColorAtLightness: GetColorForLightness; + references: readonly string[]; + wcagTarget: number; + perceptualTarget: number; +} ) { + const normalContrast = getPerceptualContrastMagnitude( + background, + normalColor + ); + const requiredStrongContrast = normalContrast + perceptualTarget; + const strongContrast = getPerceptualContrastMagnitude( + background, + strongColor + ); + if ( strongContrast >= requiredStrongContrast ) { + return strongColor; + } + + const serializedEndpoint = getColorString( strongEndpoint ); + if ( + getPerceptualContrastMagnitude( background, serializedEndpoint ) < + requiredStrongContrast || + references.some( + ( reference ) => + getContrast( reference, serializedEndpoint ) < wcagTarget + ) + ) { + return strongColor; + } + + const strongLightness = get( clampToGamut( strongColor ), [ OKLCH, 'l' ] ); + const endpointLightness = get( strongEndpoint, [ OKLCH, 'l' ] ); + const direction = Math.sign( endpointLightness - strongLightness ); + if ( direction === 0 ) { + return strongColor; + } + const maximumOffset = Math.abs( endpointLightness - strongLightness ); + + for ( let offset = 0.00025; offset <= maximumOffset; offset += 0.00025 ) { + const strengthened = getColorString( + getColorAtLightness( strongLightness + direction * offset ) + ); + if ( + getPerceptualContrastMagnitude( background, strengthened ) >= + requiredStrongContrast && + references.every( + ( reference ) => + getContrast( reference, strengthened ) >= wcagTarget + ) + ) { + return strengthened; + } + } + + return serializedEndpoint; +} + /** * Rebuild the foreground part of an otherwise complete ramp. APCA positions * the intermediate steps within the visible contrast range, while WCAG ratios @@ -269,7 +342,7 @@ function serializeColorMeetingContrast( { * * @param ramp Ramp whose foreground steps will be rebuilt. * @param backgroundRamp Background ramp on which the colors will appear. - * @param config Foreground scale anchors, positions, and constraints. + * @param config Foreground scale anchors, targets, and constraints. */ export function buildForegroundScale( ramp: RampResult, @@ -284,39 +357,21 @@ export function buildForegroundScale( clampToGamut( backgroundRamp.ramp[ config.perceptualReference ] ), sRGB ); - const strongStep = config.steps.at( -1 )!; - const currentStrongColor = clampToGamut( ramp.ramp[ strongStep.name ] ); + const steps = Object.fromEntries( + config.steps.map( ( step ) => [ step.name, step ] ) + ) as Record< + ForegroundRampStep, + ForegroundScaleConfig[ 'steps' ][ number ] + >; + const strongStep = steps.fgSurface5; const strongEndpoint = getColorAtLightness( ramp.direction === 'lighter' ? 1 : 0 ); - const strongColor = meetsContrastTarget( - currentStrongColor, - strongStep, - ramp, - backgroundRamp - ) - ? currentStrongColor - : findColorAtContrastTarget( { - getColorAtLightness, - weakColor: currentStrongColor, - strongColor: strongEndpoint, - references: getConstraintReferences( - strongStep, - ramp, - backgroundRamp - ), - target: strongStep.contrast.target, - } ); const colors = new Map< ForegroundRampStep, PlainColorObject >(); for ( const step of config.steps.filter( ( { preserveAnchor } ) => preserveAnchor ) ) { - if ( step === strongStep ) { - colors.set( step.name, strongColor ); - continue; - } - const currentColor = clampToGamut( ramp.ramp[ step.name ] ); colors.set( step.name, @@ -329,7 +384,7 @@ export function buildForegroundScale( lightness ), weakColor: currentColor, - strongColor, + strongColor: strongEndpoint, references: getConstraintReferences( step, ramp, @@ -340,59 +395,144 @@ export function buildForegroundScale( ); } - const firstStep = config.steps[ 0 ]; - const firstColor = colors.get( firstStep.name )!; - const weakContrast = getPerceptualContrastMagnitude( + const fgSurface2 = colors.get( 'fgSurface2' )!; + const fgSurface3 = findColorAtContrastTarget( { + getColorAtLightness, + weakColor: getColorAtLightness( get( fgSurface2, [ OKLCH, 'l' ] ) ), + strongColor: strongEndpoint, + references: getConstraintReferences( + steps.fgSurface3, + ramp, + backgroundRamp + ), + target: steps.fgSurface3.contrast.target, + } ); + const fgSurface3Contrast = getPerceptualContrastMagnitude( displayBackground, - firstColor + fgSurface3 ); - const strongContrast = getPerceptualContrastMagnitude( + const minimumFgSurface4 = findColorAtContrastTarget( { + getColorAtLightness, + weakColor: fgSurface3, + strongColor: strongEndpoint, + references: getConstraintReferences( + steps.fgSurface4, + ramp, + backgroundRamp + ), + target: steps.fgSurface4.contrast.target, + } ); + const minimumFgSurface4Contrast = getPerceptualContrastMagnitude( displayBackground, - strongColor + minimumFgSurface4 ); - - for ( const step of config.steps.filter( - ( { preserveAnchor } ) => ! preserveAnchor - ) ) { - const stepIndex = config.steps.indexOf( step ); - const previousStep = config.steps[ stepIndex - 1 ]; - const previousColor = colors.get( previousStep.name )!; - const target = - weakContrast + ( strongContrast - weakContrast ) * step.progress; - const previousContrast = getPerceptualContrastMagnitude( - displayBackground, - previousColor + const maximumStrongContrast = getPerceptualContrastMagnitude( + displayBackground, + strongEndpoint + ); + const { + normalContrast: preferredNormalContrast, + endpointReserve, + weakToNormal, + normalToActive, + } = config.perceptualTargets; + const weakToNormalTarget = + weakToNormal + PERCEPTUAL_INTERVAL_SERIALIZATION_TOPUP; + const normalToActiveTarget = + normalToActive + PERCEPTUAL_INTERVAL_SERIALIZATION_TOPUP; + const minimumNormalContrast = Math.max( + minimumFgSurface4Contrast, + fgSurface3Contrast + weakToNormalTarget + ); + const preferredActiveContrast = Math.max( + fgSurface3Contrast, + maximumStrongContrast - endpointReserve + ); + const maximumPreferredNormalContrast = + preferredActiveContrast - normalToActiveTarget; + const maximumAbsoluteNormalContrast = + maximumStrongContrast - normalToActiveTarget; + let requestedNormalContrast: number; + if ( minimumNormalContrast <= maximumPreferredNormalContrast ) { + requestedNormalContrast = Math.max( + minimumNormalContrast, + Math.min( preferredNormalContrast, maximumPreferredNormalContrast ) ); - const candidate = - target <= previousContrast - ? previousColor - : findColorAtPerceptualContrast( { - background: displayBackground, - getColorAtLightness, - weakColor: getColorAtLightness( - get( previousColor, [ OKLCH, 'l' ] ) - ), - strongColor, - target, - } ); - - colors.set( - step.name, - meetsContrastTarget( candidate, step, ramp, backgroundRamp ) - ? candidate - : findColorAtContrastTarget( { - getColorAtLightness, - weakColor: candidate, - strongColor, - references: getConstraintReferences( - step, - ramp, - backgroundRamp - ), - target: step.contrast.target, - } ) + } else if ( minimumNormalContrast <= maximumAbsoluteNormalContrast ) { + // Spend the endpoint reserve only when the semantic intervals need it. + requestedNormalContrast = minimumNormalContrast; + } else { + // Both preferred intervals do not fit. Keep FGS4 at its WCAG floor so + // every remaining bit of perceptual range belongs to FGS5. + requestedNormalContrast = Math.min( + maximumStrongContrast, + minimumFgSurface4Contrast ); } + let normalColor = findColorAtPerceptualContrast( { + background: displayBackground, + getColorAtLightness, + weakColor: minimumFgSurface4, + strongColor: strongEndpoint, + target: requestedNormalContrast, + } ); + if ( + ! meetsContrastTarget( + normalColor, + steps.fgSurface4, + ramp, + backgroundRamp + ) + ) { + normalColor = findColorAtContrastTarget( { + getColorAtLightness, + weakColor: normalColor, + strongColor: strongEndpoint, + references: getConstraintReferences( + steps.fgSurface4, + ramp, + backgroundRamp + ), + target: steps.fgSurface4.contrast.target, + } ); + } + const normalContrast = getPerceptualContrastMagnitude( + displayBackground, + normalColor + ); + const requestedStrongContrast = Math.min( + maximumStrongContrast, + Math.max( + normalContrast + normalToActiveTarget, + preferredActiveContrast + ) + ); + let strongColor = findColorAtPerceptualContrast( { + background: displayBackground, + getColorAtLightness, + weakColor: normalColor, + strongColor: strongEndpoint, + target: requestedStrongContrast, + } ); + if ( + ! meetsContrastTarget( strongColor, strongStep, ramp, backgroundRamp ) + ) { + strongColor = findColorAtContrastTarget( { + getColorAtLightness, + weakColor: strongColor, + strongColor: strongEndpoint, + references: getConstraintReferences( + strongStep, + ramp, + backgroundRamp + ), + target: strongStep.contrast.target, + } ); + } + + colors.set( 'fgSurface3', fgSurface3 ); + colors.set( 'fgSurface4', normalColor ); + colors.set( 'fgSurface5', strongColor ); const nextRamp = { ...ramp.ramp }; for ( const step of config.steps ) { @@ -412,6 +552,16 @@ export function buildForegroundScale( target: step.contrast.target, } ); } + nextRamp.fgSurface5 = serializeColorMeetingPerceptualInterval( { + background: displayBackground, + normalColor: nextRamp.fgSurface4, + strongColor: nextRamp.fgSurface5, + strongEndpoint, + getColorAtLightness, + references: getConstraintReferences( strongStep, ramp, backgroundRamp ), + wcagTarget: strongStep.contrast.target, + perceptualTarget: normalToActive, + } ); const foregroundStepNames = new Set< keyof typeof ramp.ramp >( config.steps.map( ( { name } ) => name ) diff --git a/packages/theme/src/color-ramps/lib/build-perceptual-steps.ts b/packages/theme/src/color-ramps/lib/build-perceptual-steps.ts new file mode 100644 index 00000000000000..2836884cd9f8fb --- /dev/null +++ b/packages/theme/src/color-ramps/lib/build-perceptual-steps.ts @@ -0,0 +1,428 @@ +import { + ColorSpace, + contrastAPCA, + deltaEOK2, + get, + OKLab, + OKLCH, + OKLrab, + sRGB, + to, + type PlainColorObject, +} from 'colorjs.io/fn'; +import { clampToGamut, getColorString, getContrast } from './color-utils.ts'; +import { UNIVERSAL_CONTRAST_TOPUP } from './constants.ts'; +import type { Ramp, RampResult } from './types.ts'; +import { solveWithBisect } from './utils.ts'; + +ColorSpace.register( sRGB ); +ColorSpace.register( OKLab ); +ColorSpace.register( OKLCH ); +ColorSpace.register( OKLrab ); + +type GetColorAtLightness = ( lightness: number ) => PlainColorObject; + +const MINIMUM_CROSS_LANE_LIGHTNESS_GAP = 0.005; +// The configured 3.01 target leaves room for the 3:1 hard gate after sRGB +// serialization. The universal top-up covers the solver tolerance. +const SERIALIZED_STROKE_WCAG_TARGET = 3.01 + UNIVERSAL_CONTRAST_TOPUP; + +function clampUnitInterval( value: number ) { + return Math.max( 0, Math.min( 1, value ) ); +} + +function getPerceptualLightness( color: string | PlainColorObject ) { + return get( color, [ OKLrab, 'l' ] ); +} + +function createColorAtPerceptualLightness( + seed: string | PlainColorObject +): GetColorAtLightness { + const parsedSeed = clampToGamut( seed ); + const chroma = get( parsedSeed, [ OKLCH, 'c' ] ); + const hue = get( parsedSeed, [ OKLCH, 'h' ] ); + + return ( perceptualLightness ) => { + const oklab = to( + { + space: OKLrab, + coords: [ perceptualLightness, 0, 0 ], + alpha: 1, + }, + OKLab + ); + + return clampToGamut( { + space: OKLCH, + coords: [ get( oklab, [ OKLab, 'l' ] ), chroma, hue ], + alpha: parsedSeed.alpha, + } ); + }; +} + +function getColorForPerceptualLightness( + color: string, + perceptualLightness: number +) { + return getColorString( + createColorAtPerceptualLightness( color )( + clampUnitInterval( perceptualLightness ) + ) + ); +} + +function createAnchoredColorForStepAtPerceptualLightness( { + ramp, + step, + anchor, + startLightness, + endLightness, +}: { + ramp: Record< keyof Ramp, string >; + step: keyof Ramp; + anchor: string; + startLightness: number; + endLightness: number; +} ) { + const stepColor = to( clampToGamut( ramp[ step ] ), OKLCH ); + const getTargetColor = createColorAtPerceptualLightness( stepColor ); + const anchorColor = to( clampToGamut( anchor ), OKLCH ); + const anchorChroma = get( anchorColor, [ OKLCH, 'c' ] ); + const stepChroma = get( stepColor, [ OKLCH, 'c' ] ); + const distance = endLightness - startLightness; + + return ( perceptualLightness: number ) => { + const targetColor = to( getTargetColor( perceptualLightness ), OKLCH ); + const progress = + distance === 0 + ? 1 + : clampUnitInterval( + ( perceptualLightness - startLightness ) / distance + ); + + return clampToGamut( { + space: OKLCH, + coords: [ + get( targetColor, [ OKLCH, 'l' ] ), + anchorChroma + ( stepChroma - anchorChroma ) * progress, + get( stepColor, [ OKLCH, 'h' ] ), + ], + alpha: targetColor.alpha, + } ); + }; +} + +function findColorAtDeltaE( { + reference, + getColorAtLightness, + startLightness, + endLightness, + target, +}: { + reference: PlainColorObject; + getColorAtLightness: GetColorAtLightness; + startLightness: number; + endLightness: number; + target: number; +} ) { + const getDifference = ( color: PlainColorObject ) => + deltaEOK2( reference, color ); + const startColor = getColorAtLightness( startLightness ); + const endColor = getColorAtLightness( endLightness ); + const startDelta = getDifference( startColor ) - target; + const endDelta = getDifference( endColor ) - target; + + if ( Math.sign( startDelta ) === Math.sign( endDelta ) ) { + return Math.abs( startDelta ) <= Math.abs( endDelta ) + ? startColor + : endColor; + } + + return solveWithBisect( + getColorAtLightness, + ( color ) => getDifference( color ) - target, + startLightness, + startDelta, + endLightness, + endDelta + ); +} + +function findColorMeetingWcag( { + color, + references, + target, +}: { + color: string; + references: readonly string[]; + target: number; +} ) { + if ( + references.every( + ( reference ) => getContrast( reference, color ) >= target + ) + ) { + return color; + } + + const getColorAtLightness = createColorAtPerceptualLightness( color ); + const endpoints = [ 0, 1 ].map( ( lightness ) => { + const endpoint = getColorAtLightness( lightness ); + return { + color: endpoint, + lightness, + margin: Math.min( + ...references.map( ( reference ) => + Math.log( getContrast( reference, endpoint ) / target ) + ) + ), + }; + } ); + const strongest = endpoints.reduce( ( best, candidate ) => + candidate.margin > best.margin ? candidate : best + ); + + if ( strongest.margin < 0 ) { + return getColorString( strongest.color ); + } + + const colorLightness = getPerceptualLightness( color ); + const weakColor = getColorAtLightness( colorLightness ); + const weakMargin = Math.min( + ...references.map( ( reference ) => + Math.log( getContrast( reference, weakColor ) / target ) + ) + ); + if ( weakMargin >= 0 ) { + return getColorString( weakColor ); + } + + return getColorString( + solveWithBisect( + getColorAtLightness, + ( candidate ) => + Math.min( + ...references.map( ( reference ) => + Math.log( getContrast( reference, candidate ) / target ) + ) + ), + colorLightness, + weakMargin, + strongest.lightness, + strongest.margin + ) + ); +} + +function rebuildSurfaces( ramp: RampResult ) { + const nextRamp = { ...ramp.ramp }; + const surface1Lightness = getPerceptualLightness( ramp.ramp.surface1 ); + const surface2Lightness = getPerceptualLightness( ramp.ramp.surface2 ); + const surface3Lightness = getPerceptualLightness( ramp.ramp.surface3 ); + const authoredElevationGap = + ( surface2Lightness - + surface1Lightness + + surface3Lightness - + surface2Lightness ) / + 2; + const elevationGap = Math.max( + 0.001, + Math.min( + authoredElevationGap, + surface2Lightness - 0.001, + 1 - surface2Lightness - 0.001 + ) + ); + const targetSurface1Lightness = surface2Lightness - elevationGap; + const targetSurface3Lightness = surface2Lightness + elevationGap; + const emphasisDirection = ramp.direction === 'lighter' ? 1 : -1; + const sameDirectionElevationLightness = + ramp.direction === 'lighter' + ? targetSurface3Lightness + : targetSurface1Lightness; + const authoredSurface6Distance = Math.abs( + getPerceptualLightness( ramp.ramp.surface6 ) - surface2Lightness + ); + const maximumEmphasisDistance = + ramp.direction === 'lighter' + ? 1 - surface2Lightness + : surface2Lightness; + const minimumEmphasisDistance = + Math.abs( sameDirectionElevationLightness - surface2Lightness ) + + MINIMUM_CROSS_LANE_LIGHTNESS_GAP * 3; + const surface6Distance = Math.min( + maximumEmphasisDistance, + Math.max( authoredSurface6Distance, minimumEmphasisDistance ) + ); + const surface6Lightness = + surface2Lightness + emphasisDirection * surface6Distance; + const availableAfterElevation = Math.abs( + surface6Lightness - sameDirectionElevationLightness + ); + const crossLaneGap = Math.min( + MINIMUM_CROSS_LANE_LIGHTNESS_GAP, + availableAfterElevation / 3 + ); + const minimumSurface4Lightness = + sameDirectionElevationLightness + emphasisDirection * crossLaneGap; + const evenlySpacedSurface4Lightness = + surface2Lightness + emphasisDirection * ( surface6Distance / 3 ); + const surface4Lightness = + ramp.direction === 'lighter' + ? Math.max( + evenlySpacedSurface4Lightness, + minimumSurface4Lightness + ) + : Math.min( + evenlySpacedSurface4Lightness, + minimumSurface4Lightness + ); + const surface5Lightness = + surface4Lightness + ( surface6Lightness - surface4Lightness ) / 2; + const targetLightnesses = { + surface1: targetSurface1Lightness, + surface2: surface2Lightness, + surface3: targetSurface3Lightness, + surface4: surface4Lightness, + surface5: surface5Lightness, + surface6: surface6Lightness, + } as const; + + for ( const [ step, lightness ] of Object.entries( targetLightnesses ) ) { + nextRamp[ step as keyof typeof targetLightnesses ] = + getColorForPerceptualLightness( + ramp.ramp[ step as keyof typeof targetLightnesses ], + lightness + ); + } + + return nextRamp; +} + +function getStrokeReferences( + ramp: Record< keyof Ramp, string >, + backgroundRamp: RampResult +) { + return Array.from( + new Set( [ + ramp.surface1, + ramp.surface2, + ramp.surface3, + backgroundRamp.ramp.surface1, + backgroundRamp.ramp.surface2, + backgroundRamp.ramp.surface3, + ] ) + ); +} + +function rebuildStrokes( ramp: RampResult, backgroundRamp: RampResult ) { + const nextRamp = { ...ramp.ramp }; + const references = getStrokeReferences( nextRamp, backgroundRamp ); + nextRamp.stroke3 = findColorMeetingWcag( { + color: nextRamp.stroke3, + references, + target: SERIALIZED_STROKE_WCAG_TARGET, + } ); + + const stroke1Reference = to( clampToGamut( nextRamp.stroke1 ), OKLab ); + const stroke1To3Difference = deltaEOK2( + stroke1Reference, + nextRamp.stroke3 + ); + const authoredStroke2Difference = deltaEOK2( + stroke1Reference, + ramp.ramp.stroke2 + ); + const stroke1Lightness = getPerceptualLightness( nextRamp.stroke1 ); + const stroke3Lightness = getPerceptualLightness( nextRamp.stroke3 ); + const getStroke2AtLightness = + createAnchoredColorForStepAtPerceptualLightness( { + ramp: nextRamp, + step: 'stroke2', + anchor: nextRamp.stroke1, + startLightness: stroke1Lightness, + endLightness: stroke3Lightness, + } ); + nextRamp.stroke2 = getColorString( + findColorAtDeltaE( { + reference: stroke1Reference, + getColorAtLightness: getStroke2AtLightness, + startLightness: stroke1Lightness, + endLightness: stroke3Lightness, + target: Math.min( + authoredStroke2Difference, + stroke1To3Difference * 0.95 + ), + } ) + ); + + const stroke3Contrast = Math.abs( + contrastAPCA( nextRamp.surface3, nextRamp.stroke3 ) + ); + const stroke4Contrast = Math.abs( + contrastAPCA( nextRamp.surface3, nextRamp.stroke4 ) + ); + if ( + stroke4Contrast >= stroke3Contrast && + references.every( + ( reference ) => + getContrast( reference, nextRamp.stroke4 ) >= + SERIALIZED_STROKE_WCAG_TARGET + ) + ) { + return nextRamp; + } + + const stroke3Reference = to( clampToGamut( nextRamp.stroke3 ), OKLab ); + const minimumActiveDifference = deltaEOK2( + stroke3Reference, + ramp.ramp.stroke4 + ); + const stroke4EndLightness = ramp.direction === 'lighter' ? 1 : 0; + const getStroke4AtLightness = + createAnchoredColorForStepAtPerceptualLightness( { + ramp: nextRamp, + step: 'stroke4', + anchor: nextRamp.stroke3, + startLightness: stroke3Lightness, + endLightness: stroke4EndLightness, + } ); + const stroke4Candidate = getColorString( + findColorAtDeltaE( { + reference: stroke3Reference, + getColorAtLightness: getStroke4AtLightness, + startLightness: stroke3Lightness, + endLightness: stroke4EndLightness, + target: minimumActiveDifference, + } ) + ); + nextRamp.stroke4 = findColorMeetingWcag( { + color: stroke4Candidate, + references, + target: SERIALIZED_STROKE_WCAG_TARGET, + } ); + + return nextRamp; +} + +/** + * Rebalance surface lightness and stroke strength without replacing the + * chroma policy established by the base ramp configuration. + * + * @param ramp Base ramp to adjust. + * @param backgroundRamp Background surfaces used by accent strokes. + */ +export function buildPerceptualSteps( + ramp: RampResult, + backgroundRamp?: RampResult +): RampResult { + const surfaceResult = { + ...ramp, + ramp: rebuildSurfaces( ramp ), + }; + + return { + ...surfaceResult, + ramp: rebuildStrokes( surfaceResult, backgroundRamp ?? surfaceResult ), + }; +} diff --git a/packages/theme/src/color-ramps/lib/default-ramps.ts b/packages/theme/src/color-ramps/lib/default-ramps.ts index 044df57607a700..3d37e464189139 100644 --- a/packages/theme/src/color-ramps/lib/default-ramps.ts +++ b/packages/theme/src/color-ramps/lib/default-ramps.ts @@ -12,12 +12,12 @@ export const DEFAULT_RAMPS: Record< bgFill2: '#484848', fgFill: '#f0f0f0', surface6: '#d6d6d6', - surface5: '#e6e6e6', - surface4: '#ededed', + surface5: '#e3e3e3', + surface4: '#efefef', surface3: '#fff', - fgSurface5: '#1e1e1e', - fgSurface4: '#646464', - fgSurface3: '#707070', + fgSurface5: '#141414', + fgSurface4: '#505050', + fgSurface3: '#737373', fgSurface2: '#8d8d8d', fgSurface1: '#aeaeae', stroke3: '#8d8d8d', @@ -29,7 +29,7 @@ export const DEFAULT_RAMPS: Record< bgFillInverted2: '#1e1e1e', bgFillInverted1: '#2d2d2d', fgFillInverted: '#f0f0f0', - surface1: '#f4f4f4', + surface1: '#f9f9f9', }, direction: 'darker', }, @@ -43,9 +43,9 @@ export const DEFAULT_RAMPS: Record< surface5: '#dce3f4', surface4: '#e5eaf7', surface3: '#fff', - fgSurface5: '#0b0070', - fgSurface4: '#3351e8', - fgSurface3: '#3e60ea', + fgSurface5: '#03063b', + fgSurface4: '#222eda', + fgSurface3: '#3f61ea', fgSurface2: '#5a82ff', fgSurface1: '#86a9ff', stroke3: '#3858e9', @@ -57,7 +57,7 @@ export const DEFAULT_RAMPS: Record< bgFillInverted2: '#1a1e27', bgFillInverted1: '#13009f', fgFillInverted: '#eff0f2', - surface1: '#ebf0fc', + surface1: '#edf1fc', }, direction: 'darker', }, @@ -71,9 +71,9 @@ export const DEFAULT_RAMPS: Record< surface5: '#d3e4f7', surface4: '#deebfa', surface3: '#fff', - fgSurface5: '#001b4f', - fgSurface4: '#0063b2', - fgSurface3: '#006ec6', + fgSurface5: '#00132a', + fgSurface4: '#005092', + fgSurface3: '#0070c8', fgSurface2: '#008bfa', fgSurface1: '#59b0ff', stroke3: '#006bd7', @@ -180,9 +180,9 @@ export const DEFAULT_RAMPS: Record< surface5: '#f7dbd7', surface4: '#fae5e1', surface3: '#fff', - fgSurface5: '#470000', - fgSurface4: '#be1616', - fgSurface3: '#d71a1a', + fgSurface5: '#260101', + fgSurface4: '#980f0f', + fgSurface3: '#d91a1a', fgSurface2: '#f74c40', fgSurface1: '#ff8879', stroke3: '#cc1818', @@ -190,11 +190,11 @@ export const DEFAULT_RAMPS: Record< stroke2: '#d99a91', stroke1: '#e0afa7', bgFillDark: '#231c1b', - fgFillDark: '#f3efef', + fgFillDark: '#f2efef', bgFillInverted2: '#231c1b', bgFillInverted1: '#640000', - fgFillInverted: '#f3efef', - surface1: '#feebe8', + fgFillInverted: '#f2efef', + surface1: '#feeeeb', }, direction: 'darker', }, diff --git a/packages/theme/src/color-ramps/lib/index.ts b/packages/theme/src/color-ramps/lib/index.ts index f1577f7c37eb61..42fb1921d9e1d2 100644 --- a/packages/theme/src/color-ramps/lib/index.ts +++ b/packages/theme/src/color-ramps/lib/index.ts @@ -7,6 +7,7 @@ import { } from './color-utils.ts'; import { findColorMeetingRequirements } from './find-color-with-constraints.ts'; import { buildForegroundScale } from './build-foreground-scale.ts'; +import { buildPerceptualSteps } from './build-perceptual-steps.ts'; import { sortByDependency, computeBetterFgColorDirection, @@ -338,11 +339,14 @@ export function buildRamp( bestRamp.surface3 = tmpSurface1; } - const rampResult = { - ramp: bestRamp, - warnings: bestWarnings, - direction: mainDir, - }; + const rampResult = buildPerceptualSteps( + { + ramp: bestRamp, + warnings: bestWarnings, + direction: mainDir, + }, + backgroundRamp + ); return buildForegroundScale( rampResult, diff --git a/packages/theme/src/color-ramps/lib/ramp-configs.ts b/packages/theme/src/color-ramps/lib/ramp-configs.ts index 29be4c952b5170..cf7163f0141574 100644 --- a/packages/theme/src/color-ramps/lib/ramp-configs.ts +++ b/packages/theme/src/color-ramps/lib/ramp-configs.ts @@ -39,9 +39,9 @@ const ACCENT_SURFACE_TAPER_CHROMA: TaperChromaOptions = { radiusLight: 0.01, }; -const fgSurface5Config: RampStepConfig = { - // Keep the former fgSurface4 endpoint in the base solver. The foreground - // scale pass exposes it as fgSurface5 and inserts a new state step before it. +// Keep the existing inverted active-fill behavior independent from the new +// foreground endpoint policy. +const highContrastInvertedFillConfig: RampStepConfig = { contrast: { reference: 'surface3', followDirection: 'main', @@ -52,24 +52,46 @@ const fgSurface5Config: RampStepConfig = { taperChromaOptions: FG_TAPER_CHROMA, }; +const fgSurface5Config: RampStepConfig = { + // The foreground scale pass derives the strong endpoint from APCA targets. + // The base solver only needs to establish the WCAG floor and direction. + contrast: { + reference: 'surface3', + followDirection: 'main', + target: 4.5, + preferLighter: true, + }, + taperChromaOptions: FG_TAPER_CHROMA, +}; + +const FOREGROUND_PERCEPTUAL_TARGETS = { + // These are design targets, not accessibility thresholds. WCAG ratios remain + // the hard gates for every surface on which a foreground can appear. + // Prefer strong normal text, but lower it when that is necessary to preserve + // a visible active state before the gamut endpoint. + normalContrast: 86, + // Let the interaction-state endpoint approach black or white when that makes + // the state change more visible. WCAG remains a hard floor. + endpointReserve: 1, + // Keep weak supporting content visibly separate from normal content, then + // reserve a larger interval for the interaction-state-only endpoint. + weakToNormal: 12, + normalToActive: 14, +} as const; + const FOREGROUND_SCALE_STEPS = [ { name: 'fgSurface1', - progress: 0, preserveAnchor: true, contrast: { references: [ 'surface3' ], target: 2 }, }, { name: 'fgSurface2', - progress: 0.2, preserveAnchor: true, contrast: { references: [ 'surface3' ], target: 3 }, }, { name: 'fgSurface3', - // APCA positions this step at 40% of the visible contrast range. WCAG - // contrast remains the hard minimum against every listed surface. - progress: 0.4, contrast: { references: [ 'surface1', 'surface2', 'surface3' ], target: 4.5, @@ -77,8 +99,6 @@ const FOREGROUND_SCALE_STEPS = [ }, { name: 'fgSurface4', - // Leave the final 40% of the visible contrast range for the active state. - progress: 0.6, contrast: { references: [ 'surface1', @@ -92,8 +112,6 @@ const FOREGROUND_SCALE_STEPS = [ }, { name: 'fgSurface5', - progress: 1, - preserveAnchor: true, contrast: { references: [ 'surface1', @@ -180,7 +198,7 @@ const BG_RAMP_STEPS: RampConfig[ 'steps' ] = { target: 1.2, }, }, - bgFillInverted2: fgSurface5Config, + bgFillInverted2: highContrastInvertedFillConfig, bgFillDark: { contrast: { reference: 'surface3', @@ -302,6 +320,7 @@ export const BG_RAMP_CONFIG: RampConfig = { foregroundScale: { seed: 'surface2', perceptualReference: 'surface2', + perceptualTargets: FOREGROUND_PERCEPTUAL_TARGETS, chroma: { mode: 'tapered', options: FG_TAPER_CHROMA, @@ -392,6 +411,7 @@ export const ACCENT_RAMP_CONFIG: RampConfig = { foregroundScale: { seed: 'bgFill1', perceptualReference: 'surface2', + perceptualTargets: FOREGROUND_PERCEPTUAL_TARGETS, chroma: { mode: 'gamut-relative' }, steps: FOREGROUND_SCALE_STEPS, }, diff --git a/packages/theme/src/color-ramps/lib/types.ts b/packages/theme/src/color-ramps/lib/types.ts index 6c29554a4a57cb..7f514eca886e41 100644 --- a/packages/theme/src/color-ramps/lib/types.ts +++ b/packages/theme/src/color-ramps/lib/types.ts @@ -94,6 +94,16 @@ export type ForegroundScaleConfig = { seed: keyof Ramp; /** Background step used to measure the APCA contrast range. */ perceptualReference: keyof Ramp; + perceptualTargets: { + /** Preferred APCA contrast for normal content and resting controls. */ + normalContrast: number; + /** APCA contrast left unused unless the foreground intervals need it. */ + endpointReserve: number; + /** Minimum APCA interval from weak to normal emphasis. */ + weakToNormal: number; + /** Minimum APCA interval from a resting control to its active state. */ + normalToActive: number; + }; chroma: | { mode: 'tapered'; @@ -104,8 +114,6 @@ export type ForegroundScaleConfig = { }; steps: readonly { name: ForegroundRampStep; - /** Position in the APCA contrast range from the first to last step. */ - progress: number; /** Preserve the base solver's color when it meets every WCAG floor. */ preserveAnchor?: boolean; contrast: { diff --git a/packages/theme/src/color-ramps/stories/perceptual-ramp-experiment.ts b/packages/theme/src/color-ramps/stories/perceptual-ramp-experiment.ts deleted file mode 100644 index 7b3a2b74e9cbc3..00000000000000 --- a/packages/theme/src/color-ramps/stories/perceptual-ramp-experiment.ts +++ /dev/null @@ -1,1092 +0,0 @@ -import { - ColorSpace, - contrastAPCA, - deltaEOK2, - get, - OKLab, - OKLCH, - OKLCH_sRGB as OklchSrgb, - OKLrab, - sRGB, - to, - type PlainColorObject, -} from 'colorjs.io/fn'; -import { buildAccentRamp, buildBgRamp } from '../index.ts'; -import { buildForegroundScale } from '../lib/build-foreground-scale'; -import { clampToGamut, getColorString, getContrast } from '../lib/color-utils'; -import { - DEFAULT_SEED_COLORS, - UNIVERSAL_CONTRAST_TOPUP, -} from '../lib/constants'; -import { ACCENT_RAMP_CONFIG, BG_RAMP_CONFIG } from '../lib/ramp-configs'; -import { taperChroma } from '../lib/taper-chroma'; -import type { Ramp, RampResult } from '../lib/types'; -import { buildRamp } from '../lib/index'; -import { - clampAccentScaleReferenceLightness, - solveWithBisect, -} from '../lib/utils'; - -ColorSpace.register( sRGB ); -ColorSpace.register( OKLab ); -ColorSpace.register( OKLCH ); -ColorSpace.register( OklchSrgb ); -ColorSpace.register( OKLrab ); - -export const EXPERIMENTAL_RAMP_METHODS = [ - 'anchored', - 'constrained-perceptual', - 'apca-all', - 'role-hybrid', - 'pinned-role-hybrid', -] as const; - -export type ExperimentalRampMethod = - ( typeof EXPERIMENTAL_RAMP_METHODS )[ number ]; - -export type ExperimentalThemeRamps = { - background: RampResult; - primary: RampResult; - error: RampResult; -}; - -type RampFamily = 'neutral' | 'accent'; -type PerceptualMetric = 'apca' | 'delta-e'; -type ChromaMode = 'absolute' | 'relative' | 'tapered'; -type GetColorAtLightness = ( lightness: number ) => PlainColorObject; - -const SURFACE_STEPS = [ 'surface4', 'surface5', 'surface6' ] as const; -const PINNED_SURFACE_STEPS = [ - 'surface1', - 'surface3', - 'surface4', - 'surface5', - 'surface6', -] as const; -const PINNED_SURFACE_PROGRESS = [ 0.15, 0.3, 0.55, 0.75, 1 ] as const; -const STROKE_STEPS = [ 'stroke1', 'stroke2', 'stroke3', 'stroke4' ] as const; -const SURFACE_PROGRESS = [ 1 / 3, 2 / 3, 1 ] as const; -const STROKE_PROGRESS = [ 0, 1 / 3, 2 / 3, 1 ] as const; -const MINIMUM_ACTIVE_FILL_DELTA_E = 0.055; -const MINIMUM_ACTIVE_FILL_APCA = 12; -const SERIALIZED_FILL_WCAG_TARGET = BG_RAMP_CONFIG.steps.fgFill.contrast.target; -const SERIALIZED_ACTIVE_FILL_WCAG_TARGET = - BG_RAMP_CONFIG.steps.bgFill2.contrast.target + UNIVERSAL_CONTRAST_TOPUP; -const SERIALIZED_STROKE_WCAG_TARGET = - BG_RAMP_CONFIG.steps.stroke3.contrast.target + UNIVERSAL_CONTRAST_TOPUP; -const SERIALIZED_SURFACE_FOREGROUND_WCAG_TARGET = - 4.5 + UNIVERSAL_CONTRAST_TOPUP; - -function getMetric( - metric: PerceptualMetric, - reference: string | PlainColorObject, - color: string | PlainColorObject -) { - return metric === 'apca' - ? Math.abs( contrastAPCA( reference, color ) ) - : deltaEOK2( reference, color ); -} - -function prepareMetricReference( - metric: PerceptualMetric, - reference: string | PlainColorObject -) { - return to( clampToGamut( reference ), metric === 'apca' ? sRGB : OKLab ); -} - -function createColorAtLightness( - seed: string | PlainColorObject, - mode: ChromaMode, - taperOptions = BG_RAMP_CONFIG.steps.surface4.taperChromaOptions -): GetColorAtLightness { - const parsedSeed = clampToGamut( seed ); - - if ( mode === 'relative' ) { - const relativeSeed = to( parsedSeed, OklchSrgb ); - const chroma = get( relativeSeed, [ OklchSrgb, 'c' ] ); - const hue = get( relativeSeed, [ OklchSrgb, 'h' ] ); - return ( lightness ) => - to( - { - space: OklchSrgb, - coords: [ lightness, chroma, hue ], - alpha: parsedSeed.alpha, - }, - OKLCH - ); - } - - if ( mode === 'tapered' ) { - return ( lightness ) => { - const tapered = taperChroma( parsedSeed, lightness, taperOptions ); - if ( 'l' in tapered && 'c' in tapered ) { - return clampToGamut( { - space: OKLCH, - coords: [ - tapered.l, - tapered.c, - get( parsedSeed, [ OKLCH, 'h' ] ), - ], - alpha: parsedSeed.alpha, - } ); - } - return clampToGamut( tapered ); - }; - } - - return ( lightness ) => - clampToGamut( { - space: OKLCH, - coords: [ - lightness, - get( parsedSeed, [ OKLCH, 'c' ] ), - get( parsedSeed, [ OKLCH, 'h' ] ), - ], - alpha: parsedSeed.alpha, - } ); -} - -function getPerceptualLightness( color: string | PlainColorObject ) { - return get( color, [ OKLrab, 'l' ] ); -} - -function createColorAtPerceptualLightness( - seed: string | PlainColorObject, - mode: ChromaMode, - taperOptions?: Parameters< typeof taperChroma >[ 2 ] -): GetColorAtLightness { - const getColorAtLightness = createColorAtLightness( - seed, - mode, - taperOptions - ); - - return ( perceptualLightness ) => { - const oklab = to( - { - space: OKLrab, - coords: [ perceptualLightness, 0, 0 ], - alpha: 1, - }, - OKLab - ); - return getColorAtLightness( get( oklab, [ OKLab, 'l' ] ) ); - }; -} - -function createAnchoredColorForStepAtPerceptualLightness( { - ramp, - step, - anchor, - startLightness, - endLightness, -}: { - ramp: Record< keyof Ramp, string >; - step: keyof Ramp; - anchor: string; - startLightness: number; - endLightness: number; -} ) { - const stepColor = to( clampToGamut( ramp[ step ] ), OKLCH ); - const getTargetColor = createColorAtPerceptualLightness( - stepColor, - 'absolute' - ); - const anchorColor = to( clampToGamut( anchor ), OKLCH ); - const anchorChroma = get( anchorColor, [ OKLCH, 'c' ] ); - const stepChroma = get( stepColor, [ OKLCH, 'c' ] ); - const distance = endLightness - startLightness; - - return ( perceptualLightness: number ) => { - const targetColor = to( getTargetColor( perceptualLightness ), OKLCH ); - const progress = - distance === 0 - ? 1 - : Math.min( - 1, - Math.max( - 0, - ( perceptualLightness - startLightness ) / distance - ) - ); - - return clampToGamut( { - space: OKLCH, - coords: [ - get( targetColor, [ OKLCH, 'l' ] ), - anchorChroma + ( stepChroma - anchorChroma ) * progress, - get( stepColor, [ OKLCH, 'h' ] ), - ], - alpha: targetColor.alpha, - } ); - }; -} - -function getClosestBoundary( - target: number, - first: { color: PlainColorObject; value: number }, - second: { color: PlainColorObject; value: number } -) { - return Math.abs( first.value - target ) <= Math.abs( second.value - target ) - ? first.color - : second.color; -} - -function findColorAtMetric( { - metric, - reference, - getColorAtLightness, - startLightness, - endLightness, - target, -}: { - metric: PerceptualMetric; - reference: string | PlainColorObject; - getColorAtLightness: GetColorAtLightness; - startLightness: number; - endLightness: number; - target: number; -} ) { - const startColor = getColorAtLightness( startLightness ); - const endColor = getColorAtLightness( endLightness ); - const startValue = getMetric( metric, reference, startColor ); - const endValue = getMetric( metric, reference, endColor ); - const lower = { color: startColor, value: startValue }; - const upper = { color: endColor, value: endValue }; - const minimumValue = Math.min( startValue, endValue ); - const maximumValue = Math.max( startValue, endValue ); - - if ( target <= minimumValue || target >= maximumValue ) { - return getClosestBoundary( target, lower, upper ); - } - - const startDelta = startValue - target; - const endDelta = endValue - target; - if ( Math.sign( startDelta ) === Math.sign( endDelta ) ) { - return getClosestBoundary( target, lower, upper ); - } - - return solveWithBisect( - getColorAtLightness, - ( color ) => getMetric( metric, reference, color ) - target, - startLightness, - startDelta, - endLightness, - endDelta - ); -} - -function getRampChromaMode( - method: Exclude< ExperimentalRampMethod, 'anchored' >, - family: RampFamily -): ChromaMode { - if ( family === 'neutral' ) { - return 'tapered'; - } - return method === 'role-hybrid' || method === 'pinned-role-hybrid' - ? 'relative' - : 'absolute'; -} - -function rebuildProgression( { - ramp, - referenceName, - steps, - progress, - metric, - seed, - mode, - taperOptions, -}: { - ramp: Record< keyof Ramp, string >; - referenceName: keyof Ramp; - steps: readonly ( keyof Ramp )[]; - progress: readonly number[]; - metric: PerceptualMetric; - seed: string; - mode: ChromaMode; - taperOptions?: Parameters< typeof taperChroma >[ 2 ]; -} ) { - const nextRamp = { ...ramp }; - const reference = prepareMetricReference( metric, ramp[ referenceName ] ); - const firstStep = steps[ 0 ]; - const lastStep = steps.at( -1 )!; - const startLightness = get( ramp[ firstStep ], [ OKLCH, 'l' ] ); - const endLightness = get( ramp[ lastStep ], [ OKLCH, 'l' ] ); - const seedForScale = mode === 'absolute' ? ramp[ lastStep ] : seed; - const getColorAtLightness = createColorAtLightness( - seedForScale, - mode, - taperOptions - ); - const startValue = getMetric( - metric, - reference, - getColorAtLightness( startLightness ) - ); - const endValue = getMetric( - metric, - reference, - getColorAtLightness( endLightness ) - ); - - steps.forEach( ( step, index ) => { - const target = - startValue + ( endValue - startValue ) * progress[ index ]; - nextRamp[ step ] = getColorString( - findColorAtMetric( { - metric, - reference, - getColorAtLightness, - startLightness, - endLightness, - target, - } ) - ); - } ); - - return nextRamp; -} - -function findColorMeetingWcag( { - color, - references, - target, - seed, - mode, -}: { - color: string; - references: readonly string[]; - target: number; - seed: string; - mode: ChromaMode; -} ) { - if ( - references.every( - ( reference ) => getContrast( reference, color ) >= target - ) - ) { - return color; - } - - const getColorAtLightness = createColorAtLightness( seed, mode ); - const endpoints = [ 0, 1 ].map( ( lightness ) => { - const endpoint = getColorAtLightness( lightness ); - return { - color: endpoint, - lightness, - margin: Math.min( - ...references.map( ( reference ) => - Math.log( getContrast( reference, endpoint ) / target ) - ) - ), - }; - } ); - const strongest = endpoints.reduce( ( best, candidate ) => - candidate.margin > best.margin ? candidate : best - ); - - if ( strongest.margin < 0 ) { - return getColorString( strongest.color ); - } - const colorLightness = get( color, [ OKLCH, 'l' ] ); - const weakColor = getColorAtLightness( colorLightness ); - const weakMargin = Math.min( - ...references.map( ( reference ) => - Math.log( getContrast( reference, weakColor ) / target ) - ) - ); - if ( weakMargin >= 0 ) { - return getColorString( weakColor ); - } - - return getColorString( - solveWithBisect( - getColorAtLightness, - ( candidate ) => - Math.min( - ...references.map( ( reference ) => - Math.log( getContrast( reference, candidate ) / target ) - ) - ), - colorLightness, - weakMargin, - strongest.lightness, - strongest.margin - ) - ); -} - -function rebuildConstrainedSurfaces( { ramp }: { ramp: RampResult } ) { - const nextRamp = { ...ramp.ramp }; - const surfaceReference = prepareMetricReference( - 'delta-e', - ramp.ramp.surface2 - ); - const elevationTarget = Math.max( - getMetric( 'delta-e', surfaceReference, ramp.ramp.surface1 ), - getMetric( 'delta-e', surfaceReference, ramp.ramp.surface3 ) - ); - const surface2Lightness = getPerceptualLightness( ramp.ramp.surface2 ); - - for ( const step of [ 'surface1', 'surface3' ] as const ) { - const endLightness = step === 'surface1' ? 0 : 1; - const getColorAtLightness = createColorAtPerceptualLightness( - ramp.ramp[ step ], - 'absolute' - ); - nextRamp[ step ] = getColorString( - findColorAtMetric( { - metric: 'delta-e', - reference: surfaceReference, - getColorAtLightness, - startLightness: surface2Lightness, - endLightness, - target: elevationTarget, - } ) - ); - } - - const surface6Difference = getMetric( - 'delta-e', - surfaceReference, - ramp.ramp.surface6 - ); - const surface6Lightness = getPerceptualLightness( ramp.ramp.surface6 ); - for ( const [ index, step ] of SURFACE_STEPS.entries() ) { - if ( step === 'surface6' ) { - continue; - } - const getColorAtLightness = createColorAtPerceptualLightness( - ramp.ramp[ step ], - 'absolute' - ); - const minimumDifference = getMetric( - 'delta-e', - surfaceReference, - getColorAtLightness( surface2Lightness ) - ); - nextRamp[ step ] = getColorString( - findColorAtMetric( { - metric: 'delta-e', - reference: surfaceReference, - getColorAtLightness, - startLightness: surface2Lightness, - endLightness: surface6Lightness, - target: - minimumDifference + - ( surface6Difference - minimumDifference ) * - SURFACE_PROGRESS[ index ], - } ) - ); - } - - return nextRamp; -} - -function rebuildConstrainedStrokes( { - ramp, - backgroundRamp, -}: { - ramp: RampResult; - backgroundRamp: RampResult; -} ) { - const nextRamp = { ...ramp.ramp }; - const references = getStrokeReferences( nextRamp, backgroundRamp ); - - // ST3 is the accessibility boundary. Preserve the production color whenever - // it meets every required 3:1 contrast pair. - nextRamp.stroke3 = findColorMeetingWcag( { - color: nextRamp.stroke3, - references, - target: SERIALIZED_STROKE_WCAG_TARGET, - seed: nextRamp.stroke3, - mode: 'absolute', - } ); - - const stroke1Reference = prepareMetricReference( - 'delta-e', - nextRamp.stroke1 - ); - const stroke1To3Difference = getMetric( - 'delta-e', - stroke1Reference, - nextRamp.stroke3 - ); - const authoredStroke2Difference = getMetric( - 'delta-e', - stroke1Reference, - ramp.ramp.stroke2 - ); - const stroke1Lightness = getPerceptualLightness( nextRamp.stroke1 ); - const stroke3Lightness = getPerceptualLightness( nextRamp.stroke3 ); - const getStroke2AtLightness = - createAnchoredColorForStepAtPerceptualLightness( { - ramp: nextRamp, - step: 'stroke2', - anchor: nextRamp.stroke1, - startLightness: stroke1Lightness, - endLightness: stroke3Lightness, - } ); - nextRamp.stroke2 = getColorString( - findColorAtMetric( { - metric: 'delta-e', - reference: stroke1Reference, - getColorAtLightness: getStroke2AtLightness, - startLightness: stroke1Lightness, - endLightness: stroke3Lightness, - target: Math.min( - authoredStroke2Difference, - stroke1To3Difference * 0.95 - ), - } ) - ); - const stroke3Contrast = Math.abs( - contrastAPCA( nextRamp.surface3, nextRamp.stroke3 ) - ); - const stroke4Contrast = Math.abs( - contrastAPCA( nextRamp.surface3, nextRamp.stroke4 ) - ); - if ( - stroke4Contrast >= stroke3Contrast && - references.every( - ( reference ) => - getContrast( reference, nextRamp.stroke4 ) >= - SERIALIZED_STROKE_WCAG_TARGET - ) - ) { - return nextRamp; - } - - const stroke3Reference = prepareMetricReference( - 'delta-e', - nextRamp.stroke3 - ); - const minimumActiveDifference = getMetric( - 'delta-e', - stroke3Reference, - ramp.ramp.stroke4 - ); - const stroke4EndLightness = ramp.direction === 'lighter' ? 1 : 0; - const getStroke4AtLightness = - createAnchoredColorForStepAtPerceptualLightness( { - ramp: nextRamp, - step: 'stroke4', - anchor: nextRamp.stroke3, - startLightness: stroke3Lightness, - endLightness: stroke4EndLightness, - } ); - const stroke4Candidate = getColorString( - findColorAtMetric( { - metric: 'delta-e', - reference: stroke3Reference, - getColorAtLightness: getStroke4AtLightness, - startLightness: stroke3Lightness, - endLightness: stroke4EndLightness, - target: minimumActiveDifference, - } ) - ); - nextRamp.stroke4 = findColorMeetingWcag( { - color: stroke4Candidate, - references, - target: SERIALIZED_STROKE_WCAG_TARGET, - seed: stroke4Candidate, - mode: 'absolute', - } ); - - return nextRamp; -} - -function darkenUntilContrast( { - color, - reference, - seed, - mode, - target, -}: { - color: string; - reference: string; - seed: string; - mode: ChromaMode; - target: number; -} ) { - if ( getContrast( reference, color ) >= target ) { - return color; - } - - const getColorAtLightness = createColorAtLightness( seed, mode ); - const endpoint = getColorAtLightness( 0 ); - const endpointDelta = Math.log( - getContrast( reference, endpoint ) / target - ); - if ( endpointDelta < 0 ) { - return getColorString( endpoint ); - } - const colorLightness = get( color, [ OKLCH, 'l' ] ); - const weakColor = getColorAtLightness( colorLightness ); - const colorDelta = Math.log( getContrast( reference, weakColor ) / target ); - if ( colorDelta >= 0 ) { - return getColorString( weakColor ); - } - - return getColorString( - solveWithBisect( - getColorAtLightness, - ( candidate ) => - Math.log( getContrast( reference, candidate ) / target ), - colorLightness, - colorDelta, - 0, - endpointDelta - ) - ); -} - -function rebuildElevationPair( { - ramp, - metric, - seed, - mode, -}: { - ramp: Record< keyof Ramp, string >; - metric: PerceptualMetric; - seed: string; - mode: ChromaMode; -} ) { - const nextRamp = { ...ramp }; - const reference = ramp.surface2; - const metricReference = prepareMetricReference( metric, reference ); - const target = Math.max( - getMetric( metric, metricReference, ramp.surface1 ), - getMetric( metric, metricReference, ramp.surface3 ) - ); - - for ( const step of [ 'surface1', 'surface3' ] as const ) { - const original = ramp[ step ]; - const getColorAtLightness = createColorAtLightness( - mode === 'absolute' ? original : seed, - mode, - BG_RAMP_CONFIG.steps[ step ].taperChromaOptions - ); - nextRamp[ step ] = getColorString( - findColorAtMetric( { - metric, - reference: metricReference, - getColorAtLightness, - startLightness: get( reference, [ OKLCH, 'l' ] ), - endLightness: get( original, [ OKLCH, 'l' ] ), - target, - } ) - ); - } - - return nextRamp; -} - -function rebuildPinnedSurfaceLane( { - ramp, - direction, - seed, - mode, -}: { - ramp: Record< keyof Ramp, string >; - direction: RampResult[ 'direction' ]; - seed: string; - mode: ChromaMode; -} ) { - const getColorAtLightness = createColorAtLightness( seed, mode ); - const foregroundEndpoint = getColorAtLightness( - direction === 'lighter' ? 1 : 0 - ); - const constrainedSurfaces = [ - 'surface1', - 'surface2', - 'surface3', - 'surface4', - 'surface5', - ] as const; - - if ( - constrainedSurfaces.every( - ( step ) => - getContrast( ramp[ step ], foregroundEndpoint ) >= - SERIALIZED_SURFACE_FOREGROUND_WCAG_TARGET - ) - ) { - return ramp; - } - - const reference = prepareMetricReference( 'delta-e', ramp.surface2 ); - const startLightness = get( ramp.surface2, [ OKLCH, 'l' ] ); - const endLightness = direction === 'lighter' ? 0 : 1; - const maximumDifference = Math.max( - ...PINNED_SURFACE_STEPS.map( ( step ) => - getMetric( 'delta-e', reference, ramp[ step ] ) - ) - ); - const nextRamp = { ...ramp }; - - PINNED_SURFACE_STEPS.forEach( ( step, index ) => { - nextRamp[ step ] = getColorString( - findColorAtMetric( { - metric: 'delta-e', - reference, - getColorAtLightness, - startLightness, - endLightness, - target: maximumDifference * PINNED_SURFACE_PROGRESS[ index ], - } ) - ); - } ); - - return nextRamp; -} - -function findColorAtWcagContrast( { - reference, - getColorAtLightness, - startLightness, - endLightness, - target, -}: { - reference: string; - getColorAtLightness: GetColorAtLightness; - startLightness: number; - endLightness: number; - target: number; -} ) { - const startColor = getColorAtLightness( startLightness ); - const startDelta = Math.log( - getContrast( reference, startColor ) / target - ); - - if ( startDelta >= 0 ) { - return startColor; - } - - const endColor = getColorAtLightness( endLightness ); - const endDelta = Math.log( getContrast( reference, endColor ) / target ); - if ( endDelta < 0 ) { - return endColor; - } - - return solveWithBisect( - getColorAtLightness, - ( color ) => Math.log( getContrast( reference, color ) / target ), - startLightness, - startDelta, - endLightness, - endDelta - ); -} - -function createRoomForDarkerActiveFill( { - ramp, - seed, - mode, -}: { - ramp: Record< keyof Ramp, string >; - seed: string; - mode: ChromaMode; -} ) { - if ( - getContrast( ramp.bgFill1, ramp.bgFill2 ) >= - SERIALIZED_ACTIVE_FILL_WCAG_TARGET - ) { - return ramp; - } - - const getColorAtLightness = createColorAtLightness( seed, mode ); - const nextRamp = { ...ramp }; - nextRamp.bgFill1 = getColorString( - findColorAtWcagContrast( { - reference: ramp.surface2, - getColorAtLightness, - startLightness: get( ramp.surface2, [ OKLCH, 'l' ] ), - endLightness: 1, - target: - BG_RAMP_CONFIG.steps.bgFill1.contrast.target + - UNIVERSAL_CONTRAST_TOPUP, - } ) - ); - nextRamp.bgFill2 = nextRamp.bgFill1; - - return nextRamp; -} - -function rebuildActiveFill( { - ramp, - method, - seed, - mode, -}: { - ramp: Record< keyof Ramp, string >; - method: Exclude< ExperimentalRampMethod, 'anchored' >; - seed: string; - mode: ChromaMode; -} ) { - const nextRamp = { ...ramp }; - const metric: PerceptualMetric = method === 'apca-all' ? 'apca' : 'delta-e'; - const metricReference = prepareMetricReference( metric, ramp.bgFill1 ); - const currentMetric = getMetric( metric, metricReference, ramp.bgFill2 ); - const target = Math.max( - currentMetric, - metric === 'apca' - ? MINIMUM_ACTIVE_FILL_APCA - : MINIMUM_ACTIVE_FILL_DELTA_E - ); - const getColorAtLightness = createColorAtLightness( - mode === 'absolute' ? ramp.bgFill2 : seed, - mode - ); - const candidate = getColorString( - findColorAtMetric( { - metric, - reference: metricReference, - getColorAtLightness, - startLightness: get( ramp.bgFill1, [ OKLCH, 'l' ] ), - endLightness: 0, - target, - } ) - ); - nextRamp.bgFill2 = darkenUntilContrast( { - color: candidate, - reference: ramp.bgFill1, - seed: mode === 'absolute' ? ramp.bgFill2 : seed, - mode, - // Leave enough room for 8-bit sRGB serialization. - target: SERIALIZED_ACTIVE_FILL_WCAG_TARGET, - } ); - nextRamp.fgFill = findColorMeetingWcag( { - color: ramp.fgFill, - references: [ nextRamp.bgFill1, nextRamp.bgFill2 ], - target: SERIALIZED_FILL_WCAG_TARGET, - seed: ramp.fgFill, - mode: 'tapered', - } ); - - return nextRamp; -} - -function getStrokeReferences( - ramp: Record< keyof Ramp, string >, - backgroundRamp: RampResult -) { - return [ - ramp.surface1, - ramp.surface2, - ramp.surface3, - backgroundRamp.ramp.surface1, - backgroundRamp.ramp.surface2, - backgroundRamp.ramp.surface3, - ]; -} - -function rebuildRamp( { - ramp, - backgroundRamp, - method, - family, - seed, -}: { - ramp: RampResult; - backgroundRamp: RampResult; - method: Exclude< ExperimentalRampMethod, 'anchored' >; - family: RampFamily; - seed: string; -} ) { - const config = family === 'neutral' ? BG_RAMP_CONFIG : ACCENT_RAMP_CONFIG; - - if ( method === 'constrained-perceptual' ) { - let nextRamp = rebuildConstrainedSurfaces( { - ramp, - } ); - const surfaceResult: RampResult = { - ...ramp, - ramp: nextRamp, - warnings: undefined, - }; - nextRamp = rebuildConstrainedStrokes( { - ramp: surfaceResult, - backgroundRamp, - } ); - nextRamp = rebuildActiveFill( { - ramp: nextRamp, - method, - seed, - mode: getRampChromaMode( method, family ), - } ); - - return buildForegroundScale( - { - ...ramp, - ramp: nextRamp, - warnings: undefined, - }, - backgroundRamp, - config.foregroundScale - ); - } - - const metric: PerceptualMetric = method === 'apca-all' ? 'apca' : 'delta-e'; - const mode = getRampChromaMode( method, family ); - let nextRamp = rebuildElevationPair( { - ramp: ramp.ramp, - metric, - seed, - mode, - } ); - nextRamp = rebuildProgression( { - ramp: nextRamp, - referenceName: 'surface2', - steps: SURFACE_STEPS, - progress: SURFACE_PROGRESS, - metric, - seed, - mode, - taperOptions: BG_RAMP_CONFIG.steps.surface4.taperChromaOptions, - } ); - if ( method === 'pinned-role-hybrid' ) { - nextRamp = rebuildPinnedSurfaceLane( { - ramp: nextRamp, - direction: ramp.direction, - seed, - mode, - } ); - } - nextRamp = rebuildProgression( { - ramp: nextRamp, - referenceName: 'surface3', - steps: STROKE_STEPS, - progress: STROKE_PROGRESS, - metric, - seed, - mode, - taperOptions: BG_RAMP_CONFIG.steps.stroke3.taperChromaOptions, - } ); - nextRamp.stroke3 = findColorMeetingWcag( { - color: nextRamp.stroke3, - references: getStrokeReferences( nextRamp, backgroundRamp ), - target: SERIALIZED_STROKE_WCAG_TARGET, - seed: nextRamp.stroke3, - mode, - } ); - nextRamp = rebuildActiveFill( { - ramp: nextRamp, - method, - seed, - mode, - } ); - if ( method === 'pinned-role-hybrid' && family === 'neutral' ) { - const fillRamp = createRoomForDarkerActiveFill( { - ramp: nextRamp, - seed, - mode, - } ); - if ( fillRamp !== nextRamp ) { - nextRamp = rebuildActiveFill( { - ramp: fillRamp, - method, - seed, - mode, - } ); - } - } - - const intermediateResult: RampResult = { - ...ramp, - ramp: nextRamp, - warnings: undefined, - }; - return buildForegroundScale( - intermediateResult, - backgroundRamp, - config.foregroundScale - ); -} - -function getNeutralSeed( ramp: RampResult ) { - return ramp.ramp.surface2; -} - -function buildExperimentalBackground( - method: ExperimentalRampMethod, - background: string -) { - const anchored = - method === 'pinned-role-hybrid' - ? buildRamp( background, BG_RAMP_CONFIG, { - rescaleToFitContrastTargets: false, - } ) - : buildBgRamp( background ); - if ( method === 'anchored' ) { - return anchored; - } - return rebuildRamp( { - ramp: anchored, - backgroundRamp: anchored, - method, - family: 'neutral', - seed: getNeutralSeed( anchored ), - } ); -} - -function buildExperimentalAccent( - method: ExperimentalRampMethod, - seed: string, - backgroundRamp: RampResult -) { - const anchored = - method === 'pinned-role-hybrid' - ? buildRamp( seed, ACCENT_RAMP_CONFIG, { - backgroundRamp, - mainDirection: backgroundRamp.direction, - pinLightness: { - stepName: 'surface2', - value: clampAccentScaleReferenceLightness( - get( backgroundRamp.ramp.surface2, [ OKLCH, 'l' ] ), - backgroundRamp.direction - ), - }, - rescaleToFitContrastTargets: false, - } ) - : buildAccentRamp( seed, backgroundRamp ); - if ( method === 'anchored' ) { - return anchored; - } - return rebuildRamp( { - ramp: anchored, - backgroundRamp, - method, - family: 'accent', - seed: anchored.ramp.bgFill1, - } ); -} - -/** - * Builds complete private comparison ramps. These methods are Storybook-only - * and do not change ThemeProvider or the package's public entrypoint. - */ -export function buildExperimentalThemeRamps( { - method, - background, - primary, - error = DEFAULT_SEED_COLORS.error, -}: { - method: ExperimentalRampMethod; - background: string; - primary: string; - error?: string; -} ): ExperimentalThemeRamps { - const backgroundRamp = buildExperimentalBackground( method, background ); - - return { - background: backgroundRamp, - primary: buildExperimentalAccent( method, primary, backgroundRamp ), - error: buildExperimentalAccent( method, error, backgroundRamp ), - }; -} - -export function getExperimentalChroma( color: string ) { - return get( color, [ OklchSrgb, 'c' ] ); -} diff --git a/packages/theme/src/color-ramps/stories/perceptual-ramp-pilot.module.css b/packages/theme/src/color-ramps/stories/perceptual-ramp-pilot.module.css deleted file mode 100644 index 7fd8ed4273ff7e..00000000000000 --- a/packages/theme/src/color-ramps/stories/perceptual-ramp-pilot.module.css +++ /dev/null @@ -1,313 +0,0 @@ -.page { - display: grid; - gap: 32px; - font-family: var(--wpds-typography-font-family-body); -} - -.page h1, -.page h2, -.page h3, -.page p { - margin: 0; -} - -.introduction, -.seed-combination, -.seed-combination > header, -.scale-section, -.component-comparison { - display: grid; - gap: 12px; -} - -.seed-combination { - gap: 24px; - padding: 20px; - border: 1px solid #dcdcde; - border-radius: 8px; -} - -.seeds, -.seed, -.scale-heading, -.button-row { - display: flex; - flex-wrap: wrap; - align-items: center; -} - -.seeds { - gap: 16px; -} - -.seed { - gap: 6px; - font-size: 12px; -} - -.seed-swatch { - box-sizing: border-box; - width: 20px; - height: 20px; - border: 1px solid #757575; - border-radius: 4px; -} - -.seed code, -.scale-table code { - font-family: monospace; -} - -.scale-heading { - justify-content: space-between; - gap: 8px; -} - -.scale-heading h3 { - font-size: 16px; -} - -.table-navigation { - display: flex; - justify-content: flex-end; - gap: 8px; -} - -.table-navigation button { - padding: 4px 8px; - border: 1px solid #949494; - border-radius: 4px; - background: #fff; - font: inherit; - cursor: var(--wpds-cursor-control); -} - -.table-navigation button:focus-visible { - outline: 2px solid currentColor; - outline-offset: 2px; -} - -.table-scroll { - padding-block-end: 8px; - overflow-x: auto; - overscroll-behavior-inline: contain; -} - -.scale-table { - width: 100%; - min-width: 1560px; - table-layout: fixed; - border-collapse: separate; - border-spacing: 2px; -} - -.scale-table th, -.scale-table td { - text-align: start; - vertical-align: top; -} - -.scale-table thead th { - padding: 5px 6px; - background: #f6f7f7; - font-size: 10px; - text-align: center; -} - -.scale-table thead th:first-child { - width: 220px; -} - -.scale-table tbody th { - padding: 8px; - border: 1px solid #dcdcde; - background: #fff; -} - -.scale-table td { - position: relative; - box-sizing: border-box; - height: 88px; - padding: 6px; - font-size: 9px; - vertical-align: bottom; -} - -.scale-table td code { - font-size: 9px; - font-weight: 600; -} - -.method-summary { - display: grid; - gap: 3px; - font-size: 10px; - font-weight: 400; -} - -.method-summary strong { - font-size: 12px; -} - -.status { - justify-self: start; - padding: 2px 5px; - border-radius: 999px; - background: #e7f5e7; - color: #1d5f1d; - font-weight: 600; -} - -.status[data-pass="false"] { - background: #fcf0f1; - color: #8a2424; -} - -.anchor-status { - justify-self: start; - font-weight: 600; -} - -.anchor-status[data-preserved="true"] { - color: #1d5f1d; -} - -.seed-anchor, -.unavailable-step { - position: absolute; - inset-block-start: 0; - inset-inline: 0; - box-sizing: border-box; - padding: 3px 5px; - border-block-end: 1px dashed currentColor; - font-size: 8px; - font-weight: 600; -} - -.unavailable-step { - background: rgb(0 0 0 / 72%); - color: #fff; -} - -.component-grid { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(min(100%, 360px), 1fr)); - gap: 12px; -} - -.component-panel { - display: grid; - gap: 12px; - padding: 14px; - border: 1px solid var(--pilot-border); - border-radius: 6px; - background: var(--pilot-bg); - color: var(--pilot-fg); - font-size: 13px; -} - -.weak-text { - color: var(--pilot-fg-weak); -} - -.state-list, -.state-sample { - display: grid; -} - -.state-list { - gap: 14px; -} - -.state-sample { - gap: 6px; -} - -.state-label { - color: var(--pilot-fg-weak); - font-size: 11px; - font-weight: 600; -} - -.button-row { - gap: 6px; -} - -.state-button { - display: inline-flex; - box-sizing: border-box; - align-items: center; - justify-content: center; - min-width: 58px; - min-height: 32px; - padding: 5px 9px; - border: 1px solid transparent; - border-radius: var(--wpds-border-radius-sm); - font: inherit; - font-size: 11px; - font-weight: var(--wpds-typography-font-weight-emphasis); -} - -.state-button[data-state="focus"] { - outline: 2px solid var(--pilot-focus); - outline-offset: 2px; -} - -.tabs-row { - display: flex; - flex-wrap: wrap; - align-items: stretch; - border-block-end: 1px solid var(--pilot-border); -} - -.tab-state { - display: inline-flex; - box-sizing: border-box; - align-items: center; - justify-content: center; - min-height: 40px; - margin-block-end: -1px; - padding: 8px 10px; - border-block-end: 2px solid transparent; - font-size: 11px; -} - -.tab-state[data-state="focus"] { - border-radius: var(--wpds-border-radius-sm); - outline: 2px solid var(--pilot-focus); - outline-offset: -4px; -} - -.component-note { - font-size: 11px; -} - -.details, -.empty { - padding: 14px; - border: 1px solid #dcdcde; - border-radius: 6px; -} - -.details summary { - cursor: var(--wpds-cursor-control); - font-weight: 600; -} - -.details dl, -.details dl > div { - display: grid; - gap: 8px; -} - -.details dl > div { - grid-template-columns: minmax(220px, 1fr) 3fr; -} - -.details dt { - font-weight: 600; -} - -.details dd { - margin: 0; -} diff --git a/packages/theme/src/color-ramps/stories/perceptual-ramp-pilot.story.tsx b/packages/theme/src/color-ramps/stories/perceptual-ramp-pilot.story.tsx deleted file mode 100644 index b25f50c25679af..00000000000000 --- a/packages/theme/src/color-ramps/stories/perceptual-ramp-pilot.story.tsx +++ /dev/null @@ -1,828 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/react-vite'; -import { useRef } from '@wordpress/element'; -import { contrastAPCA, deltaEOK2, get, OKLCH } from 'colorjs.io/fn'; -import { checkAccessibleCombinations } from '..'; -import { getContrast } from '../lib/color-utils'; -import { DEFAULT_SEED_COLORS } from '../lib/constants'; -import type { Ramp, RampResult } from '../lib/types'; -import { - EXPERIMENTAL_RAMP_METHODS, - buildExperimentalThemeRamps, - getExperimentalChroma, - type ExperimentalThemeRamps, -} from './perceptual-ramp-experiment'; -import styles from './perceptual-ramp-pilot.module.css'; -import { - TRUNK_PRODUCTION_COMMIT, - getTrunkProductionThemeRamps, -} from './trunk-production-fixtures'; - -const COMPARISON_RAMP_METHODS = [ - 'trunk-production', - ...EXPERIMENTAL_RAMP_METHODS, -] as const; - -type ComparisonRampMethod = ( typeof COMPARISON_RAMP_METHODS )[ number ]; - -const SAMPLE_COMBINATIONS = [ - { - label: 'Default light', - background: DEFAULT_SEED_COLORS.background, - primary: DEFAULT_SEED_COLORS.primary, - }, - { - label: 'Default dark', - background: '#1e1e1e', - primary: DEFAULT_SEED_COLORS.primary, - }, - { - label: 'Ectoplasm stress case', - background: '#413256', - primary: '#a3b745', - }, - { - label: 'Blue admin scheme', - background: '#3876a8', - primary: '#437aa8', - }, - { - label: 'Ocean admin scheme', - background: '#5f787f', - primary: '#567958', - }, - { - label: 'Sunrise admin scheme', - background: '#cc4541', - primary: '#ad631e', - }, - { - label: 'Middle-gray polarity boundary', - background: '#777777', - primary: '#d63638', - }, - { - label: 'Yellow gamut edge', - background: '#fcfcfc', - primary: '#ffd700', - }, - { - label: 'Cyan gamut edge', - background: '#1e1e1e', - primary: '#00ffff', - }, -] as const; - -const METHOD_DETAILS: Record< - ComparisonRampMethod, - { label: string; description: string } -> = { - 'trunk-production': { - label: 'Trunk production', - description: `Exact fixed-sample output from trunk at ${ TRUNK_PRODUCTION_COMMIT.slice( - 0, - 11 - ) }. Trunk has four foreground steps.`, - }, - anchored: { - label: 'Current production candidate', - description: - 'Current draft algorithm. APCA shapes the five foreground steps and WCAG ratios shape the other lanes.', - }, - 'constrained-perceptual': { - label: 'Constrained perceptual surfaces and strokes', - description: - 'Uses Oklr lightness and OKLab color difference to space surfaces and strokes while retaining the production chroma policy and WCAG gates.', - }, - 'apca-all': { - label: 'APCA for every lane', - description: - 'Uses APCA as the spacing coordinate for surfaces, strokes, fills, and foregrounds. WCAG remains a hard gate.', - }, - 'role-hybrid': { - label: 'Role-specific hybrid', - description: - 'Uses APCA for foreground readability, OKLab color difference for surfaces and states, and relative chroma for accents.', - }, - 'pinned-role-hybrid': { - label: 'Pinned-seed role hybrid', - description: - 'Pins the supplied color at its semantic anchor, then redirects neighboring surfaces and fills when the original polarity has no contrast headroom.', - }, -}; - -const RAMP_STEPS = [ - { name: 'surface1', label: 'SF1', group: 'Surfaces' }, - { name: 'surface2', label: 'SF2', group: 'Surfaces' }, - { name: 'surface3', label: 'SF3', group: 'Surfaces' }, - { name: 'surface4', label: 'SF4', group: 'Surfaces' }, - { name: 'surface5', label: 'SF5', group: 'Surfaces' }, - { name: 'surface6', label: 'SF6', group: 'Surfaces' }, - { name: 'bgFill1', label: 'BGF1', group: 'Fills' }, - { name: 'bgFill2', label: 'BGF2', group: 'Fills' }, - { name: 'fgFill', label: 'FGF', group: 'Fills' }, - { name: 'stroke1', label: 'ST1', group: 'Strokes' }, - { name: 'stroke2', label: 'ST2', group: 'Strokes' }, - { name: 'stroke3', label: 'ST3', group: 'Strokes' }, - { name: 'stroke4', label: 'ST4', group: 'Strokes' }, - { name: 'fgSurface1', label: 'FGS1', group: 'Foregrounds' }, - { name: 'fgSurface2', label: 'FGS2', group: 'Foregrounds' }, - { name: 'fgSurface3', label: 'FGS3', group: 'Foregrounds' }, - { name: 'fgSurface4', label: 'FGS4', group: 'Foregrounds' }, - { name: 'fgSurface5', label: 'FGS5', group: 'Foregrounds' }, -] as const satisfies readonly { - name: keyof Ramp; - label: string; - group: string; -}[]; - -const SCALE_DETAILS = [ - { name: 'background', label: 'Neutral', seed: 'background' }, - { name: 'primary', label: 'Brand', seed: 'primary' }, - { name: 'error', label: 'Error', seed: 'error' }, -] as const; - -type PilotArgs = { - approaches: ComparisonRampMethod[]; -}; - -const meta = { - title: 'Design System/Theme/Theme Provider/Perceptual Ramp Pilot', - parameters: { - controls: { expanded: true }, - docs: { canvas: { sourceState: 'hidden' } }, - }, - argTypes: { - approaches: { - control: { - type: 'inline-check', - labels: Object.fromEntries( - COMPARISON_RAMP_METHODS.map( ( method ) => [ - method, - METHOD_DETAILS[ method ].label, - ] ) - ), - }, - description: 'Approaches shown in each comparison table.', - options: COMPARISON_RAMP_METHODS, - }, - }, -} satisfies Meta< PilotArgs >; - -export default meta; - -type Story = StoryObj< PilotArgs >; -type ScaleName = ( typeof SCALE_DETAILS )[ number ][ 'name' ]; - -const rampCache = new Map< string, ExperimentalThemeRamps >(); - -function getRamps( - method: ComparisonRampMethod, - background: string, - primary: string -) { - const key = `${ method }|${ background }|${ primary }`; - const cached = rampCache.get( key ); - if ( cached ) { - return cached; - } - - const ramps = - method === 'trunk-production' - ? getTrunkProductionThemeRamps( background, primary ) - : buildExperimentalThemeRamps( { - method, - background, - primary, - error: DEFAULT_SEED_COLORS.error, - } ); - rampCache.set( key, ramps ); - return ramps; -} - -function getSwatchTextColor( color: string ) { - return getContrast( color, '#000' ) >= getContrast( color, '#fff' ) - ? '#000' - : '#fff'; -} - -function getCrossRampFailures( ramp: RampResult, backgroundRamp: RampResult ) { - let failures = 0; - for ( const surface of [ 'surface1', 'surface2', 'surface3' ] as const ) { - for ( const foreground of [ - 'fgSurface3', - 'fgSurface4', - 'fgSurface5', - ] as const ) { - if ( - getContrast( - backgroundRamp.ramp[ surface ], - ramp.ramp[ foreground ] - ) < 4.5 - ) { - failures++; - } - } - if ( - getContrast( backgroundRamp.ramp[ surface ], ramp.ramp.stroke3 ) < 3 - ) { - failures++; - } - } - return failures; -} - -function getScaleMetrics( - ramp: RampResult, - backgroundRamp: RampResult, - isAccent: boolean, - seed: string -) { - const anchor = isAccent ? ramp.ramp.bgFill1 : ramp.ramp.surface2; - const displayBackground = backgroundRamp.ramp.surface2; - const currentForegroundContrast = Math.abs( - contrastAPCA( displayBackground, ramp.ramp.fgSurface4 ) - ); - const activeForegroundContrast = Math.abs( - contrastAPCA( displayBackground, ramp.ramp.fgSurface5 ) - ); - const failures = - checkAccessibleCombinations( { bgRamp: ramp } ).length + - ( isAccent ? getCrossRampFailures( ramp, backgroundRamp ) : 0 ); - const seedChroma = getExperimentalChroma( seed ); - const chromaDrift = isAccent - ? [ 'surface4', 'surface5', 'stroke2', 'stroke3' ].reduce( - ( total, step ) => - total + - Math.abs( - getExperimentalChroma( - ramp.ramp[ step as keyof Ramp ] - ) - seedChroma - ), - 0 - ) / 4 - : undefined; - - return { - failures, - anchorDifference: deltaEOK2( seed, anchor ), - anchorLightnessShift: - get( anchor, [ OKLCH, 'l' ] ) - get( seed, [ OKLCH, 'l' ] ), - fillDifference: deltaEOK2( ramp.ramp.bgFill1, ramp.ramp.bgFill2 ), - foregroundDifference: deltaEOK2( - ramp.ramp.fgSurface4, - ramp.ramp.fgSurface5 - ), - foregroundApcaInterval: - activeForegroundContrast - currentForegroundContrast, - chromaDrift, - }; -} - -function Seed( { label, color }: { label: string; color: string } ) { - return ( - - - ); -} - -function MethodSummary( { - method, - ramp, - backgroundRamp, - isAccent, - seed, -}: { - method: ComparisonRampMethod; - ramp: RampResult; - backgroundRamp: RampResult; - isAccent: boolean; - seed: string; -} ) { - const metrics = getScaleMetrics( ramp, backgroundRamp, isAccent, seed ); - - return ( -
- { METHOD_DETAILS[ method ].label } - - { metrics.failures === 0 - ? 'WCAG gates pass' - : `${ metrics.failures } WCAG failures` } - - - { metrics.anchorDifference <= 0.002 - ? 'Seed preserved' - : `Seed moved ΔE ${ metrics.anchorDifference.toFixed( - 3 - ) }` } - - Anchor ΔL { metrics.anchorLightnessShift.toFixed( 3 ) } - - Foreground polarity:{ ' ' } - { ramp.direction === 'lighter' ? 'light' : 'dark' } - - Fill ΔE { metrics.fillDifference.toFixed( 3 ) } - { method === 'trunk-production' ? ( - FGS5 not present on trunk - ) : ( - <> - - FGS4→5 ΔE { metrics.foregroundDifference.toFixed( 3 ) } - - - FGS4→5 ΔLc{ ' ' } - { metrics.foregroundApcaInterval.toFixed( 1 ) } - - - ) } - { metrics.chromaDrift === undefined ? null : ( - - Mean relative chroma drift{ ' ' } - { metrics.chromaDrift.toFixed( 3 ) } - - ) } -
- ); -} - -function ScaleTable( { - label, - seed, - scaleName, - methods, - rampsByMethod, -}: { - label: string; - seed: string; - scaleName: ScaleName; - methods: ComparisonRampMethod[]; - rampsByMethod: Map< ComparisonRampMethod, ExperimentalThemeRamps >; -} ) { - const groups = Array.from( - new Set( RAMP_STEPS.map( ( step ) => step.group ) ) - ); - const scrollContainer = useRef< HTMLDivElement >( null ); - const scrollTable = ( direction: -1 | 1 ) => { - const container = scrollContainer.current; - if ( ! container ) { - return; - } - container.scrollBy( { - behavior: 'auto', - left: direction * container.clientWidth * 0.75, - } ); - }; - - return ( -
-
-

{ label }

- -
-
- - -
-
- - - - - { groups.map( ( group ) => ( - - ) ) } - - - { RAMP_STEPS.map( ( step ) => ( - - ) ) } - - - - { methods.map( ( method ) => { - const methodRamps = rampsByMethod.get( method )!; - const ramp = methodRamps[ scaleName ]; - const anchorStep = - scaleName === 'background' - ? 'surface2' - : 'bgFill1'; - return ( - - - { RAMP_STEPS.map( ( step ) => { - const color = ramp.ramp[ step.name ]; - const unavailableOnTrunk = - method === 'trunk-production' && - step.name === 'fgSurface5'; - return ( - - ); - } ) } - - ); - } ) } - -
- Approach - step.group === group - ).length - } - key={ group } - scope="colgroup" - > - { group } -
- { step.label } -
- - - { unavailableOnTrunk ? ( - - Not on trunk - - ) : null } - { step.name === anchorStep ? ( - - Input seed - - ) : null } - { color } -
-
-
- ); -} - -function ComponentComparison( { - methods, - rampsByMethod, - label, -}: { - methods: ComparisonRampMethod[]; - rampsByMethod: Map< ComparisonRampMethod, ExperimentalThemeRamps >; - label: string; -} ) { - return ( -
-

Representative states

-
- { methods.map( ( method ) => { - const ramps = rampsByMethod.get( method )!; - const neutral = ramps.background.ramp; - const brand = ramps.primary.ramp; - const error = ramps.error.ramp; - const buttonExamples = [ - { - label: 'Brand button', - restingBackground: brand.bgFill1, - activeBackground: brand.bgFill2, - foreground: brand.fgFill, - }, - { - label: 'Neutral button', - restingBackground: neutral.bgFillInverted1, - activeBackground: neutral.bgFillInverted2, - foreground: neutral.fgFillInverted, - }, - { - label: 'Error button', - restingBackground: error.bgFill1, - activeBackground: error.bgFill2, - foreground: error.fgFill, - }, - ] as const; - const tabExamples = [ - { - label: 'Resting', - state: 'resting', - foreground: neutral.fgSurface4, - }, - { - label: 'Hover', - state: 'hover', - foreground: neutral.fgSurface5, - }, - { - label: 'Focus', - state: 'focus', - foreground: neutral.fgSurface5, - }, - { - label: 'Active', - state: 'active', - foreground: neutral.fgSurface5, - }, - { - label: 'Disabled', - state: 'disabled', - foreground: neutral.fgSurface2, - }, - ] as const; - return ( -
- { METHOD_DETAILS[ method ].label } -

- Supporting text and hierarchy -

-
- { buttonExamples.map( ( example ) => ( -
- - { example.label } - -
- { [ - { - label: 'Resting', - state: 'resting', - background: - example.restingBackground, - foreground: - example.foreground, - }, - { - label: 'Hover', - state: 'hover', - background: - example.activeBackground, - foreground: - example.foreground, - }, - { - label: 'Focus', - state: 'focus', - background: - example.activeBackground, - foreground: - example.foreground, - }, - { - label: 'Active', - state: 'active', - background: - example.activeBackground, - foreground: - example.foreground, - }, - { - label: 'Disabled', - state: 'disabled', - background: - neutral.surface5, - foreground: - neutral.fgSurface2, - }, - ].map( ( state ) => ( - - { state.label } - - ) ) } -
-
- ) ) } -
- - Neutral tabs - -
- { tabExamples.map( ( example ) => ( - - { example.label } - - ) ) } -
-
-
-
- ); - } ) } -
-

- These static samples map the generated ramps for { label } to - the semantic tokens used by WordPress UI. Focus also includes - the generated focus stroke. Identical hover and active samples - mean those states use the same semantic token. -

-
- ); -} - -function SeedCombination( { - label, - background, - primary, - methods, -}: { - label: string; - background: string; - primary: string; - methods: ComparisonRampMethod[]; -} ) { - const rampsByMethod = new Map( - methods.map( ( method ) => [ - method, - getRamps( method, background, primary ), - ] ) - ); - const seeds = { - background, - primary, - error: DEFAULT_SEED_COLORS.error, - }; - - return ( -
-
-

{ label }

-
- - - -
-
- { SCALE_DETAILS.map( ( scale ) => ( - - ) ) } - -
- ); -} - -export const Comparison: Story = { - args: { - approaches: [ - 'trunk-production', - 'anchored', - 'constrained-perceptual', - ], - }, - render: ( { approaches } ) => { - const methods = COMPARISON_RAMP_METHODS.filter( ( method ) => - approaches.includes( method ) - ); - - return ( -
-
-

Perceptual ramp comparison

-

- Compare each complete neutral, brand, and error ramp by - step. The default rows show trunk at{ ' ' } - { TRUNK_PRODUCTION_COMMIT.slice( 0, 11 ) }, the current - branch, and the new constrained-perceptual experiment. - WCAG ratios remain hard gates for the current and - experimental algorithms. Use the Approaches control to - show older experiments. -

-
- { methods.length === 0 ? ( -

- Select at least one approach. -

- ) : ( - SAMPLE_COMBINATIONS.map( ( combination ) => ( - - ) ) - ) } -
- Approach definitions -
- { COMPARISON_RAMP_METHODS.map( ( method ) => ( -
-
{ METHOD_DETAILS[ method ].label }
-
- { METHOD_DETAILS[ method ].description } -
-
- ) ) } -
-
-
- ); - }, -}; diff --git a/packages/theme/src/color-ramps/stories/test/perceptual-ramp-experiment.test.ts b/packages/theme/src/color-ramps/stories/test/perceptual-ramp-experiment.test.ts deleted file mode 100644 index d4af966db2dd44..00000000000000 --- a/packages/theme/src/color-ramps/stories/test/perceptual-ramp-experiment.test.ts +++ /dev/null @@ -1,357 +0,0 @@ -import { - ColorSpace, - contrastAPCA, - deltaEOK2, - get, - getLuminance, - OKLab, - OKLCH, - OKLCH_sRGB as OklchSrgb, - OKLrab, - sRGB, -} from 'colorjs.io/fn'; -import { describe, expect, it } from 'vitest'; -import { checkAccessibleCombinations } from '../..'; -import { getContrast } from '../../lib/color-utils'; -import { DEFAULT_SEED_COLORS } from '../../lib/constants'; -import { - EXPERIMENTAL_RAMP_METHODS, - buildExperimentalThemeRamps, -} from '../perceptual-ramp-experiment'; -import { - TRUNK_PRODUCTION_COMMIT, - getTrunkProductionThemeRamps, -} from '../trunk-production-fixtures'; - -ColorSpace.register( sRGB ); -ColorSpace.register( OKLab ); -ColorSpace.register( OKLCH ); -ColorSpace.register( OklchSrgb ); -ColorSpace.register( OKLrab ); - -const SAMPLE_COMBINATIONS = [ - { background: '#fcfcfc', primary: '#3858e9' }, - { background: '#1e1e1e', primary: '#3858e9' }, - { background: '#222524', primary: '#3858e9' }, - { background: '#4f386e', primary: '#608010' }, - { background: '#f8f8f8', primary: '#3858e9' }, - { background: '#25292b', primary: '#3858e9' }, - { background: '#eaeeed', primary: '#007cba' }, - { background: '#3876a8', primary: '#437aa8' }, - { background: '#5b534d', primary: '#916745' }, - { background: '#413256', primary: '#a3b745' }, - { background: '#5f787f', primary: '#567958' }, - { background: '#3d4042', primary: '#cf4339' }, - { background: '#cc4541', primary: '#ad631e' }, - // Synthetic stress cases for the polarity boundary and high-chroma gamut edges. - { background: '#777777', primary: '#d63638' }, - { background: '#fcfcfc', primary: '#ffd700' }, - { background: '#1e1e1e', primary: '#00ffff' }, -] as const; - -const FOREGROUND_STEPS = [ - 'fgSurface1', - 'fgSurface2', - 'fgSurface3', - 'fgSurface4', - 'fgSurface5', -] as const; -const STROKE_STEPS = [ 'stroke1', 'stroke2', 'stroke3', 'stroke4' ] as const; - -function getForegroundContrasts( - background: string, - ramp: ReturnType< typeof buildExperimentalThemeRamps >[ 'background' ] -) { - return FOREGROUND_STEPS.map( ( step ) => - Math.abs( contrastAPCA( background, ramp.ramp[ step ] ) ) - ); -} - -describe( 'perceptual ramp experiment', () => { - it( 'exposes the exact pinned trunk ramps for every Storybook seed pair', () => { - for ( const seeds of [ - { background: '#fcfcfc', primary: '#3858e9' }, - { background: '#1e1e1e', primary: '#3858e9' }, - { background: '#413256', primary: '#a3b745' }, - { background: '#3876a8', primary: '#437aa8' }, - { background: '#5f787f', primary: '#567958' }, - { background: '#cc4541', primary: '#ad631e' }, - { background: '#777777', primary: '#d63638' }, - { background: '#fcfcfc', primary: '#ffd700' }, - { background: '#1e1e1e', primary: '#00ffff' }, - ] as const ) { - const ramps = getTrunkProductionThemeRamps( - seeds.background, - seeds.primary - ); - - for ( const ramp of Object.values( ramps ) ) { - expect( Object.keys( ramp.ramp ) ).toHaveLength( 23 ); - expect( ramp.ramp.fgSurface5 ).toBe( ramp.ramp.fgSurface4 ); - } - } - - expect( TRUNK_PRODUCTION_COMMIT ).toBe( - '0b466a172502fa0d103ad4102dd5f24e22175049' - ); - expect( - getTrunkProductionThemeRamps( '#fcfcfc', '#3858e9' ).background.ramp - ).toMatchObject( { - surface2: '#fcfcfc', - stroke3: '#8d8d8d', - fgSurface4: '#1e1e1e', - } ); - expect( () => - getTrunkProductionThemeRamps( '#123456', '#abcdef' ) - ).toThrow( 'No trunk production fixture exists' ); - } ); - - it.each( EXPERIMENTAL_RAMP_METHODS )( - 'builds complete %s ramps for every representative seed pair', - ( method ) => { - for ( const seeds of SAMPLE_COMBINATIONS ) { - const ramps = buildExperimentalThemeRamps( { - method, - ...seeds, - } ); - - for ( const ramp of Object.values( ramps ) ) { - expect( Object.keys( ramp.ramp ) ).toHaveLength( 23 ); - expect( Object.values( ramp.ramp ) ).not.toContain( - undefined - ); - } - } - } - ); - - it( 'keeps constrained surfaces ordered and perceptually progressive', () => { - for ( const seeds of SAMPLE_COMBINATIONS ) { - const ramps = buildExperimentalThemeRamps( { - method: 'constrained-perceptual', - ...seeds, - } ); - - for ( const ramp of Object.values( ramps ) ) { - const lightness = ( step: keyof typeof ramp.ramp ) => - get( ramp.ramp[ step ], [ OKLrab, 'l' ] ); - expect( lightness( 'surface1' ) ).toBeLessThan( - lightness( 'surface2' ) - ); - expect( lightness( 'surface3' ) ).toBeGreaterThan( - lightness( 'surface2' ) - ); - - const emphasisLightness = [ - lightness( 'surface2' ), - lightness( 'surface4' ), - lightness( 'surface5' ), - lightness( 'surface6' ), - ]; - const orderedLightness = [ ...emphasisLightness ].sort( - ramp.direction === 'lighter' - ? ( first, second ) => first - second - : ( first, second ) => second - first - ); - expect( emphasisLightness ).toEqual( orderedLightness ); - - const distances = [ 'surface4', 'surface5', 'surface6' ].map( - ( step ) => - deltaEOK2( - ramp.ramp.surface2, - ramp.ramp[ step as keyof typeof ramp.ramp ] - ) - ); - expect( distances[ 0 ] ).toBeLessThan( distances[ 1 ] ); - expect( distances[ 1 ] ).toBeLessThan( distances[ 2 ] ); - } - } - } ); - - it( 'keeps constrained ST3 accessible and ST4 perceptually stronger', () => { - for ( const seeds of SAMPLE_COMBINATIONS ) { - const ramps = buildExperimentalThemeRamps( { - method: 'constrained-perceptual', - ...seeds, - } ); - - for ( const ramp of Object.values( ramps ) ) { - const stroke3Contrast = Math.abs( - contrastAPCA( ramp.ramp.surface3, ramp.ramp.stroke3 ) - ); - const stroke4Contrast = Math.abs( - contrastAPCA( ramp.ramp.surface3, ramp.ramp.stroke4 ) - ); - expect( stroke4Contrast ).toBeGreaterThanOrEqual( - stroke3Contrast - ); - expect( - getContrast( ramp.ramp.surface3, ramp.ramp.stroke3 ) - ).toBeGreaterThanOrEqual( 3 ); - } - } - } ); - - it.each( EXPERIMENTAL_RAMP_METHODS )( - 'keeps every configured WCAG pair accessible in the %s ramps', - ( method ) => { - for ( const seeds of SAMPLE_COMBINATIONS ) { - const { background, primary, error } = - buildExperimentalThemeRamps( { method, ...seeds } ); - - for ( const ramp of [ background, primary, error ] ) { - expect( - checkAccessibleCombinations( { bgRamp: ramp } ) - ).toEqual( [] ); - } - - for ( const ramp of [ primary, error ] ) { - for ( const surface of [ - 'surface1', - 'surface2', - 'surface3', - ] as const ) { - for ( const foreground of [ - 'fgSurface3', - 'fgSurface4', - 'fgSurface5', - ] as const ) { - expect( - getContrast( - background.ramp[ surface ], - ramp.ramp[ foreground ] - ) - ).toBeGreaterThanOrEqual( 4.5 ); - } - expect( - getContrast( - background.ramp[ surface ], - ramp.ramp.stroke3 - ) - ).toBeGreaterThanOrEqual( 3 ); - } - } - } - } - ); - - it( 'keeps supplied seeds at their anchors in the pinned-seed role hybrid', () => { - for ( const seeds of SAMPLE_COMBINATIONS ) { - const ramps = buildExperimentalThemeRamps( { - method: 'pinned-role-hybrid', - ...seeds, - } ); - - expect( - deltaEOK2( seeds.background, ramps.background.ramp.surface2 ) - ).toBeLessThanOrEqual( 0.002 ); - expect( - deltaEOK2( seeds.primary, ramps.primary.ramp.bgFill1 ) - ).toBeLessThanOrEqual( 0.002 ); - expect( - deltaEOK2( DEFAULT_SEED_COLORS.error, ramps.error.ramp.bgFill1 ) - ).toBeLessThanOrEqual( 0.002 ); - } - } ); - - it.each( EXPERIMENTAL_RAMP_METHODS )( - 'orders foreground and stroke strength and keeps active fills darker in the %s ramps', - ( method ) => { - for ( const seeds of SAMPLE_COMBINATIONS ) { - const ramps = buildExperimentalThemeRamps( { - method, - ...seeds, - } ); - const displayBackground = ramps.background.ramp.surface2; - - for ( const ramp of Object.values( ramps ) ) { - const contrasts = getForegroundContrasts( - displayBackground, - ramp - ); - expect( contrasts ).toEqual( - [ ...contrasts ].sort( ( a, b ) => a - b ) - ); - const strokeContrasts = STROKE_STEPS.map( ( step ) => - Math.abs( - contrastAPCA( - ramp.ramp.surface3, - ramp.ramp[ step ] - ) - ) - ); - expect( strokeContrasts ).toEqual( - [ ...strokeContrasts ].sort( ( a, b ) => a - b ) - ); - expect( getLuminance( ramp.ramp.bgFill2 ) ).toBeLessThan( - getLuminance( ramp.ramp.bgFill1 ) - ); - expect( - getContrast( ramp.ramp.bgFill1, ramp.ramp.bgFill2 ) - ).toBeGreaterThanOrEqual( 1.2 ); - } - } - } - ); - - it( 'keeps more accent chroma in the role-specific hybrid than in APCA-all', () => { - for ( const seeds of SAMPLE_COMBINATIONS.slice( -2 ) ) { - const apca = buildExperimentalThemeRamps( { - method: 'apca-all', - ...seeds, - } ).primary.ramp; - const hybrid = buildExperimentalThemeRamps( { - method: 'role-hybrid', - ...seeds, - } ).primary.ramp; - const seedChroma = get( seeds.primary, [ OklchSrgb, 'c' ] ); - const steps = [ - 'surface4', - 'surface5', - 'stroke2', - 'stroke3', - ] as const; - const totalDrift = ( ramp: typeof apca ) => - steps.reduce( - ( drift, step ) => - drift + - Math.abs( - get( ramp[ step ], [ OklchSrgb, 'c' ] ) - seedChroma - ), - 0 - ); - - expect( totalDrift( hybrid ) ).toBeLessThanOrEqual( - totalDrift( apca ) - ); - } - } ); - - it.each( [ 'role-hybrid', 'pinned-role-hybrid' ] as const )( - 'gives the %s a measurable active-state difference', - ( method ) => { - for ( const seeds of SAMPLE_COMBINATIONS ) { - const ramps = buildExperimentalThemeRamps( { - method, - ...seeds, - } ); - - for ( const ramp of Object.values( ramps ) ) { - expect( - deltaEOK2( ramp.ramp.bgFill1, ramp.ramp.bgFill2 ) - ).toBeGreaterThanOrEqual( 0.045 ); - } - } - } - ); - - it.each( EXPERIMENTAL_RAMP_METHODS )( - 'returns deterministic output for the %s method', - ( method ) => { - const seeds = SAMPLE_COMBINATIONS[ 2 ]; - expect( - buildExperimentalThemeRamps( { method, ...seeds } ) - ).toEqual( buildExperimentalThemeRamps( { method, ...seeds } ) ); - } - ); -} ); diff --git a/packages/theme/src/color-ramps/stories/trunk-production-fixtures.ts b/packages/theme/src/color-ramps/stories/trunk-production-fixtures.ts deleted file mode 100644 index 9c7bbc312e1f81..00000000000000 --- a/packages/theme/src/color-ramps/stories/trunk-production-fixtures.ts +++ /dev/null @@ -1,806 +0,0 @@ -import type { RampResult } from '../lib/types'; - -export const TRUNK_PRODUCTION_COMMIT = - '0b466a172502fa0d103ad4102dd5f24e22175049'; - -export type TrunkProductionThemeRamps = { - background: RampResult; - primary: RampResult; - error: RampResult; -}; - -const TRUNK_PRODUCTION_FIXTURES: Record< string, TrunkProductionThemeRamps > = { - '#fcfcfc|#3858e9': { - background: { - ramp: { - surface2: '#fcfcfc', - bgFill1: '#555', - fgFill: '#f0f0f0', - bgFill2: '#484848', - surface6: '#d6d6d6', - surface5: '#e6e6e6', - surface4: '#ededed', - surface3: '#fff', - fgSurface4: '#1e1e1e', - fgSurface3: '#707070', - fgSurface2: '#8d8d8d', - fgSurface1: '#aeaeae', - stroke3: '#8d8d8d', - stroke4: '#6e6e6e', - stroke2: '#dbdbdb', - stroke1: '#f0f0f0', - bgFillDark: '#1e1e1e', - fgFillDark: '#f0f0f0', - bgFillInverted2: '#1e1e1e', - bgFillInverted1: '#2d2d2d', - fgFillInverted: '#f0f0f0', - surface1: '#f4f4f4', - fgSurface5: '#1e1e1e', - }, - direction: 'darker', - }, - primary: { - ramp: { - bgFill1: '#3858e9', - fgFill: '#eff0f2', - bgFill2: '#2e49d9', - surface2: '#f6f8fe', - surface6: '#c7d2ee', - surface5: '#dce3f4', - surface4: '#e6eaf4', - surface3: '#fff', - fgSurface4: '#0b0070', - fgSurface3: '#3858e9', - fgSurface2: '#5a82ff', - fgSurface1: '#86a9ff', - stroke3: '#3858e9', - stroke4: '#2337c8', - stroke2: '#9caacc', - stroke1: '#b0bbd6', - bgFillDark: '#1a1e27', - fgFillDark: '#eff0f2', - bgFillInverted2: '#1a1e27', - bgFillInverted1: '#13009f', - fgFillInverted: '#eff0f2', - surface1: '#ecf0fa', - fgSurface5: '#0b0070', - }, - direction: 'darker', - }, - error: { - ramp: { - bgFill1: '#cc1818', - fgFill: '#f2efef', - bgFill2: '#b90000', - surface2: '#fff6f5', - surface6: '#f3c9c2', - surface5: '#f7dbd7', - surface4: '#f6e6e3', - surface3: '#fff', - fgSurface4: '#470000', - fgSurface3: '#cc1818', - fgSurface2: '#f74c40', - fgSurface1: '#ff8879', - stroke3: '#cc1818', - stroke4: '#9d0000', - stroke2: '#d39c95', - stroke1: '#dab1aa', - bgFillDark: '#231c1b', - fgFillDark: '#f2efef', - bgFillInverted2: '#231c1b', - bgFillInverted1: '#640000', - fgFillInverted: '#f2efef', - surface1: '#fcece9', - fgSurface5: '#470000', - }, - direction: 'darker', - }, - }, - '#1e1e1e|#3858e9': { - background: { - ramp: { - surface2: '#1e1e1e', - bgFill1: '#959595', - fgFill: '#1e1e1e', - bgFill2: '#a5a5a5', - surface6: '#383838', - surface5: '#2d2d2d', - surface4: '#282828', - surface3: '#242424', - fgSurface4: '#f0f0f0', - fgSurface3: '#b4b4b4', - fgSurface2: '#6e6e6e', - fgSurface1: '#535353', - stroke3: '#6e6e6e', - stroke4: '#8c8c8c', - stroke2: '#343434', - stroke1: '#272727', - bgFillDark: '#000', - fgFillDark: '#f0f0f0', - bgFillInverted2: '#f0f0f0', - bgFillInverted1: '#dbdbdb', - fgFillInverted: '#1e1e1e', - surface1: '#171717', - fgSurface5: '#f0f0f0', - }, - direction: 'lighter', - }, - primary: { - ramp: { - bgFill1: '#3858e9', - fgFill: '#eff0f2', - bgFill2: '#4367f9', - surface2: '#0b174b', - surface6: '#1d307d', - surface5: '#162668', - surface4: '#122062', - surface3: '#111e52', - fgSurface4: '#e4f0ff', - fgSurface3: '#8eb0ff', - fgSurface2: '#3c5eef', - fgSurface1: '#273fcf', - stroke3: '#8eb0ff', - stroke4: '#c6daff', - stroke2: '#506ab4', - stroke1: '#435ca9', - bgFillDark: '#000', - fgFillDark: '#eff0f2', - bgFillInverted2: '#eff0f2', - bgFillInverted1: '#c8dcff', - fgFillInverted: '#1a1e27', - surface1: '#07113a', - fgSurface5: '#e4f0ff', - }, - direction: 'lighter', - }, - error: { - ramp: { - bgFill1: '#cc1818', - fgFill: '#f2efef', - bgFill2: '#dd3029', - surface2: '#36100c', - surface6: '#5e241f', - surface5: '#4e1d18', - surface4: '#481713', - surface3: '#3e1713', - fgSurface4: '#ffe6e0', - fgSurface3: '#ff8879', - fgSurface2: '#d32420', - fgSurface1: '#a00', - stroke3: '#ff8879', - stroke4: '#ffc3b9', - stroke2: '#9f5148', - stroke1: '#8c473f', - bgFillDark: '#000', - fgFillDark: '#f2efef', - bgFillInverted2: '#f2efef', - bgFillInverted1: '#ffd0c7', - fgFillInverted: '#231c1b', - surface1: '#290b08', - fgSurface5: '#ffe6e0', - }, - direction: 'lighter', - }, - }, - '#413256|#a3b745': { - background: { - ramp: { - surface2: '#413256', - bgFill1: '#a191bb', - fgFill: '#201c27', - bgFill2: '#b2a2cd', - surface6: '#5c447b', - surface5: '#513b6f', - surface4: '#4d376a', - surface3: '#483562', - fgSurface4: '#f0f0f1', - fgSurface3: '#b5b3ba', - fgSurface2: '#8a8691', - fgSurface1: '#6e6977', - stroke3: '#9181aa', - stroke4: '#b0a5c2', - stroke2: '#544569', - stroke1: '#48395c', - bgFillDark: '#000', - fgFillDark: '#f0f0f1', - bgFillInverted2: '#f0f0f1', - bgFillInverted1: '#e4d3ff', - fgFillInverted: '#201c27', - surface1: '#3e2b56', - fgSurface5: '#f0f0f1', - }, - direction: 'lighter', - }, - primary: { - ramp: { - bgFill1: '#a3b745', - fgFill: '#1e1e1b', - bgFill2: '#b5c959', - surface2: '#383e21', - surface6: '#4d552e', - surface5: '#444b28', - surface4: '#404724', - surface3: '#3d4326', - fgSurface4: '#e7fd8d', - fgSurface3: '#a3b745', - fgSurface2: '#819316', - fgSurface1: '#657500', - stroke3: '#a3b745', - stroke4: '#cadf6f', - stroke2: '#666e4b', - stroke1: '#5a613f', - bgFillDark: '#000', - fgFillDark: '#eef3e1', - bgFillInverted2: '#eef3e1', - bgFillInverted1: '#d0e575', - fgFillInverted: '#1e1e1b', - surface1: '#33391c', - fgSurface5: '#e7fd8d', - }, - direction: 'lighter', - }, - error: { - ramp: { - bgFill1: '#cc1818', - fgFill: '#f2efef', - bgFill2: '#dd3029', - surface2: '#602620', - surface6: '#87332b', - surface5: '#752e27', - surface4: '#702a23', - surface3: '#662b25', - fgSurface4: '#ffe6e0', - fgSurface3: '#ff8879', - fgSurface2: '#f4493e', - fgSurface1: '#d01f1d', - stroke3: '#ff8879', - stroke4: '#ffc3b9', - stroke2: '#9f5148', - stroke1: '#8c473f', - bgFillDark: '#000', - fgFillDark: '#f2efef', - bgFillInverted2: '#f2efef', - bgFillInverted1: '#ffd0c7', - fgFillInverted: '#231c1b', - surface1: '#5a201b', - fgSurface5: '#ffe6e0', - }, - direction: 'lighter', - }, - }, - '#3876a8|#437aa8': { - background: { - ramp: { - surface2: '#165888', - bgFill1: '#84c3f9', - fgFill: '#1b1f22', - bgFill2: '#9bd8ff', - surface6: '#386f9d', - surface5: '#2e6591', - surface4: '#326086', - surface3: '#2e5d83', - fgSurface4: '#fff', - fgSurface3: '#cbd0d4', - fgSurface2: '#a3abb3', - fgSurface1: '#808b95', - stroke3: '#8daecc', - stroke4: '#c0d4e5', - stroke2: '#476b8a', - stroke1: '#405e78', - bgFillDark: '#000', - fgFillDark: '#eef0f2', - bgFillInverted2: '#fff', - bgFillInverted1: '#cbeeff', - fgFillInverted: '#1b1f22', - surface1: '#245278', - fgSurface5: '#fff', - }, - direction: 'lighter', - }, - primary: { - ramp: { - bgFill1: '#437aa8', - fgFill: '#fdfeff', - bgFill2: '#5189b8', - surface2: '#2a4b67', - surface6: '#386285', - surface5: '#355876', - surface4: '#315371', - surface3: '#2f506c', - fgSurface4: '#d8f4ff', - fgSurface3: '#8ac3f5', - fgSurface2: '#689fd0', - fgSurface1: '#4980ae', - stroke3: '#8ac3f5', - stroke4: '#c9edff', - stroke2: '#527a9d', - stroke1: '#4e6c87', - bgFillDark: '#000', - fgFillDark: '#eef0f2', - bgFillInverted2: '#eef0f2', - bgFillInverted1: '#b0e1ff', - fgFillInverted: '#1b1e21', - surface1: '#254661', - fgSurface5: '#d8f4ff', - }, - direction: 'lighter', - }, - error: { - ramp: { - bgFill1: '#cc1818', - fgFill: '#f2efef', - bgFill2: '#dd3029', - surface2: '#752e27', - surface6: '#993d34', - surface5: '#8a362e', - surface4: '#85312a', - surface3: '#7a332c', - fgSurface4: '#ffe6e0', - fgSurface3: '#ff9f91', - fgSurface2: '#ff6153', - fgSurface1: '#e1342d', - stroke3: '#ff9f91', - stroke4: '#ffd9d1', - stroke2: '#ab5c52', - stroke1: '#985249', - bgFillDark: '#000', - fgFillDark: '#f2efef', - bgFillInverted2: '#f2efef', - bgFillInverted1: '#ffd0c7', - fgFillInverted: '#231c1b', - surface1: '#6f2922', - fgSurface5: '#ffe6e0', - }, - direction: 'lighter', - }, - }, - '#5f787f|#567958': { - background: { - ramp: { - surface2: '#40585f', - bgFill1: '#a7c1c9', - fgFill: '#1c1e1f', - bgFill2: '#b9d5dc', - surface6: '#527078', - surface5: '#47666e', - surface4: '#456168', - surface3: '#425d65', - fgSurface4: '#fff', - fgSurface3: '#c8d1d4', - fgSurface2: '#a3acae', - fgSurface1: '#838b8d', - stroke3: '#8cb0ba', - stroke4: '#b3d7e2', - stroke2: '#566b71', - stroke1: '#4c5e63', - bgFillDark: '#000', - fgFillDark: '#edf1f2', - bgFillInverted2: '#fff', - bgFillInverted1: '#d2edf5', - fgFillInverted: '#1c1e1f', - surface1: '#39535a', - fgSurface5: '#fff', - }, - direction: 'lighter', - }, - primary: { - ramp: { - bgFill1: '#567958', - fgFill: '#f2f7f2', - bgFill2: '#648766', - surface2: '#354f37', - surface6: '#456848', - surface5: '#3e5d40', - surface4: '#3a593c', - surface3: '#3a543b', - fgSurface4: '#d5fdd7', - fgSurface3: '#a3c8a4', - fgSurface2: '#80a582', - fgSurface1: '#628664', - stroke3: '#a3c8a4', - stroke4: '#ccf3ce', - stroke2: '#627e63', - stroke1: '#577158', - bgFillDark: '#000', - fgFillDark: '#edf2ed', - bgFillInverted2: '#edf2ed', - bgFillInverted1: '#bee5c0', - fgFillInverted: '#1c1f1c', - surface1: '#304a32', - fgSurface5: '#d5fdd7', - }, - direction: 'lighter', - }, - error: { - ramp: { - bgFill1: '#cc1818', - fgFill: '#f2efef', - bgFill2: '#dd3029', - surface2: '#752e27', - surface6: '#993d34', - surface5: '#8a362e', - surface4: '#85312a', - surface3: '#7a332c', - fgSurface4: '#ffe6e0', - fgSurface3: '#ff9f91', - fgSurface2: '#ff6153', - fgSurface1: '#e1342d', - stroke3: '#ff9f91', - stroke4: '#ffd9d1', - stroke2: '#ab5c52', - stroke1: '#985249', - bgFillDark: '#000', - fgFillDark: '#f2efef', - bgFillInverted2: '#f2efef', - bgFillInverted1: '#ffd0c7', - fgFillInverted: '#231c1b', - surface1: '#6f2922', - fgSurface5: '#ffe6e0', - }, - direction: 'lighter', - }, - }, - '#cc4541|#ad631e': { - background: { - ramp: { - surface2: '#a51b20', - bgFill1: '#ffa69c', - fgFill: '#231c1b', - bgFill2: '#ffc2ba', - surface6: '#b54842', - surface5: '#a5403c', - surface4: '#a03b36', - surface3: '#9c3633', - fgSurface4: '#fff', - fgSurface3: '#d6cecd', - fgSurface2: '#b5a7a5', - fgSurface1: '#9f8380', - stroke3: '#d39d97', - stroke4: '#eacac6', - stroke2: '#9e514b', - stroke1: '#8b4742', - bgFillDark: '#000', - fgFillDark: '#f2efef', - bgFillInverted2: '#fff', - bgFillInverted1: '#ffe3de', - fgFillInverted: '#231c1b', - surface1: '#8c302c', - fgSurface5: '#fff', - }, - direction: 'lighter', - }, - primary: { - ramp: { - bgFill1: '#ad631e', - fgFill: '#fffdfc', - bgFill2: '#bd7230', - surface2: '#613f24', - surface6: '#7e5331', - surface5: '#714a2b', - surface4: '#6d4627', - surface3: '#664429', - fgSurface4: '#ffe8cd', - fgSurface3: '#f9a96a', - fgSurface2: '#d38646', - fgSurface1: '#b26824', - stroke3: '#f9a96a', - stroke4: '#ffdfbe', - stroke2: '#946c4e', - stroke1: '#826148', - bgFillDark: '#000', - fgFillDark: '#f3efed', - bgFillInverted2: '#f3efed', - bgFillInverted1: '#ffd3a7', - fgFillInverted: '#211d1b', - surface1: '#5b3a1f', - fgSurface5: '#ffe8cd', - }, - direction: 'lighter', - }, - error: { - ramp: { - bgFill1: '#cc1818', - fgFill: '#f2efef', - bgFill2: '#dd3029', - surface2: '#752e27', - surface6: '#993d34', - surface5: '#8a362e', - surface4: '#85312a', - surface3: '#7a332c', - fgSurface4: '#ffe6e0', - fgSurface3: '#ff9f91', - fgSurface2: '#ff6153', - fgSurface1: '#e1342d', - stroke3: '#ff9f91', - stroke4: '#ffd9d1', - stroke2: '#ab5c52', - stroke1: '#985249', - bgFillDark: '#000', - fgFillDark: '#f2efef', - bgFillInverted2: '#f2efef', - bgFillInverted1: '#ffd0c7', - fgFillInverted: '#231c1b', - surface1: '#6f2922', - fgSurface5: '#ffe6e0', - }, - direction: 'lighter', - }, - }, - '#777777|#d63638': { - background: { - ramp: { - surface2: '#9b9b9b', - bgFill1: '#3b3b3b', - fgFill: '#f0f0f0', - bgFill2: '#2e2e2e', - surface6: '#808080', - surface5: '#8c8c8c', - surface4: '#919191', - surface3: '#a2a2a2', - fgSurface4: '#000', - fgSurface3: '#2e2e2e', - fgSurface2: '#494949', - fgSurface1: '#636363', - stroke3: '#494949', - stroke4: '#2d2d2d', - stroke2: '#848484', - stroke1: '#929292', - bgFillDark: '#000', - fgFillDark: '#f0f0f0', - bgFillInverted2: '#000', - bgFillInverted1: '#1b1b1b', - fgFillInverted: '#f0f0f0', - surface1: '#959595', - fgSurface5: '#000', - }, - direction: 'darker', - }, - primary: { - ramp: { - bgFill1: '#d63638', - fgFill: '#fbfafa', - bgFill2: '#c5222a', - surface2: '#de9a93', - surface6: '#db6d66', - surface5: '#e67c75', - surface4: '#ec827a', - surface3: '#e5a09a', - fgSurface4: '#3d0000', - fgSurface3: '#7d0002', - fgSurface2: '#ae0014', - fgSurface1: '#d13034', - stroke3: '#7d0002', - stroke4: '#410000', - stroke2: '#ab5f59', - stroke1: '#bc6b65', - bgFillDark: '#1e1818', - fgFillDark: '#f2efef', - bgFillInverted2: '#1e1818', - bgFillInverted1: '#5d0000', - fgFillInverted: '#f2efef', - surface1: '#d7938d', - fgSurface5: '#3d0000', - }, - direction: 'darker', - }, - error: { - ramp: { - bgFill1: '#cc1818', - fgFill: '#f2efef', - bgFill2: '#b90000', - surface2: '#e0998f', - surface6: '#df6b5e', - surface5: '#ea7a6d', - surface4: '#f08072', - surface3: '#e89f95', - fgSurface4: '#3d0000', - fgSurface3: '#7d0000', - fgSurface2: '#ae0000', - fgSurface1: '#d62723', - stroke3: '#7d0000', - stroke4: '#410000', - stroke2: '#ad5e54', - stroke1: '#c06a5f', - bgFillDark: '#1e1817', - fgFillDark: '#f2efef', - bgFillInverted2: '#1e1817', - bgFillInverted1: '#5d0000', - fgFillInverted: '#f2efef', - surface1: '#d99288', - fgSurface5: '#3d0000', - }, - direction: 'darker', - }, - }, - '#fcfcfc|#ffd700': { - background: { - ramp: { - surface2: '#fcfcfc', - bgFill1: '#555', - fgFill: '#f0f0f0', - bgFill2: '#484848', - surface6: '#d6d6d6', - surface5: '#e6e6e6', - surface4: '#ededed', - surface3: '#fff', - fgSurface4: '#1e1e1e', - fgSurface3: '#707070', - fgSurface2: '#8d8d8d', - fgSurface1: '#aeaeae', - stroke3: '#8d8d8d', - stroke4: '#6e6e6e', - stroke2: '#dbdbdb', - stroke1: '#f0f0f0', - bgFillDark: '#1e1e1e', - fgFillDark: '#f0f0f0', - bgFillInverted2: '#1e1e1e', - bgFillInverted1: '#2d2d2d', - fgFillInverted: '#f0f0f0', - surface1: '#f4f4f4', - fgSurface5: '#1e1e1e', - }, - direction: 'darker', - }, - primary: { - ramp: { - bgFill1: '#ffd700', - fgFill: '#1f1e1b', - bgFill2: '#eac300', - surface2: '#fff9c8', - surface6: '#e8d178', - surface5: '#f9e184', - surface4: '#ffe890', - surface3: '#fff', - fgSurface4: '#281d00', - fgSurface3: '#836a00', - fgSurface2: '#a58600', - fgSurface1: '#cba600', - stroke3: '#836a00', - stroke4: '#634f00', - stroke2: '#bfb179', - stroke1: '#d1c288', - bgFillDark: '#1f1e1b', - fgFillDark: '#fff1b1', - bgFillInverted2: '#1f1e1b', - bgFillInverted1: '#3a2c00', - fgFillInverted: '#fff1b1', - surface1: '#fff098', - fgSurface5: '#281d00', - }, - direction: 'darker', - }, - error: { - ramp: { - bgFill1: '#cc1818', - fgFill: '#f2efef', - bgFill2: '#b90000', - surface2: '#fff6f5', - surface6: '#f3c9c2', - surface5: '#f7dbd7', - surface4: '#f6e6e3', - surface3: '#fff', - fgSurface4: '#470000', - fgSurface3: '#cc1818', - fgSurface2: '#f74c40', - fgSurface1: '#ff8879', - stroke3: '#cc1818', - stroke4: '#9d0000', - stroke2: '#d39c95', - stroke1: '#dab1aa', - bgFillDark: '#231c1b', - fgFillDark: '#f2efef', - bgFillInverted2: '#231c1b', - bgFillInverted1: '#640000', - fgFillInverted: '#f2efef', - surface1: '#fcece9', - fgSurface5: '#470000', - }, - direction: 'darker', - }, - }, - '#1e1e1e|#00ffff': { - background: { - ramp: { - surface2: '#1e1e1e', - bgFill1: '#959595', - fgFill: '#1e1e1e', - bgFill2: '#a5a5a5', - surface6: '#383838', - surface5: '#2d2d2d', - surface4: '#282828', - surface3: '#242424', - fgSurface4: '#f0f0f0', - fgSurface3: '#b4b4b4', - fgSurface2: '#6e6e6e', - fgSurface1: '#535353', - stroke3: '#6e6e6e', - stroke4: '#8c8c8c', - stroke2: '#343434', - stroke1: '#272727', - bgFillDark: '#000', - fgFillDark: '#f0f0f0', - bgFillInverted2: '#f0f0f0', - bgFillInverted1: '#dbdbdb', - fgFillInverted: '#1e1e1e', - surface1: '#171717', - fgSurface5: '#f0f0f0', - }, - direction: 'lighter', - }, - primary: { - ramp: { - bgFill1: '#00e9e9', - fgFill: '#1c1f1f', - bgFill2: '#3ffefe', - surface2: '#0e2222', - surface6: '#1f3e3d', - surface5: '#193232', - surface4: '#142d2d', - surface3: '#142828', - fgSurface4: '#95ffff', - fgSurface3: '#00e9e9', - fgSurface2: '#007b7c', - fgSurface1: '#005e5e', - stroke3: '#00e9e9', - stroke4: '#fff', - stroke2: '#5f8e8d', - stroke1: '#567f7e', - bgFillDark: '#000', - fgFillDark: '#d9f7f7', - bgFillInverted2: '#d9f7f7', - bgFillInverted1: '#29f3f4', - fgFillInverted: '#1c1f1f', - surface1: '#0a1a1a', - fgSurface5: '#95ffff', - }, - direction: 'lighter', - }, - error: { - ramp: { - bgFill1: '#cc1818', - fgFill: '#f2efef', - bgFill2: '#dd3029', - surface2: '#36100c', - surface6: '#5e241f', - surface5: '#4e1d18', - surface4: '#481713', - surface3: '#3e1713', - fgSurface4: '#ffe6e0', - fgSurface3: '#ff8879', - fgSurface2: '#d32420', - fgSurface1: '#a00', - stroke3: '#ff8879', - stroke4: '#ffc3b9', - stroke2: '#9f5148', - stroke1: '#8c473f', - bgFillDark: '#000', - fgFillDark: '#f2efef', - bgFillInverted2: '#f2efef', - bgFillInverted1: '#ffd0c7', - fgFillInverted: '#231c1b', - surface1: '#290b08', - fgSurface5: '#ffe6e0', - }, - direction: 'lighter', - }, - }, -}; - -/** - * Return the exact theme ramps generated by trunk for the fixed Storybook - * samples. Trunk had four foreground steps, so fgSurface5 mirrors fgSurface4 - * only to keep the comparison data shape compatible with the current branch. - */ -export function getTrunkProductionThemeRamps( - background: string, - primary: string -): TrunkProductionThemeRamps { - const key = `${ background.toLowerCase() }|${ primary.toLowerCase() }`; - const ramps = TRUNK_PRODUCTION_FIXTURES[ key ]; - - if ( ! ramps ) { - throw new Error( `No trunk production fixture exists for ${ key }.` ); - } - - return ramps; -} diff --git a/packages/theme/src/color-ramps/test/__snapshots__/index.test.ts.snap b/packages/theme/src/color-ramps/test/__snapshots__/index.test.ts.snap index b80a69704763d0..48816f67402f73 100644 --- a/packages/theme/src/color-ramps/test/__snapshots__/index.test.ts.snap +++ b/packages/theme/src/color-ramps/test/__snapshots__/index.test.ts.snap @@ -1553,19 +1553,19 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgFillDark": "#f0f0f0", "fgFillInverted": "#1e1e1e", "fgSurface1": "#454545", - "fgSurface2": "#5f5f5f", - "fgSurface3": "#959595", - "fgSurface4": "#b6b6b6", - "fgSurface5": "#f0f0f0", + "fgSurface2": "#5e5e5e", + "fgSurface3": "#757575", + "fgSurface4": "#ddd", + "fgSurface5": "#fdfdfd", "stroke1": "#787878", "stroke2": "#858585", "stroke3": "#d1d1d1", "stroke4": "#fff", "surface1": "#000", "surface2": "#000", - "surface3": "#0d0d0d", - "surface4": "#141414", - "surface5": "#1b1b1b", + "surface3": "#000", + "surface4": "#0b0b0b", + "surface5": "#1a1a1a", "surface6": "#282828", }, "warnings": undefined, @@ -1597,17 +1597,17 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgFillInverted": "#1e1e1e", "fgSurface1": "#464646", "fgSurface2": "#606060", - "fgSurface3": "#969696", - "fgSurface4": "#b6b6b6", - "fgSurface5": "#f0f0f0", + "fgSurface3": "#797979", + "fgSurface4": "#ddd", + "fgSurface5": "#fdfdfd", "stroke1": "#787878", "stroke2": "#858585", "stroke3": "#d1d1d1", "stroke4": "#fff", "surface1": "#000", "surface2": "#030303", - "surface3": "#101010", - "surface4": "#161616", + "surface3": "#0a0a0a", + "surface4": "#111", "surface5": "#1d1d1d", "surface6": "#292929", }, @@ -1640,18 +1640,18 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgFillInverted": "#1e1e1e", "fgSurface1": "#4f4f4f", "fgSurface2": "#696969", - "fgSurface3": "#999", - "fgSurface4": "#b8b8b8", - "fgSurface5": "#f0f0f0", + "fgSurface3": "#868686", + "fgSurface4": "#dedede", + "fgSurface5": "#fdfdfd", "stroke1": "#787878", "stroke2": "#858585", "stroke3": "#d1d1d1", "stroke4": "#fff", - "surface1": "#0c0c0c", + "surface1": "#0d0d0d", "surface2": "#161616", - "surface3": "#1e1e1e", - "surface4": "#222", - "surface5": "#282828", + "surface3": "#1f1f1f", + "surface4": "#202020", + "surface5": "#292929", "surface6": "#323232", }, "warnings": undefined, @@ -1683,9 +1683,9 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgFillInverted": "#1e1e1e", "fgSurface1": "#606060", "fgSurface2": "#7c7c7c", - "fgSurface3": "#a1a1a1", - "fgSurface4": "#bdbdbd", - "fgSurface5": "#f0f0f0", + "fgSurface3": "#9b9b9b", + "fgSurface4": "#e4e4e4", + "fgSurface5": "#fdfdfd", "stroke1": "#787878", "stroke2": "#858585", "stroke3": "#d1d1d1", @@ -1693,8 +1693,8 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "surface1": "#282828", "surface2": "#2e2e2e", "surface3": "#333", - "surface4": "#373737", - "surface5": "#3b3b3b", + "surface4": "#363636", + "surface5": "#3d3d3d", "surface6": "#454545", }, "warnings": undefined, @@ -1724,20 +1724,20 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgFill": "#4d4d4d", "fgFillDark": "#f0f0f0", "fgFillInverted": "#1e1e1e", - "fgSurface1": "#7b7b7b", + "fgSurface1": "#7c7c7c", "fgSurface2": "#9a9a9a", - "fgSurface3": "#bcbcbc", - "fgSurface4": "#c8c8c8", - "fgSurface5": "#f0f0f0", + "fgSurface3": "#bebebe", + "fgSurface4": "#e8e8e8", + "fgSurface5": "#fefefe", "stroke1": "#787878", "stroke2": "#858585", "stroke3": "#d2d2d2", "stroke4": "#fff", - "surface1": "#434343", + "surface1": "#444", "surface2": "#484848", - "surface3": "#4c4c4c", - "surface4": "#505050", - "surface5": "#545454", + "surface3": "#4d4d4d", + "surface4": "#4f4f4f", + "surface5": "#575757", "surface6": "#5e5e5e", }, "warnings": undefined, @@ -1770,8 +1770,8 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgSurface1": "#656565", "fgSurface2": "#4b4b4b", "fgSurface3": "#313131", - "fgSurface4": "#282828", - "fgSurface5": "#050505", + "fgSurface4": "#262626", + "fgSurface5": "#000", "stroke1": "#777", "stroke2": "#6a6a6a", "stroke3": "#313131", @@ -1779,8 +1779,8 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "surface1": "#989898", "surface2": "#9e9e9e", "surface3": "#a5a5a5", - "surface4": "#949494", - "surface5": "#8e8e8e", + "surface4": "#959595", + "surface5": "#8c8c8c", "surface6": "#838383", }, "warnings": undefined, @@ -1813,17 +1813,17 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgSurface1": "#7e7e7e", "fgSurface2": "#626262", "fgSurface3": "#484848", - "fgSurface4": "#404040", - "fgSurface5": "#1e1e1e", + "fgSurface4": "#3e3e3e", + "fgSurface5": "#000", "stroke1": "#929292", "stroke2": "#838383", "stroke3": "#484848", "stroke4": "#2c2c2c", - "surface1": "#b6b6b6", + "surface1": "#b7b7b7", "surface2": "#bebebe", - "surface3": "#c5c5c5", - "surface4": "#b2b2b2", - "surface5": "#acacac", + "surface3": "#c6c6c6", + "surface4": "#b4b4b4", + "surface5": "#a9a9a9", "surface6": "#9f9f9f", }, "warnings": undefined, @@ -1856,8 +1856,8 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgSurface1": "#969696", "fgSurface2": "#787878", "fgSurface3": "#5d5d5d", - "fgSurface4": "#555", - "fgSurface5": "#1e1e1e", + "fgSurface4": "#535353", + "fgSurface5": "#141414", "stroke1": "#acacac", "stroke2": "#9d9d9d", "stroke3": "#5d5d5d", @@ -1865,8 +1865,8 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "surface1": "#d6d6d6", "surface2": "#dedede", "surface3": "#e6e6e6", - "surface4": "#d0d0d0", - "surface5": "#cacaca", + "surface4": "#d2d2d2", + "surface5": "#c7c7c7", "surface6": "#bbb", }, "warnings": undefined, @@ -1898,18 +1898,18 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#717171", - "fgSurface4": "#656565", - "fgSurface5": "#1e1e1e", + "fgSurface3": "#767676", + "fgSurface4": "#555", + "fgSurface5": "#141414", "stroke1": "#c9c9c9", "stroke2": "#b7b7b7", "stroke3": "#717171", "stroke4": "#565656", - "surface1": "#f7f7f7", + "surface1": "#fff", "surface2": "#fff", "surface3": "#fff", - "surface4": "#f0f0f0", - "surface5": "#e8e8e8", + "surface4": "#f2f2f2", + "surface5": "#e5e5e5", "surface6": "#d8d8d8", }, "warnings": undefined, @@ -1940,7 +1940,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFillInverted2": "#eff0f2", "fgFill": "#fefeff", "fgFillDark": "#eff0f2", - "fgFillInverted": "#1a1e28", + "fgFillInverted": "#1a1e27", "fgSurface1": "#1e2dbf", "fgSurface2": "#314ede", "fgSurface3": "#8eb0ff", @@ -1982,7 +1982,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFillInverted2": "#eff0f2", "fgFill": "#fefeff", "fgFillDark": "#eff0f2", - "fgFillInverted": "#1a1e28", + "fgFillInverted": "#1a1e27", "fgSurface1": "#1f2ec0", "fgSurface2": "#324fe0", "fgSurface3": "#8eb0ff", @@ -2108,7 +2108,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFillInverted2": "#eff0f2", "fgFill": "#fefeff", "fgFillDark": "#eff0f2", - "fgFillInverted": "#1a1e28", + "fgFillInverted": "#1a1e27", "fgSurface1": "#466bfd", "fgSurface2": "#6a91ff", "fgSurface3": "#99b8ff", @@ -2187,10 +2187,10 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "ramp": { "bgFill1": "#3858e9", "bgFill2": "#2e49d9", - "bgFillDark": "#1a1e28", + "bgFillDark": "#1a1e27", "bgFillInverted1": "#13009f", - "bgFillInverted2": "#1a1e28", - "fgFill": "#e7e8ea", + "bgFillInverted2": "#1a1e27", + "fgFill": "#e7e8eb", "fgFillDark": "#eff0f2", "fgFillInverted": "#eff0f2", "fgSurface1": "#4b71ff", @@ -2229,10 +2229,10 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "ramp": { "bgFill1": "#3858e9", "bgFill2": "#2e49d9", - "bgFillDark": "#1a1e28", + "bgFillDark": "#1a1e27", "bgFillInverted1": "#13009f", - "bgFillInverted2": "#1a1e28", - "fgFill": "#e7e8ea", + "bgFillInverted2": "#1a1e27", + "fgFill": "#e7e8eb", "fgFillDark": "#eff0f2", "fgFillInverted": "#eff0f2", "fgSurface1": "#6a91ff", @@ -2271,10 +2271,10 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "ramp": { "bgFill1": "#3858e9", "bgFill2": "#2e49d9", - "bgFillDark": "#1a1e28", + "bgFillDark": "#1a1e27", "bgFillInverted1": "#13009f", - "bgFillInverted2": "#1a1e28", - "fgFill": "#e7e8ea", + "bgFillInverted2": "#1a1e27", + "fgFill": "#e7e8eb", "fgFillDark": "#eff0f2", "fgFillInverted": "#eff0f2", "fgSurface1": "#8daeff", @@ -2318,7 +2318,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFillDark": "#000", "bgFillInverted1": "#b8e0ff", "bgFillInverted2": "#eef0f2", - "fgFill": "#12161b", + "fgFill": "#12161a", "fgFillDark": "#eef0f2", "fgFillInverted": "#1a1f24", "fgSurface1": "#004192", @@ -2360,7 +2360,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFillDark": "#000", "bgFillInverted1": "#b8e0ff", "bgFillInverted2": "#eef0f2", - "fgFill": "#12161b", + "fgFill": "#12161a", "fgFillDark": "#eef0f2", "fgFillInverted": "#1a1f24", "fgSurface1": "#004394", @@ -2402,7 +2402,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFillDark": "#000", "bgFillInverted1": "#b8e0ff", "bgFillInverted2": "#eef0f2", - "fgFill": "#12161b", + "fgFill": "#12161a", "fgFillDark": "#eef0f2", "fgFillInverted": "#1a1f24", "fgSurface1": "#004ba3", @@ -2444,7 +2444,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFillDark": "#000", "bgFillInverted1": "#b8e0ff", "bgFillInverted2": "#eef0f2", - "fgFill": "#12161b", + "fgFill": "#12161a", "fgFillDark": "#eef0f2", "fgFillInverted": "#1a1f24", "fgSurface1": "#005dc2", @@ -2486,7 +2486,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFillDark": "#000", "bgFillInverted1": "#b8e0ff", "bgFillInverted2": "#eef0f2", - "fgFill": "#12161b", + "fgFill": "#12161a", "fgFillDark": "#eef0f2", "fgFillInverted": "#1a1f24", "fgSurface1": "#007be8", @@ -2793,7 +2793,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "stroke2": "#466a4c", "stroke3": "#4ab866", "stroke4": "#73df8b", - "surface1": "#051007", + "surface1": "#051107", "surface2": "#081b0c", "surface3": "#0d2312", "surface4": "#0f2815", @@ -2947,7 +2947,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "ramp": { "bgFill1": "#4ab866", "bgFill2": "#37a756", - "bgFillDark": "#1c1f1c", + "bgFillDark": "#1b1f1c", "bgFillInverted1": "#003701", "bgFillInverted2": "#1b1f1c", "fgFill": "#1b1f1c", @@ -2989,7 +2989,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "ramp": { "bgFill1": "#4ab866", "bgFill2": "#37a756", - "bgFillDark": "#1c1f1c", + "bgFillDark": "#1b1f1c", "bgFillInverted1": "#003701", "bgFillInverted2": "#1b1f1c", "fgFill": "#1b1f1c", @@ -3031,7 +3031,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "ramp": { "bgFill1": "#4ab866", "bgFill2": "#37a756", - "bgFillDark": "#1c1f1c", + "bgFillDark": "#1b1f1c", "bgFillInverted1": "#003701", "bgFillInverted2": "#1b1f1c", "fgFill": "#1b1f1c", @@ -3086,7 +3086,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgSurface3": "#f0d148", "fgSurface4": "#fff075", "stroke1": "#817854", - "stroke2": "#8f865e", + "stroke2": "#8e8661", "stroke3": "#f0d148", "stroke4": "#fff", "surface1": "#000", @@ -3128,7 +3128,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgSurface3": "#f0d148", "fgSurface4": "#fff075", "stroke1": "#817854", - "stroke2": "#8f865e", + "stroke2": "#8e8661", "stroke3": "#f0d148", "stroke4": "#fff", "surface1": "#000", @@ -3170,7 +3170,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgSurface3": "#f0d148", "fgSurface4": "#fff075", "stroke1": "#817854", - "stroke2": "#8f865e", + "stroke2": "#8e8661", "stroke3": "#f0d148", "stroke4": "#fff", "surface1": "#0e0c04", @@ -3207,12 +3207,12 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgFill": "#4f4d47", "fgFillDark": "#faf1ca", "fgFillInverted": "#1f1e1b", - "fgSurface1": "#745e00", + "fgSurface1": "#745e01", "fgSurface2": "#957a00", "fgSurface3": "#f0d148", "fgSurface4": "#fff075", "stroke1": "#817854", - "stroke2": "#8f865e", + "stroke2": "#8e8661", "stroke3": "#f0d148", "stroke4": "#fff", "surface1": "#2d2814", @@ -3254,7 +3254,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgSurface3": "#f0d148", "fgSurface4": "#fff075", "stroke1": "#817854", - "stroke2": "#8f865e", + "stroke2": "#8e8661", "stroke3": "#f0d148", "stroke4": "#fff", "surface1": "#4a4325", @@ -3336,10 +3336,10 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgSurface1": "#977c00", "fgSurface2": "#776000", "fgSurface3": "#544814", - "fgSurface4": "#4b4011", - "fgSurface5": "#281d00", - "stroke1": "#9c9269", - "stroke2": "#8e845c", + "fgSurface4": "#493e10", + "fgSurface5": "#000", + "stroke1": "#9d9268", + "stroke2": "#8e855b", "stroke3": "#594700", "stroke4": "#392b00", "surface1": "#c8b76f", @@ -3717,10 +3717,10 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgSurface1": "#a87300", "fgSurface2": "#855900", "fgSurface3": "#5b4416", - "fgSurface4": "#523c13", - "fgSurface5": "#2e1900", - "stroke1": "#a48e69", - "stroke2": "#94805f", + "fgSurface4": "#4f3b12", + "fgSurface5": "#000", + "stroke1": "#a58e67", + "stroke2": "#96805a", "stroke3": "#644100", "stroke4": "#402700", "surface1": "#d5b16f", @@ -4092,7 +4092,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFillDark": "#231c1b", "bgFillInverted1": "#640000", "bgFillInverted2": "#231c1b", - "fgFill": "#e9e6e6", + "fgFill": "#eae6e5", "fgFillDark": "#f2efef", "fgFillInverted": "#f2efef", "fgSurface1": "#e63a32", @@ -4134,7 +4134,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFillDark": "#231c1b", "bgFillInverted1": "#640000", "bgFillInverted2": "#231c1b", - "fgFill": "#e9e6e6", + "fgFill": "#eae6e5", "fgFillDark": "#f2efef", "fgFillInverted": "#f2efef", "fgSurface1": "#ff6052", @@ -4176,7 +4176,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFillDark": "#231c1b", "bgFillInverted1": "#640000", "bgFillInverted2": "#231c1b", - "fgFill": "#e9e6e6", + "fgFill": "#eae6e5", "fgFillDark": "#f2efef", "fgFillInverted": "#f2efef", "fgSurface1": "#ff9182", @@ -15607,18 +15607,18 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#717171", - "fgSurface4": "#656565", - "fgSurface5": "#1e1e1e", + "fgSurface3": "#767676", + "fgSurface4": "#555", + "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#f7f7f7", + "surface1": "#fff", "surface2": "#fff", "surface3": "#fff", - "surface4": "#f0f0f0", - "surface5": "#e8e8e8", + "surface4": "#f2f2f2", + "surface5": "#e5e5e5", "surface6": "#d8d8d8", }, "warnings": undefined, @@ -15643,18 +15643,18 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#717171", - "fgSurface4": "#656565", - "fgSurface5": "#1e1e1e", + "fgSurface3": "#767676", + "fgSurface4": "#555", + "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#f7f7f7", + "surface1": "#fff", "surface2": "#fff", "surface3": "#fff", - "surface4": "#f0f0f0", - "surface5": "#e8e8e8", + "surface4": "#f2f2f2", + "surface5": "#e5e5e5", "surface6": "#d8d8d8", }, "warnings": undefined, @@ -15679,18 +15679,18 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#717171", - "fgSurface4": "#656565", - "fgSurface5": "#1e1e1e", + "fgSurface3": "#767676", + "fgSurface4": "#555", + "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#f7f7f7", + "surface1": "#fff", "surface2": "#fff", "surface3": "#fff", - "surface4": "#f0f0f0", - "surface5": "#e8e8e8", + "surface4": "#f2f2f2", + "surface5": "#e5e5e5", "surface6": "#d8d8d8", }, "warnings": undefined, @@ -15715,18 +15715,18 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#717171", - "fgSurface4": "#656565", - "fgSurface5": "#1e1e1e", + "fgSurface3": "#767676", + "fgSurface4": "#555", + "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#f7f7f7", + "surface1": "#fff", "surface2": "#fff", "surface3": "#fff", - "surface4": "#f0f0f0", - "surface5": "#e8e8e8", + "surface4": "#f2f2f2", + "surface5": "#e5e5e5", "surface6": "#d8d8d8", }, "warnings": undefined, @@ -15751,18 +15751,18 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#717171", - "fgSurface4": "#656565", - "fgSurface5": "#1e1e1e", + "fgSurface3": "#767676", + "fgSurface4": "#555", + "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#f7f7f7", + "surface1": "#fff", "surface2": "#fff", "surface3": "#fff", - "surface4": "#f0f0f0", - "surface5": "#e8e8e8", + "surface4": "#f2f2f2", + "surface5": "#e5e5e5", "surface6": "#d8d8d8", }, "warnings": undefined, @@ -15787,18 +15787,18 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#717171", - "fgSurface4": "#656565", - "fgSurface5": "#1e1e1e", + "fgSurface3": "#767676", + "fgSurface4": "#555", + "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#f7f7f7", + "surface1": "#fff", "surface2": "#fff", "surface3": "#fff", - "surface4": "#f0f0f0", - "surface5": "#e8e8e8", + "surface4": "#f2f2f2", + "surface5": "#e5e5e5", "surface6": "#d8d8d8", }, "warnings": undefined, @@ -15823,18 +15823,18 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#717171", - "fgSurface4": "#656565", - "fgSurface5": "#1e1e1e", + "fgSurface3": "#767676", + "fgSurface4": "#555", + "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#f7f7f7", + "surface1": "#fff", "surface2": "#fff", "surface3": "#fff", - "surface4": "#f0f0f0", - "surface5": "#e8e8e8", + "surface4": "#f2f2f2", + "surface5": "#e5e5e5", "surface6": "#d8d8d8", }, "warnings": undefined, @@ -15859,18 +15859,18 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#717171", - "fgSurface4": "#656565", - "fgSurface5": "#1e1e1e", + "fgSurface3": "#767676", + "fgSurface4": "#555", + "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#f7f7f7", + "surface1": "#fff", "surface2": "#fff", "surface3": "#fff", - "surface4": "#f0f0f0", - "surface5": "#e8e8e8", + "surface4": "#f2f2f2", + "surface5": "#e5e5e5", "surface6": "#d8d8d8", }, "warnings": undefined, @@ -15895,18 +15895,18 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#717171", - "fgSurface4": "#656565", - "fgSurface5": "#1e1e1e", + "fgSurface3": "#767676", + "fgSurface4": "#555", + "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#f7f7f7", + "surface1": "#fff", "surface2": "#fff", "surface3": "#fff", - "surface4": "#f0f0f0", - "surface5": "#e8e8e8", + "surface4": "#f2f2f2", + "surface5": "#e5e5e5", "surface6": "#d8d8d8", }, "warnings": undefined, @@ -15931,18 +15931,18 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#717171", - "fgSurface4": "#656565", - "fgSurface5": "#1e1e1e", + "fgSurface3": "#767676", + "fgSurface4": "#555", + "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#f7f7f7", + "surface1": "#fff", "surface2": "#fff", "surface3": "#fff", - "surface4": "#f0f0f0", - "surface5": "#e8e8e8", + "surface4": "#f2f2f2", + "surface5": "#e5e5e5", "surface6": "#d8d8d8", }, "warnings": undefined, @@ -15967,18 +15967,18 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#717171", - "fgSurface4": "#656565", - "fgSurface5": "#1e1e1e", + "fgSurface3": "#767676", + "fgSurface4": "#555", + "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#f7f7f7", + "surface1": "#fff", "surface2": "#fff", "surface3": "#fff", - "surface4": "#f0f0f0", - "surface5": "#e8e8e8", + "surface4": "#f2f2f2", + "surface5": "#e5e5e5", "surface6": "#d8d8d8", }, "warnings": undefined, @@ -16003,18 +16003,18 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#717171", - "fgSurface4": "#656565", - "fgSurface5": "#1e1e1e", + "fgSurface3": "#767676", + "fgSurface4": "#555", + "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#f7f7f7", + "surface1": "#fff", "surface2": "#fff", "surface3": "#fff", - "surface4": "#f0f0f0", - "surface5": "#e8e8e8", + "surface4": "#f2f2f2", + "surface5": "#e5e5e5", "surface6": "#d8d8d8", }, "warnings": undefined, @@ -16039,18 +16039,18 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#717171", - "fgSurface4": "#656565", - "fgSurface5": "#1e1e1e", + "fgSurface3": "#767676", + "fgSurface4": "#555", + "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#f7f7f7", + "surface1": "#fff", "surface2": "#fff", "surface3": "#fff", - "surface4": "#f0f0f0", - "surface5": "#e8e8e8", + "surface4": "#f2f2f2", + "surface5": "#e5e5e5", "surface6": "#d8d8d8", }, "warnings": undefined, @@ -16075,18 +16075,18 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#717171", - "fgSurface4": "#656565", - "fgSurface5": "#1e1e1e", + "fgSurface3": "#767676", + "fgSurface4": "#555", + "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#f7f7f7", + "surface1": "#fff", "surface2": "#fff", "surface3": "#fff", - "surface4": "#f0f0f0", - "surface5": "#e8e8e8", + "surface4": "#f2f2f2", + "surface5": "#e5e5e5", "surface6": "#d8d8d8", }, "warnings": undefined, @@ -16111,18 +16111,18 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#717171", - "fgSurface4": "#656565", - "fgSurface5": "#1e1e1e", + "fgSurface3": "#767676", + "fgSurface4": "#555", + "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#f7f7f7", + "surface1": "#fff", "surface2": "#fff", "surface3": "#fff", - "surface4": "#f0f0f0", - "surface5": "#e8e8e8", + "surface4": "#f2f2f2", + "surface5": "#e5e5e5", "surface6": "#d8d8d8", }, "warnings": undefined, @@ -16147,18 +16147,18 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#717171", - "fgSurface4": "#656565", - "fgSurface5": "#1e1e1e", + "fgSurface3": "#767676", + "fgSurface4": "#555", + "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#f7f7f7", + "surface1": "#fff", "surface2": "#fff", "surface3": "#fff", - "surface4": "#f0f0f0", - "surface5": "#e8e8e8", + "surface4": "#f2f2f2", + "surface5": "#e5e5e5", "surface6": "#d8d8d8", }, "warnings": undefined, @@ -16183,18 +16183,18 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#717171", - "fgSurface4": "#656565", - "fgSurface5": "#1e1e1e", + "fgSurface3": "#767676", + "fgSurface4": "#555", + "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#f7f7f7", + "surface1": "#fff", "surface2": "#fff", "surface3": "#fff", - "surface4": "#f0f0f0", - "surface5": "#e8e8e8", + "surface4": "#f2f2f2", + "surface5": "#e5e5e5", "surface6": "#d8d8d8", }, "warnings": undefined, @@ -16219,18 +16219,18 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#717171", - "fgSurface4": "#656565", - "fgSurface5": "#1e1e1e", + "fgSurface3": "#767676", + "fgSurface4": "#555", + "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#f7f7f7", + "surface1": "#fff", "surface2": "#fff", "surface3": "#fff", - "surface4": "#f0f0f0", - "surface5": "#e8e8e8", + "surface4": "#f2f2f2", + "surface5": "#e5e5e5", "surface6": "#d8d8d8", }, "warnings": undefined, @@ -16255,18 +16255,18 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#717171", - "fgSurface4": "#656565", - "fgSurface5": "#1e1e1e", + "fgSurface3": "#767676", + "fgSurface4": "#555", + "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#f7f7f7", + "surface1": "#fff", "surface2": "#fff", "surface3": "#fff", - "surface4": "#f0f0f0", - "surface5": "#e8e8e8", + "surface4": "#f2f2f2", + "surface5": "#e5e5e5", "surface6": "#d8d8d8", }, "warnings": undefined, @@ -16291,18 +16291,18 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#717171", - "fgSurface4": "#656565", - "fgSurface5": "#1e1e1e", + "fgSurface3": "#767676", + "fgSurface4": "#555", + "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#f7f7f7", + "surface1": "#fff", "surface2": "#fff", "surface3": "#fff", - "surface4": "#f0f0f0", - "surface5": "#e8e8e8", + "surface4": "#f2f2f2", + "surface5": "#e5e5e5", "surface6": "#d8d8d8", }, "warnings": undefined, @@ -16327,18 +16327,18 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#717171", - "fgSurface4": "#656565", - "fgSurface5": "#1e1e1e", + "fgSurface3": "#767676", + "fgSurface4": "#555", + "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#f7f7f7", + "surface1": "#fff", "surface2": "#fff", "surface3": "#fff", - "surface4": "#f0f0f0", - "surface5": "#e8e8e8", + "surface4": "#f2f2f2", + "surface5": "#e5e5e5", "surface6": "#d8d8d8", }, "warnings": undefined, @@ -16363,18 +16363,18 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#717171", - "fgSurface4": "#656565", - "fgSurface5": "#1e1e1e", + "fgSurface3": "#767676", + "fgSurface4": "#555", + "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#f7f7f7", + "surface1": "#fff", "surface2": "#fff", "surface3": "#fff", - "surface4": "#f0f0f0", - "surface5": "#e8e8e8", + "surface4": "#f2f2f2", + "surface5": "#e5e5e5", "surface6": "#d8d8d8", }, "warnings": undefined, @@ -16399,18 +16399,18 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#717171", - "fgSurface4": "#656565", - "fgSurface5": "#1e1e1e", + "fgSurface3": "#767676", + "fgSurface4": "#555", + "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#f7f7f7", + "surface1": "#fff", "surface2": "#fff", "surface3": "#fff", - "surface4": "#f0f0f0", - "surface5": "#e8e8e8", + "surface4": "#f2f2f2", + "surface5": "#e5e5e5", "surface6": "#d8d8d8", }, "warnings": undefined, @@ -16435,18 +16435,18 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#717171", - "fgSurface4": "#656565", - "fgSurface5": "#1e1e1e", + "fgSurface3": "#767676", + "fgSurface4": "#555", + "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#f7f7f7", + "surface1": "#fff", "surface2": "#fff", "surface3": "#fff", - "surface4": "#f0f0f0", - "surface5": "#e8e8e8", + "surface4": "#f2f2f2", + "surface5": "#e5e5e5", "surface6": "#d8d8d8", }, "warnings": undefined, @@ -16471,18 +16471,18 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#717171", - "fgSurface4": "#656565", - "fgSurface5": "#1e1e1e", + "fgSurface3": "#767676", + "fgSurface4": "#555", + "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#f7f7f7", + "surface1": "#fff", "surface2": "#fff", "surface3": "#fff", - "surface4": "#f0f0f0", - "surface5": "#e8e8e8", + "surface4": "#f2f2f2", + "surface5": "#e5e5e5", "surface6": "#d8d8d8", }, "warnings": undefined, @@ -16507,18 +16507,18 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#717171", - "fgSurface4": "#656565", - "fgSurface5": "#1e1e1e", + "fgSurface3": "#767676", + "fgSurface4": "#555", + "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#f7f7f7", + "surface1": "#fff", "surface2": "#fff", "surface3": "#fff", - "surface4": "#f0f0f0", - "surface5": "#e8e8e8", + "surface4": "#f2f2f2", + "surface5": "#e5e5e5", "surface6": "#d8d8d8", }, "warnings": undefined, @@ -16543,18 +16543,18 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#717171", - "fgSurface4": "#656565", - "fgSurface5": "#1e1e1e", + "fgSurface3": "#767676", + "fgSurface4": "#555", + "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#f7f7f7", + "surface1": "#fff", "surface2": "#fff", "surface3": "#fff", - "surface4": "#f0f0f0", - "surface5": "#e8e8e8", + "surface4": "#f2f2f2", + "surface5": "#e5e5e5", "surface6": "#d8d8d8", }, "warnings": undefined, @@ -16579,18 +16579,18 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#717171", - "fgSurface4": "#656565", - "fgSurface5": "#1e1e1e", + "fgSurface3": "#767676", + "fgSurface4": "#555", + "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#f7f7f7", + "surface1": "#fff", "surface2": "#fff", "surface3": "#fff", - "surface4": "#f0f0f0", - "surface5": "#e8e8e8", + "surface4": "#f2f2f2", + "surface5": "#e5e5e5", "surface6": "#d8d8d8", }, "warnings": undefined, @@ -16615,18 +16615,18 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#717171", - "fgSurface4": "#656565", - "fgSurface5": "#1e1e1e", + "fgSurface3": "#767676", + "fgSurface4": "#555", + "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#f7f7f7", + "surface1": "#fff", "surface2": "#fff", "surface3": "#fff", - "surface4": "#f0f0f0", - "surface5": "#e8e8e8", + "surface4": "#f2f2f2", + "surface5": "#e5e5e5", "surface6": "#d8d8d8", }, "warnings": undefined, @@ -16651,18 +16651,18 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#717171", - "fgSurface4": "#656565", - "fgSurface5": "#1e1e1e", + "fgSurface3": "#767676", + "fgSurface4": "#555", + "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#f7f7f7", + "surface1": "#fff", "surface2": "#fff", "surface3": "#fff", - "surface4": "#f0f0f0", - "surface5": "#e8e8e8", + "surface4": "#f2f2f2", + "surface5": "#e5e5e5", "surface6": "#d8d8d8", }, "warnings": undefined, @@ -16687,18 +16687,18 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#717171", - "fgSurface4": "#656565", - "fgSurface5": "#1e1e1e", + "fgSurface3": "#767676", + "fgSurface4": "#555", + "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#f7f7f7", + "surface1": "#fff", "surface2": "#fff", "surface3": "#fff", - "surface4": "#f0f0f0", - "surface5": "#e8e8e8", + "surface4": "#f2f2f2", + "surface5": "#e5e5e5", "surface6": "#d8d8d8", }, "warnings": undefined, @@ -16723,18 +16723,18 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#717171", - "fgSurface4": "#656565", - "fgSurface5": "#1e1e1e", + "fgSurface3": "#767676", + "fgSurface4": "#555", + "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#f7f7f7", + "surface1": "#fff", "surface2": "#fff", "surface3": "#fff", - "surface4": "#f0f0f0", - "surface5": "#e8e8e8", + "surface4": "#f2f2f2", + "surface5": "#e5e5e5", "surface6": "#d8d8d8", }, "warnings": undefined, @@ -16759,18 +16759,18 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#717171", - "fgSurface4": "#656565", - "fgSurface5": "#1e1e1e", + "fgSurface3": "#767676", + "fgSurface4": "#555", + "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#f7f7f7", + "surface1": "#fff", "surface2": "#fff", "surface3": "#fff", - "surface4": "#f0f0f0", - "surface5": "#e8e8e8", + "surface4": "#f2f2f2", + "surface5": "#e5e5e5", "surface6": "#d8d8d8", }, "warnings": undefined, @@ -16795,18 +16795,18 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#717171", - "fgSurface4": "#656565", - "fgSurface5": "#1e1e1e", + "fgSurface3": "#767676", + "fgSurface4": "#555", + "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#f7f7f7", + "surface1": "#fff", "surface2": "#fff", "surface3": "#fff", - "surface4": "#f0f0f0", - "surface5": "#e8e8e8", + "surface4": "#f2f2f2", + "surface5": "#e5e5e5", "surface6": "#d8d8d8", }, "warnings": undefined, @@ -16831,18 +16831,18 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#717171", - "fgSurface4": "#656565", - "fgSurface5": "#1e1e1e", + "fgSurface3": "#767676", + "fgSurface4": "#555", + "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#f7f7f7", + "surface1": "#fff", "surface2": "#fff", "surface3": "#fff", - "surface4": "#f0f0f0", - "surface5": "#e8e8e8", + "surface4": "#f2f2f2", + "surface5": "#e5e5e5", "surface6": "#d8d8d8", }, "warnings": undefined, @@ -16867,18 +16867,18 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#717171", - "fgSurface4": "#656565", - "fgSurface5": "#1e1e1e", + "fgSurface3": "#767676", + "fgSurface4": "#555", + "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#f7f7f7", + "surface1": "#fff", "surface2": "#fff", "surface3": "#fff", - "surface4": "#f0f0f0", - "surface5": "#e8e8e8", + "surface4": "#f2f2f2", + "surface5": "#e5e5e5", "surface6": "#d8d8d8", }, "warnings": undefined, diff --git a/packages/theme/src/color-ramps/test/index.test.ts b/packages/theme/src/color-ramps/test/index.test.ts index 52177e4e322c49..a910cba6966bb4 100644 --- a/packages/theme/src/color-ramps/test/index.test.ts +++ b/packages/theme/src/color-ramps/test/index.test.ts @@ -60,6 +60,9 @@ const foregroundSteps = [ 'fgSurface5', ] as const; +const MINIMUM_INTERACTION_STATE_APCA_INTERVAL = 14; +const MAXIMUM_ALTERNATE_POLARITY_DELTA_E_DRIFT = 0.02; + const perceptualSampleCombinations = [ { background: DEFAULT_SEED_COLORS.background, diff --git a/packages/theme/src/test/__snapshots__/build-plugin-parity.test.ts.snap b/packages/theme/src/test/__snapshots__/build-plugin-parity.test.ts.snap index a1137188d32386..4033cf65d77770 100644 --- a/packages/theme/src/test/__snapshots__/build-plugin-parity.test.ts.snap +++ b/packages/theme/src/test/__snapshots__/build-plugin-parity.test.ts.snap @@ -2,7 +2,7 @@ exports[`design token fallback build plugin parity > keeps esbuild and Vite source-text transforms aligned 1`] = ` "// Source text is transformed consistently: var(--wpds-border-radius-sm). -export const templateStyles = \`color: var(--wpds-color-foreground-content-neutral, #1e1e1e);\`; +export const templateStyles = \`color: var(--wpds-color-foreground-content-neutral, #141414);\`; export const doubleQuotedStyles = "gap: var(--wpds-dimension-gap-sm, 8px); content: 'fixture';"; export const singleQuotedStyles = "radius: var(--wpds-border-radius-sm, 2px);"; @@ -13,7 +13,7 @@ export const manualFallback = 'radius: var(--wpds-border-radius-sm, 99px);'; exports[`design token fallback build plugin parity > transforms supported CSS in styles.css 1`] = ` "/* Token references in CSS comments are not transformed: var(--wpds-border-radius-sm). */ .fixture { - color: var(--wpds-color-foreground-content-neutral, #1e1e1e); + color: var(--wpds-color-foreground-content-neutral, #141414); content: "var(--wpds-border-radius-sm, 2px)"; } " @@ -28,7 +28,7 @@ exports[`design token fallback build plugin parity > transforms supported CSS in exports[`design token fallback build plugin parity > transforms supported CSS with Lightning CSS in styles.css 1`] = ` ".fixture { - color: var(--wpds-color-foreground-content-neutral, #1e1e1e); + color: var(--wpds-color-foreground-content-neutral, #141414); content: "var(--wpds-border-radius-sm)"; } " diff --git a/packages/theme/src/test/theme-provider.browser.test.tsx b/packages/theme/src/test/theme-provider.browser.test.tsx index f90416513fac1e..f5949109b26fe6 100644 --- a/packages/theme/src/test/theme-provider.browser.test.tsx +++ b/packages/theme/src/test/theme-provider.browser.test.tsx @@ -96,7 +96,7 @@ describe( 'ThemeProvider', () => { expect( readProp( provider, SURFACE_BG ) ).toBe( BACKGROUND ); } ); - it( 'maps resting and active neutral foregrounds to separate scale steps', async () => { + it( 'maps normal content and resting neutral foregrounds to the same scale step', async () => { await render(
x
@@ -110,7 +110,7 @@ describe( 'ThemeProvider', () => { FOREGROUND_NEUTRAL_ACTIVE ); - expect( activeForeground ).toBe( + expect( restingForeground ).toBe( readProp( provider, FOREGROUND_CONTENT_NEUTRAL ) ); expect( activeForeground ).toBe( diff --git a/packages/theme/tokens/color.json b/packages/theme/tokens/color.json index cd475e104456a5..d907a647e2d865 100644 --- a/packages/theme/tokens/color.json +++ b/packages/theme/tokens/color.json @@ -48,8 +48,8 @@ "surface5": { "$value": { "colorSpace": "srgb", - "components": [ 0.863, 0.89, 0.957 ], - "hex": "#dce3f4" + "components": [ 0.847, 0.875, 0.941 ], + "hex": "#d8dff0" } }, "surface4": { @@ -69,22 +69,22 @@ "fgSurface5": { "$value": { "colorSpace": "srgb", - "components": [ 0.043, 0, 0.439 ], - "hex": "#0b0070" + "components": [ 0.012, 0.024, 0.231 ], + "hex": "#03063b" } }, "fgSurface4": { "$value": { "colorSpace": "srgb", - "components": [ 0.2, 0.318, 0.91 ], - "hex": "#3351e8" + "components": [ 0.133, 0.18, 0.855 ], + "hex": "#222eda" } }, "fgSurface3": { "$value": { "colorSpace": "srgb", - "components": [ 0.243, 0.376, 0.918 ], - "hex": "#3e60ea" + "components": [ 0.247, 0.38, 0.918 ], + "hex": "#3f61ea" } }, "fgSurface2": { @@ -167,8 +167,8 @@ "surface1": { "$value": { "colorSpace": "srgb", - "components": [ 0.922, 0.941, 0.988 ], - "hex": "#ebf0fc" + "components": [ 0.929, 0.945, 0.988 ], + "hex": "#edf1fc" } } }, @@ -218,8 +218,8 @@ "surface4": { "$value": { "colorSpace": "srgb", - "components": [ 0.871, 0.922, 0.98 ], - "hex": "#deebfa" + "components": [ 0.878, 0.929, 0.988 ], + "hex": "#e0edfc" } }, "surface3": { @@ -232,22 +232,22 @@ "fgSurface5": { "$value": { "colorSpace": "srgb", - "components": [ 0, 0.106, 0.31 ], - "hex": "#001b4f" + "components": [ 0, 0.075, 0.165 ], + "hex": "#00132a" } }, "fgSurface4": { "$value": { "colorSpace": "srgb", - "components": [ 0, 0.388, 0.698 ], - "hex": "#0063b2" + "components": [ 0, 0.314, 0.573 ], + "hex": "#005092" } }, "fgSurface3": { "$value": { "colorSpace": "srgb", - "components": [ 0, 0.431, 0.776 ], - "hex": "#006ec6" + "components": [ 0, 0.439, 0.784 ], + "hex": "#0070c8" } }, "fgSurface2": { @@ -281,8 +281,8 @@ "stroke2": { "$value": { "colorSpace": "srgb", - "components": [ 0.584, 0.71, 0.851 ], - "hex": "#95b5d9" + "components": [ 0.6, 0.702, 0.816 ], + "hex": "#99b3d0" } }, "stroke1": { @@ -395,15 +395,15 @@ "fgSurface5": { "$value": { "colorSpace": "srgb", - "components": [ 0, 0.161, 0 ], - "hex": "#002900" + "components": [ 0.016, 0.09, 0.031 ], + "hex": "#041708" } }, "fgSurface4": { "$value": { "colorSpace": "srgb", - "components": [ 0.169, 0.443, 0.239 ], - "hex": "#2b713d" + "components": [ 0.129, 0.357, 0.184 ], + "hex": "#215b2f" } }, "fgSurface3": { @@ -444,8 +444,8 @@ "stroke2": { "$value": { "colorSpace": "srgb", - "components": [ 0.518, 0.753, 0.561 ], - "hex": "#84c08f" + "components": [ 0.506, 0.757, 0.553 ], + "hex": "#81c18d" } }, "stroke1": { @@ -537,15 +537,15 @@ "surface5": { "$value": { "colorSpace": "srgb", - "components": [ 0.98, 0.871, 0.678 ], - "hex": "#fadead" + "components": [ 0.965, 0.855, 0.667 ], + "hex": "#f6daaa" } }, "surface4": { "$value": { "colorSpace": "srgb", - "components": [ 0.992, 0.902, 0.745 ], - "hex": "#fde6be" + "components": [ 1, 0.91, 0.753 ], + "hex": "#ffe8c0" } }, "surface3": { @@ -558,15 +558,15 @@ "fgSurface5": { "$value": { "colorSpace": "srgb", - "components": [ 0.18, 0.098, 0 ], - "hex": "#2e1900" + "components": [ 0.106, 0.071, 0.012 ], + "hex": "#1b1203" } }, "fgSurface4": { "$value": { "colorSpace": "srgb", - "components": [ 0.482, 0.365, 0.129 ], - "hex": "#7b5d21" + "components": [ 0.396, 0.298, 0.098 ], + "hex": "#654c19" } }, "fgSurface3": { @@ -700,8 +700,8 @@ "surface5": { "$value": { "colorSpace": "srgb", - "components": [ 0.969, 0.859, 0.843 ], - "hex": "#f7dbd7" + "components": [ 0.961, 0.851, 0.835 ], + "hex": "#f5d9d5" } }, "surface4": { @@ -721,22 +721,22 @@ "fgSurface5": { "$value": { "colorSpace": "srgb", - "components": [ 0.278, 0, 0 ], - "hex": "#470000" + "components": [ 0.149, 0.004, 0.004 ], + "hex": "#260101" } }, "fgSurface4": { "$value": { "colorSpace": "srgb", - "components": [ 0.745, 0.086, 0.086 ], - "hex": "#be1616" + "components": [ 0.596, 0.059, 0.059 ], + "hex": "#980f0f" } }, "fgSurface3": { "$value": { "colorSpace": "srgb", - "components": [ 0.843, 0.102, 0.102 ], - "hex": "#d71a1a" + "components": [ 0.851, 0.102, 0.102 ], + "hex": "#d91a1a" } }, "fgSurface2": { @@ -791,8 +791,8 @@ "fgFillDark": { "$value": { "colorSpace": "srgb", - "components": [ 0.953, 0.937, 0.937 ], - "hex": "#f3efef" + "components": [ 0.949, 0.937, 0.937 ], + "hex": "#f2efef" } }, "bgFillInverted2": { @@ -812,15 +812,15 @@ "fgFillInverted": { "$value": { "colorSpace": "srgb", - "components": [ 0.953, 0.937, 0.937 ], - "hex": "#f3efef" + "components": [ 0.949, 0.937, 0.937 ], + "hex": "#f2efef" } }, "surface1": { "$value": { "colorSpace": "srgb", - "components": [ 0.996, 0.922, 0.91 ], - "hex": "#feebe8" + "components": [ 0.996, 0.933, 0.922 ], + "hex": "#feeeeb" } } }, @@ -863,15 +863,15 @@ "surface5": { "$value": { "colorSpace": "srgb", - "components": [ 0.902, 0.902, 0.902 ], - "hex": "#e6e6e6" + "components": [ 0.89, 0.89, 0.89 ], + "hex": "#e3e3e3" } }, "surface4": { "$value": { "colorSpace": "srgb", - "components": [ 0.929, 0.929, 0.929 ], - "hex": "#ededed" + "components": [ 0.937, 0.937, 0.937 ], + "hex": "#efefef" } }, "surface3": { @@ -884,22 +884,22 @@ "fgSurface5": { "$value": { "colorSpace": "srgb", - "components": [ 0.118, 0.118, 0.118 ], - "hex": "#1e1e1e" + "components": [ 0.078, 0.078, 0.078 ], + "hex": "#141414" } }, "fgSurface4": { "$value": { "colorSpace": "srgb", - "components": [ 0.392, 0.392, 0.392 ], - "hex": "#646464" + "components": [ 0.314, 0.314, 0.314 ], + "hex": "#505050" } }, "fgSurface3": { "$value": { "colorSpace": "srgb", - "components": [ 0.439, 0.439, 0.439 ], - "hex": "#707070" + "components": [ 0.451, 0.451, 0.451 ], + "hex": "#737373" } }, "fgSurface2": { @@ -982,8 +982,8 @@ "surface1": { "$value": { "colorSpace": "srgb", - "components": [ 0.957, 0.957, 0.957 ], - "hex": "#f4f4f4" + "components": [ 0.976, 0.976, 0.976 ], + "hex": "#f9f9f9" } } }, @@ -1026,15 +1026,15 @@ "surface5": { "$value": { "colorSpace": "srgb", - "components": [ 0.969, 0.886, 0.541 ], - "hex": "#f7e28a" + "components": [ 0.953, 0.871, 0.525 ], + "hex": "#f3de86" } }, "surface4": { "$value": { "colorSpace": "srgb", - "components": [ 0.996, 0.914, 0.584 ], - "hex": "#fee995" + "components": [ 1, 0.918, 0.588 ], + "hex": "#ffea96" } }, "surface3": { @@ -1047,15 +1047,15 @@ "fgSurface5": { "$value": { "colorSpace": "srgb", - "components": [ 0.157, 0.114, 0 ], - "hex": "#281d00" + "components": [ 0.094, 0.078, 0.008 ], + "hex": "#181402" } }, "fgSurface4": { "$value": { "colorSpace": "srgb", - "components": [ 0.443, 0.38, 0.114 ], - "hex": "#71611d" + "components": [ 0.361, 0.31, 0.086 ], + "hex": "#5c4f16" } }, "fgSurface3": { @@ -1075,8 +1075,8 @@ "fgSurface1": { "$value": { "colorSpace": "srgb", - "components": [ 0.776, 0.659, 0 ], - "hex": "#c6a800" + "components": [ 0.78, 0.659, 0 ], + "hex": "#c7a800" } }, "stroke3": { @@ -1096,8 +1096,8 @@ "stroke2": { "$value": { "colorSpace": "srgb", - "components": [ 0.741, 0.694, 0.494 ], - "hex": "#bdb17e" + "components": [ 0.745, 0.694, 0.486 ], + "hex": "#beb17c" } }, "stroke1": { From 8d569e29655e5477f924a1cec04b2807af179294 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Thu, 3 Sep 2026 01:35:26 +0200 Subject: [PATCH 11/44] Theme: Align normal foreground semantic tokens --- packages/theme/CHANGELOG.md | 2 +- packages/theme/prebuilt/css/design-tokens.css | 6 +++--- .../theme/src/prebuilt/ts/color-tokens.ts | 21 +++++++++++-------- .../build-plugin-parity.test.ts.snap | 6 +++--- packages/theme/tokens/color.json | 12 +++++------ 5 files changed, 25 insertions(+), 22 deletions(-) diff --git a/packages/theme/CHANGELOG.md b/packages/theme/CHANGELOG.md index e1f24fdee3ab64..2fcb16ec92e2d9 100644 --- a/packages/theme/CHANGELOG.md +++ b/packages/theme/CHANGELOG.md @@ -58,7 +58,7 @@ ### Enhancements -- Generate perception-aware color ramps with ordered surface and stroke progressions, five foreground steps, stronger active interactive colors, and unchanged WCAG contrast floors. ([#82294](https://github.com/WordPress/gutenberg/pull/82294)) +- Generate perception-aware color ramps with ordered surface and stroke progressions, align normal content with resting interactive colors, reserve the fifth foreground step for interaction states, and keep WCAG contrast floors unchanged. ([#82294](https://github.com/WordPress/gutenberg/pull/82294)) - Add a Lightning CSS visitor plugin that injects design system token fallbacks, available as `@wordpress/theme/lightningcss-plugins/lightningcss-ds-token-fallbacks`, with an optional peer range of `>=1.33.0 <2.0.0`. ([#80401](https://github.com/WordPress/gutenberg/pull/80401)) ### Bug Fixes diff --git a/packages/theme/prebuilt/css/design-tokens.css b/packages/theme/prebuilt/css/design-tokens.css index 62a07d70d120c9..9322a8c80d8902 100644 --- a/packages/theme/prebuilt/css/design-tokens.css +++ b/packages/theme/prebuilt/css/design-tokens.css @@ -130,15 +130,15 @@ /* Foreground color for content like text with info tone and weak emphasis. */ --wpds-color-foreground-content-info-weak: #006bd7; /* Foreground color for content like text with normal emphasis. */ - --wpds-color-foreground-content-neutral: #141414; + --wpds-color-foreground-content-neutral: #505050; /* Foreground color for content like text with weak emphasis. */ --wpds-color-foreground-content-neutral-weak: #737373; /* Foreground color for content like text with success tone and normal emphasis. */ - --wpds-color-foreground-content-success: #041708; + --wpds-color-foreground-content-success: #215b2f; /* Foreground color for content like text with success tone and weak emphasis. */ --wpds-color-foreground-content-success-weak: #008030; /* Foreground color for content like text with warning tone and normal emphasis. */ - --wpds-color-foreground-content-warning: #1b1203; + --wpds-color-foreground-content-warning: #654c19; /* Foreground color for content like text with warning tone and weak emphasis. */ --wpds-color-foreground-content-warning-weak: #936300; /* Foreground color for interactive elements with brand tone and normal emphasis. */ diff --git a/packages/theme/src/prebuilt/ts/color-tokens.ts b/packages/theme/src/prebuilt/ts/color-tokens.ts index fa4ec37471853a..fc25a7c49ba0ba 100644 --- a/packages/theme/src/prebuilt/ts/color-tokens.ts +++ b/packages/theme/src/prebuilt/ts/color-tokens.ts @@ -35,19 +35,19 @@ export default { 'primary-surface1': [ 'background-surface-brand' ], 'info-surface2': [ 'background-surface-info-weak' ], 'info-surface4': [ 'background-surface-info' ], - 'info-fgSurface5': [ 'foreground-content-info' ], + 'info-fgSurface4': [ 'foreground-content-info' ], 'info-fgSurface3': [ 'foreground-content-info-weak' ], 'info-stroke3': [ 'stroke-surface-info-strong' ], 'info-stroke1': [ 'stroke-surface-info' ], 'success-surface2': [ 'background-surface-success-weak' ], 'success-surface4': [ 'background-surface-success' ], - 'success-fgSurface5': [ 'foreground-content-success' ], + 'success-fgSurface4': [ 'foreground-content-success' ], 'success-fgSurface3': [ 'foreground-content-success-weak' ], 'success-stroke3': [ 'stroke-surface-success-strong' ], 'success-stroke1': [ 'stroke-surface-success' ], 'warning-surface2': [ 'background-surface-warning-weak' ], 'warning-surface4': [ 'background-surface-warning' ], - 'warning-fgSurface5': [ 'foreground-content-warning' ], + 'warning-fgSurface4': [ 'foreground-content-warning' ], 'warning-fgSurface3': [ 'foreground-content-warning-weak' ], 'warning-stroke3': [ 'stroke-surface-warning-strong' ], 'warning-stroke1': [ 'stroke-surface-warning' ], @@ -65,11 +65,11 @@ export default { 'background-interactive-error-weak-active', 'background-surface-error', ], - 'error-fgSurface5': [ + 'error-fgSurface5': [ 'foreground-interactive-error-active' ], + 'error-fgSurface4': [ 'foreground-content-error', - 'foreground-interactive-error-active', + 'foreground-interactive-error', ], - 'error-fgSurface4': [ 'foreground-interactive-error' ], 'error-fgSurface3': [ 'foreground-content-error-weak' ], 'error-stroke3': [ 'stroke-interactive-error', @@ -87,11 +87,14 @@ export default { 'bg-surface4': [ 'background-interactive-neutral-weak-active' ], 'bg-surface3': [ 'background-surface-neutral-strong' ], 'bg-fgSurface5': [ - 'foreground-content-neutral', 'foreground-interactive-neutral-active', 'foreground-interactive-neutral-weak-active', ], - 'bg-fgSurface4': [ 'background-thumb-neutral', 'foreground-interactive-neutral' ], + 'bg-fgSurface4': [ + 'background-thumb-neutral', + 'foreground-content-neutral', + 'foreground-interactive-neutral', + ], 'bg-fgSurface3': [ 'foreground-content-neutral-weak', 'foreground-interactive-neutral-weak', @@ -137,7 +140,7 @@ export default { 'bg-surface1': [ 'background-surface-neutral-weak' ], 'caution-surface2': [ 'background-surface-caution-weak' ], 'caution-surface4': [ 'background-surface-caution' ], - 'caution-fgSurface5': [ 'foreground-content-caution' ], + 'caution-fgSurface4': [ 'foreground-content-caution' ], 'caution-fgSurface3': [ 'foreground-content-caution-weak' ], 'caution-stroke3': [ 'stroke-surface-caution-strong' ], 'caution-stroke1': [ 'stroke-surface-caution' ], diff --git a/packages/theme/src/test/__snapshots__/build-plugin-parity.test.ts.snap b/packages/theme/src/test/__snapshots__/build-plugin-parity.test.ts.snap index 4033cf65d77770..7d667ec18bf154 100644 --- a/packages/theme/src/test/__snapshots__/build-plugin-parity.test.ts.snap +++ b/packages/theme/src/test/__snapshots__/build-plugin-parity.test.ts.snap @@ -2,7 +2,7 @@ exports[`design token fallback build plugin parity > keeps esbuild and Vite source-text transforms aligned 1`] = ` "// Source text is transformed consistently: var(--wpds-border-radius-sm). -export const templateStyles = \`color: var(--wpds-color-foreground-content-neutral, #141414);\`; +export const templateStyles = \`color: var(--wpds-color-foreground-content-neutral, #505050);\`; export const doubleQuotedStyles = "gap: var(--wpds-dimension-gap-sm, 8px); content: 'fixture';"; export const singleQuotedStyles = "radius: var(--wpds-border-radius-sm, 2px);"; @@ -13,7 +13,7 @@ export const manualFallback = 'radius: var(--wpds-border-radius-sm, 99px);'; exports[`design token fallback build plugin parity > transforms supported CSS in styles.css 1`] = ` "/* Token references in CSS comments are not transformed: var(--wpds-border-radius-sm). */ .fixture { - color: var(--wpds-color-foreground-content-neutral, #141414); + color: var(--wpds-color-foreground-content-neutral, #505050); content: "var(--wpds-border-radius-sm, 2px)"; } " @@ -28,7 +28,7 @@ exports[`design token fallback build plugin parity > transforms supported CSS in exports[`design token fallback build plugin parity > transforms supported CSS with Lightning CSS in styles.css 1`] = ` ".fixture { - color: var(--wpds-color-foreground-content-neutral, #141414); + color: var(--wpds-color-foreground-content-neutral, #505050); content: "var(--wpds-border-radius-sm)"; } " diff --git a/packages/theme/tokens/color.json b/packages/theme/tokens/color.json index d907a647e2d865..b2ef5a6bfaab90 100644 --- a/packages/theme/tokens/color.json +++ b/packages/theme/tokens/color.json @@ -1343,7 +1343,7 @@ "foreground": { "content": { "neutral": { - "$value": "{wpds-color.primitive.bg.fgSurface5}", + "$value": "{wpds-color.primitive.bg.fgSurface4}", "$description": "Foreground color for content like text with normal emphasis." }, "neutral-weak": { @@ -1351,7 +1351,7 @@ "$description": "Foreground color for content like text with weak emphasis." }, "success": { - "$value": "{wpds-color.primitive.success.fgSurface5}", + "$value": "{wpds-color.primitive.success.fgSurface4}", "$description": "Foreground color for content like text with success tone and normal emphasis." }, "success-weak": { @@ -1359,7 +1359,7 @@ "$description": "Foreground color for content like text with success tone and weak emphasis." }, "info": { - "$value": "{wpds-color.primitive.info.fgSurface5}", + "$value": "{wpds-color.primitive.info.fgSurface4}", "$description": "Foreground color for content like text with info tone and normal emphasis." }, "info-weak": { @@ -1367,7 +1367,7 @@ "$description": "Foreground color for content like text with info tone and weak emphasis." }, "warning": { - "$value": "{wpds-color.primitive.warning.fgSurface5}", + "$value": "{wpds-color.primitive.warning.fgSurface4}", "$description": "Foreground color for content like text with warning tone and normal emphasis." }, "warning-weak": { @@ -1375,7 +1375,7 @@ "$description": "Foreground color for content like text with warning tone and weak emphasis." }, "caution": { - "$value": "{wpds-color.primitive.caution.fgSurface5}", + "$value": "{wpds-color.primitive.caution.fgSurface4}", "$description": "Foreground color for content like text with caution tone and normal emphasis." }, "caution-weak": { @@ -1383,7 +1383,7 @@ "$description": "Foreground color for content like text with caution tone and weak emphasis." }, "error": { - "$value": "{wpds-color.primitive.error.fgSurface5}", + "$value": "{wpds-color.primitive.error.fgSurface4}", "$description": "Foreground color for content like text with error tone and normal emphasis." }, "error-weak": { From b214dd59f65ed3db1809c55077d6d8404a9cdda8 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Thu, 3 Sep 2026 01:35:48 +0200 Subject: [PATCH 12/44] Theme: Improve color ramp calculation performance --- packages/theme/benchmark/README.md | 18 +- packages/theme/benchmark/color-ramps.ts | 34 ++-- .../color-ramps/lib/build-foreground-scale.ts | 169 +++++++++--------- .../theme/src/color-ramps/lib/color-utils.ts | 9 +- 4 files changed, 127 insertions(+), 103 deletions(-) diff --git a/packages/theme/benchmark/README.md b/packages/theme/benchmark/README.md index 6dcd1b1796b310..a88a0b603c3a80 100644 --- a/packages/theme/benchmark/README.md +++ b/packages/theme/benchmark/README.md @@ -1,10 +1,11 @@ # Color ramp benchmark -This benchmark measures uncached color-ramp calculation. It covers one -background ramp, one accent ramp against a precomputed background, and the full -`ThemeProvider` ramp workload of one background plus six accent ramps. Each case -cycles through the default light seed, a dark `#1e1e1e` background, and the -Ectoplasm admin color scheme. +This benchmark calls the color-ramp builders directly, bypassing the +`ThemeProvider` whole-ramp memoization while retaining internal algorithm +caches. It covers one background ramp, one accent ramp against a precomputed +background, and the full `ThemeProvider` ramp workload of one background plus +six accent ramps. Each case cycles through the default light seed, a dark +`#1e1e1e` background, and the Ectoplasm admin color scheme. Run it from the repository root: @@ -23,6 +24,7 @@ npm run benchmark:color-ramps --workspace @wordpress/theme -- \ ``` Both worktrees need their dependencies installed. Add `--json` for -machine-readable output. Compare runs made with the same Node.js version and -similar system load. The benchmark reports measurements but does not enforce a -timing threshold because local and CI timings vary. +machine-readable output. The checksum covers every generated ramp token so an +optimization can be checked for output drift. Compare runs made with the same +Node.js version and similar system load. The benchmark reports measurements but +does not enforce a timing threshold because local and CI timings vary. diff --git a/packages/theme/benchmark/color-ramps.ts b/packages/theme/benchmark/color-ramps.ts index ad0901f5e71af3..f5b8b9a5e7d05f 100644 --- a/packages/theme/benchmark/color-ramps.ts +++ b/packages/theme/benchmark/color-ramps.ts @@ -6,6 +6,7 @@ import { parseArgs } from 'node:util'; import { fileURLToPath, pathToFileURL } from 'node:url'; type RampModule = typeof import('../src/color-ramps/index.ts'); +type RampOutput = ReturnType< RampModule[ 'buildBgRamp' ] >; type BenchmarkResult = { name: string; @@ -31,7 +32,7 @@ type BenchmarkReport = { type BenchmarkCase = { name: string; iterationsPerSample: number; - run: () => string; + run: () => RampOutput | RampOutput[]; }; const WARMUP_SAMPLES = 8; @@ -103,7 +104,7 @@ function createCases( rampModule: RampModule ): BenchmarkCase[] { iterationsPerSample: 20, run: () => { const fixture = FIXTURES[ backgroundIndex++ % FIXTURES.length ]; - return buildBgRamp( fixture.background ).ramp.fgFill; + return buildBgRamp( fixture.background ); }, }, { @@ -114,7 +115,7 @@ function createCases( rampModule: RampModule ): BenchmarkCase[] { return buildAccentRamp( FIXTURES[ fixtureIndex ].primary, accentBackgroundRamps[ fixtureIndex ] - ).ramp.fgFill; + ); }, }, { @@ -131,11 +132,10 @@ function createCases( rampModule: RampModule ): BenchmarkCase[] { DEFAULT_SEED_COLORS.warning, DEFAULT_SEED_COLORS.error, ]; - let result = ''; + const result = [ backgroundRamp ]; for ( const seed of accentSeeds ) { - result = buildAccentRamp( seed, backgroundRamp ).ramp - .fgFill; + result.push( buildAccentRamp( seed, backgroundRamp ) ); } return result; }, @@ -143,10 +143,24 @@ function createCases( rampModule: RampModule ): BenchmarkCase[] { ]; } +function updateChecksum( checksum: number, output: RampOutput | RampOutput[] ) { + const ramps = Array.isArray( output ) ? output : [ output ]; + for ( const { ramp } of ramps ) { + for ( const color of Object.values( ramp ) ) { + for ( const character of color ) { + checksum = + ( checksum * 31 + ( character.codePointAt( 0 ) ?? 0 ) ) % + 2_147_483_647; + } + } + } + return checksum; +} + function measure( benchmarkCase: BenchmarkCase ): BenchmarkResult { let checksum = 0; const runSample = () => { - let result = ''; + let result: RampOutput | RampOutput[] | undefined; const start = performance.now(); for ( let iteration = 0; @@ -157,10 +171,8 @@ function measure( benchmarkCase: BenchmarkCase ): BenchmarkResult { } const duration = performance.now() - start; - for ( const character of result ) { - checksum = - ( checksum * 31 + ( character.codePointAt( 0 ) ?? 0 ) ) % - 2_147_483_647; + if ( result ) { + checksum = updateChecksum( checksum, result ); } return duration / benchmarkCase.iterationsPerSample; }; diff --git a/packages/theme/src/color-ramps/lib/build-foreground-scale.ts b/packages/theme/src/color-ramps/lib/build-foreground-scale.ts index b34a6c8b2972c4..3d15c605b435a2 100644 --- a/packages/theme/src/color-ramps/lib/build-foreground-scale.ts +++ b/packages/theme/src/color-ramps/lib/build-foreground-scale.ts @@ -9,7 +9,12 @@ import { to, type PlainColorObject, } from 'colorjs.io/fn'; -import { clampToGamut, getColorString, getContrast } from './color-utils.ts'; +import { + clampToGamut, + getColorString, + getContrastFromLuminances, + getRelativeLuminance, +} from './color-utils.ts'; import { UNIVERSAL_CONTRAST_TOPUP } from './constants.ts'; import { taperChroma } from './taper-chroma.ts'; import type { @@ -98,7 +103,7 @@ function getPerceptualContrastMagnitude( return Math.abs( contrastAPCA( background, foreground ) ); } -function getConstraintReferences( +function getConstraintReferenceLuminances( step: ForegroundScaleConfig[ 'steps' ][ number ], ramp: RampResult, backgroundRamp: RampResult @@ -109,59 +114,76 @@ function getConstraintReferences( ...step.contrast.references.map( ( name ) => backgroundRamp.ramp[ name ] ), - ] ) + ] ), + ( reference ) => getRelativeLuminance( reference ) + ); +} + +function meetsContrastFloor( + color: string | PlainColorObject, + referenceLuminances: readonly number[], + target: number +) { + const luminance = getRelativeLuminance( color ); + return referenceLuminances.every( + ( referenceLuminance ) => + getContrastFromLuminances( referenceLuminance, luminance ) >= target ); } function getContrastTargetMargin( color: PlainColorObject, - references: readonly string[], + referenceLuminances: readonly number[], target: number ) { const adjustedTarget = target + UNIVERSAL_CONTRAST_TOPUP; - return Math.min( - ...references.map( ( reference ) => - Math.log( getContrast( reference, color ) / adjustedTarget ) - ) - ); + const luminance = getRelativeLuminance( color ); + let minimumMargin = Infinity; + for ( const referenceLuminance of referenceLuminances ) { + minimumMargin = Math.min( + minimumMargin, + Math.log( + getContrastFromLuminances( referenceLuminance, luminance ) / + adjustedTarget + ) + ); + } + return minimumMargin; } function meetsContrastTarget( color: PlainColorObject, - step: ForegroundScaleConfig[ 'steps' ][ number ], - ramp: RampResult, - backgroundRamp: RampResult + referenceLuminances: readonly number[], + target: number ) { - return ( - getContrastTargetMargin( - color, - getConstraintReferences( step, ramp, backgroundRamp ), - step.contrast.target - ) >= 0 - ); + return getContrastTargetMargin( color, referenceLuminances, target ) >= 0; } function findColorAtContrastTarget( { getColorAtLightness, weakColor, strongColor, - references, + referenceLuminances, target, }: { getColorAtLightness: GetColorForLightness; weakColor: PlainColorObject; strongColor: PlainColorObject; - references: readonly string[]; + referenceLuminances: readonly number[]; target: number; } ) { - const weakMargin = getContrastTargetMargin( weakColor, references, target ); + const weakMargin = getContrastTargetMargin( + weakColor, + referenceLuminances, + target + ); if ( weakMargin >= 0 ) { return weakColor; } const strongMargin = getContrastTargetMargin( strongColor, - references, + referenceLuminances, target ); if ( strongMargin < 0 ) { @@ -170,7 +192,8 @@ function findColorAtContrastTarget( { return solveWithBisect( getColorAtLightness, - ( color ) => getContrastTargetMargin( color, references, target ), + ( color ) => + getContrastTargetMargin( color, referenceLuminances, target ), get( weakColor, [ OKLCH, 'l' ] ), weakMargin, get( strongColor, [ OKLCH, 'l' ] ), @@ -224,21 +247,17 @@ function serializeColorMeetingContrast( { color, getColorAtLightness, strongColor, - references, + referenceLuminances, target, }: { color: PlainColorObject; getColorAtLightness: GetColorForLightness; strongColor: PlainColorObject; - references: readonly string[]; + referenceLuminances: readonly number[]; target: number; } ) { const serializedColor = getColorString( color ); - if ( - references.every( - ( reference ) => getContrast( reference, serializedColor ) >= target - ) - ) { + if ( meetsContrastFloor( serializedColor, referenceLuminances, target ) ) { return serializedColor; } @@ -251,12 +270,7 @@ function serializeColorMeetingContrast( { const strengthened = getColorString( getColorAtLightness( colorLightness + direction * offset ) ); - if ( - references.every( - ( reference ) => - getContrast( reference, strengthened ) >= target - ) - ) { + if ( meetsContrastFloor( strengthened, referenceLuminances, target ) ) { return strengthened; } } @@ -270,7 +284,7 @@ function serializeColorMeetingPerceptualInterval( { strongColor, strongEndpoint, getColorAtLightness, - references, + referenceLuminances, wcagTarget, perceptualTarget, }: { @@ -279,7 +293,7 @@ function serializeColorMeetingPerceptualInterval( { strongColor: string; strongEndpoint: PlainColorObject; getColorAtLightness: GetColorForLightness; - references: readonly string[]; + referenceLuminances: readonly number[]; wcagTarget: number; perceptualTarget: number; } ) { @@ -300,9 +314,10 @@ function serializeColorMeetingPerceptualInterval( { if ( getPerceptualContrastMagnitude( background, serializedEndpoint ) < requiredStrongContrast || - references.some( - ( reference ) => - getContrast( reference, serializedEndpoint ) < wcagTarget + ! meetsContrastFloor( + serializedEndpoint, + referenceLuminances, + wcagTarget ) ) { return strongColor; @@ -323,10 +338,7 @@ function serializeColorMeetingPerceptualInterval( { if ( getPerceptualContrastMagnitude( background, strengthened ) >= requiredStrongContrast && - references.every( - ( reference ) => - getContrast( reference, strengthened ) >= wcagTarget - ) + meetsContrastFloor( strengthened, referenceLuminances, wcagTarget ) ) { return strengthened; } @@ -363,6 +375,12 @@ export function buildForegroundScale( ForegroundRampStep, ForegroundScaleConfig[ 'steps' ][ number ] >; + const referenceLuminances = Object.fromEntries( + config.steps.map( ( step ) => [ + step.name, + getConstraintReferenceLuminances( step, ramp, backgroundRamp ), + ] ) + ) as Record< ForegroundRampStep, number[] >; const strongStep = steps.fgSurface5; const strongEndpoint = getColorAtLightness( ramp.direction === 'lighter' ? 1 : 0 @@ -375,7 +393,11 @@ export function buildForegroundScale( const currentColor = clampToGamut( ramp.ramp[ step.name ] ); colors.set( step.name, - meetsContrastTarget( currentColor, step, ramp, backgroundRamp ) + meetsContrastTarget( + currentColor, + referenceLuminances[ step.name ], + step.contrast.target + ) ? currentColor : findColorAtContrastTarget( { getColorAtLightness: ( lightness ) => @@ -385,11 +407,7 @@ export function buildForegroundScale( ), weakColor: currentColor, strongColor: strongEndpoint, - references: getConstraintReferences( - step, - ramp, - backgroundRamp - ), + referenceLuminances: referenceLuminances[ step.name ], target: step.contrast.target, } ) ); @@ -400,11 +418,7 @@ export function buildForegroundScale( getColorAtLightness, weakColor: getColorAtLightness( get( fgSurface2, [ OKLCH, 'l' ] ) ), strongColor: strongEndpoint, - references: getConstraintReferences( - steps.fgSurface3, - ramp, - backgroundRamp - ), + referenceLuminances: referenceLuminances.fgSurface3, target: steps.fgSurface3.contrast.target, } ); const fgSurface3Contrast = getPerceptualContrastMagnitude( @@ -415,11 +429,7 @@ export function buildForegroundScale( getColorAtLightness, weakColor: fgSurface3, strongColor: strongEndpoint, - references: getConstraintReferences( - steps.fgSurface4, - ramp, - backgroundRamp - ), + referenceLuminances: referenceLuminances.fgSurface4, target: steps.fgSurface4.contrast.target, } ); const minimumFgSurface4Contrast = getPerceptualContrastMagnitude( @@ -479,20 +489,15 @@ export function buildForegroundScale( if ( ! meetsContrastTarget( normalColor, - steps.fgSurface4, - ramp, - backgroundRamp + referenceLuminances.fgSurface4, + steps.fgSurface4.contrast.target ) ) { normalColor = findColorAtContrastTarget( { getColorAtLightness, weakColor: normalColor, strongColor: strongEndpoint, - references: getConstraintReferences( - steps.fgSurface4, - ramp, - backgroundRamp - ), + referenceLuminances: referenceLuminances.fgSurface4, target: steps.fgSurface4.contrast.target, } ); } @@ -515,17 +520,17 @@ export function buildForegroundScale( target: requestedStrongContrast, } ); if ( - ! meetsContrastTarget( strongColor, strongStep, ramp, backgroundRamp ) + ! meetsContrastTarget( + strongColor, + referenceLuminances.fgSurface5, + strongStep.contrast.target + ) ) { strongColor = findColorAtContrastTarget( { getColorAtLightness, weakColor: strongColor, strongColor: strongEndpoint, - references: getConstraintReferences( - strongStep, - ramp, - backgroundRamp - ), + referenceLuminances: referenceLuminances.fgSurface5, target: strongStep.contrast.target, } ); } @@ -548,7 +553,7 @@ export function buildForegroundScale( ) : getColorAtLightness, strongColor, - references: getConstraintReferences( step, ramp, backgroundRamp ), + referenceLuminances: referenceLuminances[ step.name ], target: step.contrast.target, } ); } @@ -558,7 +563,7 @@ export function buildForegroundScale( strongColor: nextRamp.fgSurface5, strongEndpoint, getColorAtLightness, - references: getConstraintReferences( strongStep, ramp, backgroundRamp ), + referenceLuminances: referenceLuminances.fgSurface5, wcagTarget: strongStep.contrast.target, perceptualTarget: normalToActive, } ); @@ -571,10 +576,10 @@ export function buildForegroundScale( ); for ( const step of config.steps ) { if ( - getConstraintReferences( step, ramp, backgroundRamp ).some( - ( reference ) => - getContrast( reference, nextRamp[ step.name ] ) < - step.contrast.target + ! meetsContrastFloor( + nextRamp[ step.name ], + referenceLuminances[ step.name ], + step.contrast.target ) ) { warnings.push( step.name ); diff --git a/packages/theme/src/color-ramps/lib/color-utils.ts b/packages/theme/src/color-ramps/lib/color-utils.ts index 531e1b7d9c0bf1..21073ff83ffef3 100644 --- a/packages/theme/src/color-ramps/lib/color-utils.ts +++ b/packages/theme/src/color-ramps/lib/color-utils.ts @@ -56,7 +56,9 @@ export function getContrast( * * @param color Color to measure. */ -function getRelativeLuminance( color: string | PlainColorObject ): number { +export function getRelativeLuminance( + color: string | PlainColorObject +): number { if ( typeof color === 'string' ) { ColorSpace.register( sRGB ); return Math.max( getLuminance( color ), 0 ); @@ -81,7 +83,10 @@ function getRelativeLuminance( color: string | PlainColorObject ): number { * @param first First relative luminance. * @param second Second relative luminance. */ -function getContrastFromLuminances( first: number, second: number ): number { +export function getContrastFromLuminances( + first: number, + second: number +): number { return first > second ? ( first + 0.05 ) / ( second + 0.05 ) : ( second + 0.05 ) / ( first + 0.05 ); From 221cc2958787913a6ce37f6a3fac435856e898a4 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Thu, 3 Sep 2026 10:41:54 +0200 Subject: [PATCH 13/44] Theme: Skip unused ramp calculation work Use internal accent-purpose profiles while preserving semantic colors and contrast checks. Remove redundant base foreground solves and record parity and benchmark results. Include the requested active-fill change to follow the ramp main direction. --- packages/theme/CHANGELOG.md | 1 + packages/theme/benchmark/README.md | 17 +- packages/theme/benchmark/color-ramps.ts | 41 +- packages/theme/benchmark/step-pruning.md | 178 +++++++++ packages/theme/src/color-ramps/index.ts | 29 +- .../color-ramps/lib/build-foreground-scale.ts | 85 ++-- .../color-ramps/lib/build-perceptual-steps.ts | 103 +++-- .../src/color-ramps/lib/default-ramps.ts | 35 +- packages/theme/src/color-ramps/lib/index.ts | 56 ++- .../theme/src/color-ramps/lib/ramp-configs.ts | 71 +--- packages/theme/src/color-ramps/lib/types.ts | 51 ++- packages/theme/src/color-ramps/lib/utils.ts | 34 +- .../src/color-ramps/stories/ramp-table.tsx | 16 - .../test/__snapshots__/index.test.ts.snap | 374 +++--------------- .../theme/src/color-ramps/test/index.test.ts | 15 +- .../src/color-ramps/test/ramp-purpose.test.ts | 126 ++++++ .../theme/src/prebuilt/ts/color-tokens.ts | 40 +- .../use-theme-provider-styles.jsdom.test.tsx | 40 +- .../src/theme-provider-color-warnings.ts | 15 +- .../theme/src/use-theme-provider-styles.ts | 16 +- packages/theme/tokens/color.json | 343 +++++----------- 21 files changed, 838 insertions(+), 848 deletions(-) create mode 100644 packages/theme/benchmark/step-pruning.md create mode 100644 packages/theme/src/color-ramps/test/ramp-purpose.test.ts diff --git a/packages/theme/CHANGELOG.md b/packages/theme/CHANGELOG.md index 2fcb16ec92e2d9..74a24204b4de96 100644 --- a/packages/theme/CHANGELOG.md +++ b/packages/theme/CHANGELOG.md @@ -75,6 +75,7 @@ - Reuse seed-dependent calculations during color-ramp constraint searches. ([#82545](https://github.com/WordPress/gutenberg/pull/82545)) - Generate the default ramps before derived token artifacts so one build uses the current ramp algorithm throughout. ([#82525](https://github.com/WordPress/gutenberg/pull/82525)) - Cache relative luminance calculations used by color-ramp contrast checks. ([#82445](https://github.com/WordPress/gutenberg/pull/82445)) +- Skip unused ramp outputs and redundant base foreground solves while preserving semantic colors and contrast checks. ([#82294](https://github.com/WordPress/gutenberg/pull/82294)) - Generate the default ramps before derived token artifacts so one build uses the current ramp algorithm throughout. ([#82294](https://github.com/WordPress/gutenberg/pull/82294)) - Enforce NodeNext module resolution in the build project so future declaration imports are checked against the package's published ESM resolution rules. ([#82088](https://github.com/WordPress/gutenberg/pull/82088)) - Update the `@types/node` development dependency to v24, matching the Node.js version the repository builds and tests against ([#82616](https://github.com/WordPress/gutenberg/pull/82616)). diff --git a/packages/theme/benchmark/README.md b/packages/theme/benchmark/README.md index a88a0b603c3a80..9d2dfc1e61a21e 100644 --- a/packages/theme/benchmark/README.md +++ b/packages/theme/benchmark/README.md @@ -4,8 +4,11 @@ This benchmark calls the color-ramp builders directly, bypassing the `ThemeProvider` whole-ramp memoization while retaining internal algorithm caches. It covers one background ramp, one accent ramp against a precomputed background, and the full `ThemeProvider` ramp workload of one background plus -six accent ramps. Each case cycles through the default light seed, a dark -`#1e1e1e` background, and the Ectoplasm admin color scheme. +six accent ramps. Accent calls use the same internal purpose as `ThemeProvider`: +`interactive` for brand/error and `status` for info/success/caution/warning. +Historical algorithms ignore this extra argument. Each case cycles through the +default light seed, a dark `#1e1e1e` background, and the Ectoplasm sample +(`#413256` background and `#a3b745` primary). Run it from the repository root: @@ -24,7 +27,11 @@ npm run benchmark:color-ramps --workspace @wordpress/theme -- \ ``` Both worktrees need their dependencies installed. Add `--json` for -machine-readable output. The checksum covers every generated ramp token so an -optimization can be checked for output drift. Compare runs made with the same -Node.js version and similar system load. The benchmark reports measurements but +machine-readable output. The checksum covers every generated ramp token. It +changes when unused steps are omitted, even if semantic colors are identical; +use semantic parity tests to distinguish pruning from color drift. Compare runs +made with the same Node.js version and similar system load. The benchmark reports measurements but does not enforce a timing threshold because local and CI timings vary. + +See [the step-pruning report](./step-pruning.md) for the measured before/after +results and the optimizations retained or rejected within this PR. diff --git a/packages/theme/benchmark/color-ramps.ts b/packages/theme/benchmark/color-ramps.ts index f5b8b9a5e7d05f..076bf143bfef76 100644 --- a/packages/theme/benchmark/color-ramps.ts +++ b/packages/theme/benchmark/color-ramps.ts @@ -6,7 +6,7 @@ import { parseArgs } from 'node:util'; import { fileURLToPath, pathToFileURL } from 'node:url'; type RampModule = typeof import('../src/color-ramps/index.ts'); -type RampOutput = ReturnType< RampModule[ 'buildBgRamp' ] >; +type RampOutput = ReturnType< RampModule[ 'buildAccentRamp' ] >; type BenchmarkResult = { name: string; @@ -52,8 +52,8 @@ const FIXTURES = [ }, { name: 'ectoplasm', - background: '#4f386e', - primary: '#646c3e', + background: '#413256', + primary: '#a3b745', }, ] as const; @@ -114,7 +114,8 @@ function createCases( rampModule: RampModule ): BenchmarkCase[] { const fixtureIndex = accentIndex++ % FIXTURES.length; return buildAccentRamp( FIXTURES[ fixtureIndex ].primary, - accentBackgroundRamps[ fixtureIndex ] + accentBackgroundRamps[ fixtureIndex ], + 'interactive' ); }, }, @@ -124,18 +125,26 @@ function createCases( rampModule: RampModule ): BenchmarkCase[] { run: () => { const fixture = FIXTURES[ themeIndex++ % FIXTURES.length ]; const backgroundRamp = buildBgRamp( fixture.background ); - const accentSeeds = [ - fixture.primary, - DEFAULT_SEED_COLORS.info, - DEFAULT_SEED_COLORS.success, - DEFAULT_SEED_COLORS.caution, - DEFAULT_SEED_COLORS.warning, - DEFAULT_SEED_COLORS.error, - ]; - const result = [ backgroundRamp ]; - - for ( const seed of accentSeeds ) { - result.push( buildAccentRamp( seed, backgroundRamp ) ); + const accentSeeds = { + primary: fixture.primary, + info: DEFAULT_SEED_COLORS.info, + success: DEFAULT_SEED_COLORS.success, + caution: DEFAULT_SEED_COLORS.caution, + warning: DEFAULT_SEED_COLORS.warning, + error: DEFAULT_SEED_COLORS.error, + }; + const result: RampOutput[] = [ backgroundRamp ]; + + for ( const [ role, seed ] of Object.entries( accentSeeds ) ) { + result.push( + buildAccentRamp( + seed, + backgroundRamp, + role === 'primary' || role === 'error' + ? 'interactive' + : 'status' + ) + ); } return result; }, diff --git a/packages/theme/benchmark/step-pruning.md b/packages/theme/benchmark/step-pruning.md new file mode 100644 index 00000000000000..c01502cd8456ca --- /dev/null +++ b/packages/theme/benchmark/step-pruning.md @@ -0,0 +1,178 @@ +# Ramp step pruning + +## Goal and boundaries + +Reduce theme-generation work without changing any semantic color, its contrast +requirements, or the public ThemeProvider API. Keep the existing local +`bgFill2.followDirection: 'main'` change. + +## Plan + +1. Remove FGS1, `bgFillDark`, and `fgFillDark` from generation. No semantic token + uses them, and no retained color depends on them. Update internal diagnostics + and generated fixtures while preserving the public warning step-name type. +2. Add an internal accent-purpose string union, `full`, `interactive`, or + `status`, if the measured saving justifies it. Brand and error use + `interactive`; info, success, caution, and warning use `status`. Storybook + and full-ramp diagnostics retain `full`. Do not add a ThemeProvider prop. +3. Probe skipping accent ST2 reconstruction, then its base calculation if + removing that constraint preserves all retained outputs. +4. Probe skipping status ST4 repair and FGS5 final serialization/interval + correction. Keep foreground spacing and the strong color used as a bound + when serializing retained foregrounds. Do not replace required anchors with + approximate colors or weaken semantic contrast checks. +5. Consider skipping SF6's final reconstruction, but retain its authored + spacing contribution to SF4/SF5. Keep this separate from the first probes. +6. Keep only changes that preserve output and earn their complexity in repeated + benchmarks. Record discarded probes as well as retained changes. + +## Verification + +The author approved this regression coverage: + +- Omit unused steps. +- Preserve exact semantic colors for each ramp purpose. +- Preserve the existing contrast checks. +- Keep cached results distinct between ramp purposes. + +Compare against a frozen copy of the current working tree, including all 13 +Storybook samples, a 216-color background cube, and the same primary-color cube +against light, dark, and middle-gray backgrounds. Check all mapped values and +warning payloads. Run focused unit tests, formatting/lint, type checks, and +token generation. Public generated CSS/JS token values must not change. + +Benchmark before/after in separate, sequential Node processes. Report the +generation workload separately from cache hits and browser rendering. Use +semantic parity checks rather than expecting a raw all-step checksum to match +after internal steps have been removed. + +## Result + +Implemented locally on `codex/theme-feasibility-aware-perceptual-ramps`. +This comparison is **before versus after step pruning within this PR**, not +trunk versus this PR. The baseline is commit +`4bb53ec0db8705cb1cfbfc6df52434ad0f0e488b` plus the already-applied local +`bgFill2.followDirection: 'main'` change. Both sides use that direction change. + +### Work removed + +| Work | Decision | Reason | +| ------------------------------------------------- | ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| FGS1, `bgFillDark`, `fgFillDark` | Remove entirely | No semantic consumers or retained dependents. | +| Base FGS4/FGS5 solves | Remove | These intermediate colors were overwritten. Their base 4.5:1 constraint against SF3 was already covered by FGS3. The final foreground pass still constructs FGS4/FGS5 and checks their full surface constraints. | +| Accent ST2 final reconstruction | Skip for runtime accents | No accent semantic token uses ST2. Keep its base solve. | +| Accent SF6 final color conversion | Skip for runtime accents | Keep the base SF6 color and perceptual spacing calculation that position SF4/SF5. Only its final output conversion is unused. | +| Status ST4 final repair | Skip for status ramps | No status semantic token uses ST4. Its base constraint remains important. | +| Status FGS5 serialization and interval correction | Skip for status ramps | No status interaction-state token uses FGS5. Keep the strong foreground candidate and spacing budget that constrain FGS3/FGS4. | +| Accent ST2 base solve | Retain | Output parity passed, but the measured incremental saving was about 0.1%, insufficient to justify another conditional base configuration. | +| Status ST4 base solve | Retain | Removing it changed semantic results in 521 of 877 combinations. Its contrast constraint affects seed adjustment. | + +The enum-style option is an internal string union, not a new ThemeProvider prop: + +| Purpose | Caller | Returned steps per ramp | +| ---------------- | -------------------------------------------------------------- | ----------------------: | +| `full` (default) | Background, token generation, Storybook, full-ramp diagnostics | 20 | +| `interactive` | Primary and error runtime ramps | 18 | +| `status` | Info, success, caution, and warning runtime ramps | 16 | + +The runtime returns 120 color fields across seven ramps, down from 161. +This is not a count of independent solves: retained fields and hidden anchors +still require different amounts of work. Public semantic tokens are not removed, +including tokens with no current literal consumer in this repository. +FGS2–FGS5 keep their existing names; they are not renumbered after FGS1 removal. + +### Final production-path benchmark + +Each cell below is the median of five process-level results. The p95 columns +are medians of the five process-level p95 values, not pooled percentiles. + +| Workload | Before median | After median | Change | Before p95 | After p95 | +| ------------------------------------------ | ------------: | -----------: | -----: | ---------: | --------: | +| Background ramp | 0.482 ms | 0.421 ms | −12.6% | 0.528 ms | 0.477 ms | +| Interactive accent, precomputed background | 1.758 ms | 1.613 ms | −8.3% | 1.914 ms | 1.763 ms | +| Complete theme: background + six accents | 12.165 ms | 10.831 ms | −11.0% | 13.800 ms | 11.505 ms | + +The five paired full-theme medians were: + +| Run | Before | After | Change | +| --- | --------: | --------: | -----: | +| 1 | 12.082 ms | 10.899 ms | −9.8% | +| 2 | 12.151 ms | 10.773 ms | −11.3% | +| 3 | 12.203 ms | 10.741 ms | −12.0% | +| 4 | 12.165 ms | 10.831 ms | −11.0% | +| 5 | 12.195 ms | 10.905 ms | −10.6% | + +Environment: Node.js v20.20.2, macOS arm64. Each process used eight warmup +samples and 30 measured samples, with 20 ramp calls or five complete themes +per sample. Processes ran sequentially, alternating before/after order between +pairs, without concurrent builds or probes. Fixtures were default light, +default dark, and Ectoplasm (`#413256` / `#a3b745`) on both sides. + +These measurements bypass ThemeProvider's whole-ramp memoization and retain +internal color-algorithm caches. They do not measure browser rendering, a +cold page load, or the usual memoized cache-hit path. Raw all-step checksums +are stable within each version but differ between versions because fields +were removed and insertion order changed. Semantic equality was checked +separately. + +The checked-in benchmark now passes the same purpose arguments as the runtime. +Use it with a prepared baseline checkout, then repeat with the process order +reversed: + +```sh +npm run benchmark:color-ramps --workspace @wordpress/theme -- \ + --baseline-root /path/to/baseline \ + --baseline-label before-pruning \ + --label after-pruning +``` + +### Exploratory probes, not additive savings + +These earlier measurements used a diagnostic driver, before the final SF6 +skip. Each row reports the median of five process medians. The driver rebuilt +temporary configuration objects between calls, so use the production-path +table above for the final result. Differences between these rows are useful +for deciding which probes to pursue, not precise isolated cost estimates. + +| Cumulative probe | Full-theme median | Change versus its baseline | +| --------------------------------------------------- | ----------------: | -------------------------: | +| Baseline | 12.341 ms | — | +| Remove the three globally unused steps | 12.011 ms | −2.7% | +| Also skip accent ST2 and status ST4/FGS5 final work | 11.591 ms | −6.1% | +| Also omit accent ST2's base solve | 11.580 ms | −6.2% | +| Also omit base FGS4/FGS5 solves | 11.173 ms | −9.5% | + +The last two rows were probes, not the exact final implementation. The final +code keeps accent ST2's base solve, removes redundant base FGS4/FGS5 solves, +and skips accent SF6's final conversion. SF6's saving was not measured in +isolation. The status ST4 base-deletion probe was rejected on output parity +before using its timing to make a decision. + +Local raw results and frozen source snapshots are in +`/private/tmp/theme-step-pruning.20QyaP/`. The results file is +`benchmark-results.json`; these scratch files are not part of the PR. + +### Verification completed + +- 877 theme combinations / 6,139 ramps: all 13 Storybook samples, a 216-color + background cube, and a 216-color primary cube on light, dark, and middle-gray + backgrounds. Every retained color, semantic value, direction, and warning + payload matched the frozen baseline. No checked contrast failures or warnings. +- Both full-ramp snapshots match the baseline exactly after deleting only + FGS1, `bgFillDark`, and `fgFillDark` fields (1,377 removed snapshot lines). +- 174 theme Node/Vitest tests and 35 ThemeProvider/JSDOM tests pass. New tests + cover omission, semantic parity by purpose, foreground/stroke contrast, + redundant base foreground removal, and cache isolation between purposes. +- Theme type checks, changed-file JavaScript lint, and theme token generation + pass. No full-repository build or browser performance test was run here. +- Public prebuilt CSS/JS, token types, fallback SCSS, and token documentation + are unchanged. The generated internal alias map changes key order only; + its mappings are identical. The public warning step-name union is preserved. + +### Further scope + +Do not add a per-step bitmask or caller-supplied skip list. The three purposes +encode known semantic usage, and the parity tests will catch a future mapping +that needs an omitted step. More aggressive removal of status fills or +foreground anchors would need a separate dependency/constraint analysis; +being unmapped is not sufficient evidence that their calculations are unused. diff --git a/packages/theme/src/color-ramps/index.ts b/packages/theme/src/color-ramps/index.ts index ef4011e1843523..321a42a58d1d11 100644 --- a/packages/theme/src/color-ramps/index.ts +++ b/packages/theme/src/color-ramps/index.ts @@ -14,6 +14,8 @@ import { } from './lib/utils.ts'; import { BG_RAMP_CONFIG, ACCENT_RAMP_CONFIG } from './lib/ramp-configs.ts'; import type { + AccentRampPurpose, + AccentRampResult, RampResult as InternalRampResult, RampDirection, Ramp, @@ -124,7 +126,7 @@ function getBgRampInfo( ramp: InternalRampResult ): { mainDirection: RampDirection; backgroundRamp: InternalRampResult; pinLightness: { - stepName: keyof Ramp; + stepName: typeof STEP_TO_PIN; value: number; }; } { @@ -144,19 +146,34 @@ function getBgRampInfo( ramp: InternalRampResult ): { /** * Creates an accent ramp (ie used by primary, success, info, warning and error * ramps). - * @param seed The seed color for the accent ramp. - * @param bgRamp The ramp of the background on which the accent is shown. + * @param seed The seed color for the accent ramp. + * @param bgRamp The ramp of the background on which the accent is shown. + * @param purpose Internal profile selected by the caller's semantic usage. */ export function buildAccentRamp( seed: string, - bgRamp?: InternalRampResult -): InternalRampResult { + bgRamp?: InternalRampResult, + purpose?: 'full' +): InternalRampResult; +export function buildAccentRamp( + seed: string, + bgRamp: InternalRampResult | undefined, + purpose: AccentRampPurpose +): AccentRampResult; +export function buildAccentRamp( + seed: string, + bgRamp?: InternalRampResult, + purpose: AccentRampPurpose = 'full' +): AccentRampResult { if ( typeof seed !== 'string' || seed.trim() === '' ) { throw new Error( 'Seed color must be a non-empty string' ); } const bgRampInfo = bgRamp ? getBgRampInfo( bgRamp ) : undefined; - return buildRamp( seed, ACCENT_RAMP_CONFIG, bgRampInfo ); + return buildRamp( seed, ACCENT_RAMP_CONFIG, { + ...bgRampInfo, + purpose, + } ); } /** diff --git a/packages/theme/src/color-ramps/lib/build-foreground-scale.ts b/packages/theme/src/color-ramps/lib/build-foreground-scale.ts index 3d15c605b435a2..c5ab2f9da4ce9b 100644 --- a/packages/theme/src/color-ramps/lib/build-foreground-scale.ts +++ b/packages/theme/src/color-ramps/lib/build-foreground-scale.ts @@ -18,6 +18,10 @@ import { import { UNIVERSAL_CONTRAST_TOPUP } from './constants.ts'; import { taperChroma } from './taper-chroma.ts'; import type { + AccentRampResult, + BaseRamp, + BaseRampResult, + Ramp, ForegroundScaleConfig, ForegroundRampStep, RampResult, @@ -105,8 +109,8 @@ function getPerceptualContrastMagnitude( function getConstraintReferenceLuminances( step: ForegroundScaleConfig[ 'steps' ][ number ], - ramp: RampResult, - backgroundRamp: RampResult + ramp: BaseRampResult, + backgroundRamp: BaseRampResult ) { return Array.from( new Set( [ @@ -352,15 +356,29 @@ function serializeColorMeetingPerceptualInterval( { * the intermediate steps within the visible contrast range, while WCAG ratios * remain hard floors across the surfaces where each step is used. * - * @param ramp Ramp whose foreground steps will be rebuilt. - * @param backgroundRamp Background ramp on which the colors will appear. - * @param config Foreground scale anchors, targets, and constraints. + * @param ramp Ramp whose foreground steps will be rebuilt. + * @param backgroundRamp Background ramp on which the colors will appear. + * @param config Foreground scale anchors, targets, and constraints. + * @param includeInteractionState Whether FGS5 is needed as an output color. */ export function buildForegroundScale( - ramp: RampResult, - backgroundRamp: RampResult, - config: ForegroundScaleConfig -): RampResult { + ramp: BaseRampResult, + backgroundRamp: BaseRampResult, + config: ForegroundScaleConfig, + includeInteractionState?: true +): RampResult; +export function buildForegroundScale( + ramp: BaseRampResult, + backgroundRamp: BaseRampResult, + config: ForegroundScaleConfig, + includeInteractionState: boolean +): AccentRampResult; +export function buildForegroundScale( + ramp: BaseRampResult, + backgroundRamp: BaseRampResult, + config: ForegroundScaleConfig, + includeInteractionState = true +): AccentRampResult { const seed = clampToGamut( ramp.ramp[ config.seed ] ); const getColorAtLightness = createColorForLightness( seed, config ); // Parse and convert this once. APCA evaluates it for every intermediate @@ -388,7 +406,7 @@ export function buildForegroundScale( const colors = new Map< ForegroundRampStep, PlainColorObject >(); for ( const step of config.steps.filter( - ( { preserveAnchor } ) => preserveAnchor + ( stepConfig ) => stepConfig.preserveAnchor === true ) ) { const currentColor = clampToGamut( ramp.ramp[ step.name ] ); colors.set( @@ -539,10 +557,14 @@ export function buildForegroundScale( colors.set( 'fgSurface4', normalColor ); colors.set( 'fgSurface5', strongColor ); - const nextRamp = { ...ramp.ramp }; - for ( const step of config.steps ) { + // Keep the strong color and its budget above: even status ramps use them + // when positioning and serializing FGS3/FGS4. Only its own output is unused. + const outputSteps = includeInteractionState + ? config.steps + : config.steps.filter( ( step ) => step.name !== 'fgSurface5' ); + function serializeStep( step: ForegroundScaleConfig[ 'steps' ][ number ] ) { const color = colors.get( step.name )!; - nextRamp[ step.name ] = serializeColorMeetingContrast( { + return serializeColorMeetingContrast( { color, getColorAtLightness: step.preserveAnchor && step !== strongStep @@ -557,27 +579,38 @@ export function buildForegroundScale( target: step.contrast.target, } ); } - nextRamp.fgSurface5 = serializeColorMeetingPerceptualInterval( { - background: displayBackground, - normalColor: nextRamp.fgSurface4, - strongColor: nextRamp.fgSurface5, - strongEndpoint, - getColorAtLightness, - referenceLuminances: referenceLuminances.fgSurface5, - wcagTarget: strongStep.contrast.target, - perceptualTarget: normalToActive, - } ); + const nextRamp: BaseRamp & { fgSurface4: string; fgSurface5?: string } = { + ...ramp.ramp, + fgSurface4: serializeStep( steps.fgSurface4 ), + }; + for ( const step of outputSteps ) { + if ( step.name !== 'fgSurface4' ) { + nextRamp[ step.name ] = serializeStep( step ); + } + } + if ( includeInteractionState ) { + nextRamp.fgSurface5 = serializeColorMeetingPerceptualInterval( { + background: displayBackground, + normalColor: nextRamp.fgSurface4, + strongColor: nextRamp.fgSurface5!, + strongEndpoint, + getColorAtLightness, + referenceLuminances: referenceLuminances.fgSurface5, + wcagTarget: strongStep.contrast.target, + perceptualTarget: normalToActive, + } ); + } - const foregroundStepNames = new Set< keyof typeof ramp.ramp >( + const foregroundStepNames = new Set< keyof Ramp >( config.steps.map( ( { name } ) => name ) ); const warnings = ( ramp.warnings ?? [] ).filter( ( step ) => ! foregroundStepNames.has( step ) ); - for ( const step of config.steps ) { + for ( const step of outputSteps ) { if ( ! meetsContrastFloor( - nextRamp[ step.name ], + nextRamp[ step.name ]!, referenceLuminances[ step.name ], step.contrast.target ) diff --git a/packages/theme/src/color-ramps/lib/build-perceptual-steps.ts b/packages/theme/src/color-ramps/lib/build-perceptual-steps.ts index 2836884cd9f8fb..dadab9adb8b682 100644 --- a/packages/theme/src/color-ramps/lib/build-perceptual-steps.ts +++ b/packages/theme/src/color-ramps/lib/build-perceptual-steps.ts @@ -12,7 +12,7 @@ import { } from 'colorjs.io/fn'; import { clampToGamut, getColorString, getContrast } from './color-utils.ts'; import { UNIVERSAL_CONTRAST_TOPUP } from './constants.ts'; -import type { Ramp, RampResult } from './types.ts'; +import type { AccentRampPurpose, BaseRamp, BaseRampResult } from './types.ts'; import { solveWithBisect } from './utils.ts'; ColorSpace.register( sRGB ); @@ -78,8 +78,8 @@ function createAnchoredColorForStepAtPerceptualLightness( { startLightness, endLightness, }: { - ramp: Record< keyof Ramp, string >; - step: keyof Ramp; + ramp: Record< keyof BaseRamp, string >; + step: keyof BaseRamp; anchor: string; startLightness: number; endLightness: number; @@ -214,7 +214,7 @@ function findColorMeetingWcag( { ); } -function rebuildSurfaces( ramp: RampResult ) { +function rebuildSurfaces( ramp: BaseRampResult, purpose: AccentRampPurpose ) { const nextRamp = { ...ramp.ramp }; const surface1Lightness = getPerceptualLightness( ramp.ramp.surface1 ); const surface2Lightness = getPerceptualLightness( ramp.ramp.surface2 ); @@ -289,6 +289,10 @@ function rebuildSurfaces( ramp: RampResult ) { } as const; for ( const [ step, lightness ] of Object.entries( targetLightnesses ) ) { + // Keep SF6's spacing anchor above, but avoid its unused output conversion. + if ( purpose !== 'full' && step === 'surface6' ) { + continue; + } nextRamp[ step as keyof typeof targetLightnesses ] = getColorForPerceptualLightness( ramp.ramp[ step as keyof typeof targetLightnesses ], @@ -300,8 +304,8 @@ function rebuildSurfaces( ramp: RampResult ) { } function getStrokeReferences( - ramp: Record< keyof Ramp, string >, - backgroundRamp: RampResult + ramp: Record< keyof BaseRamp, string >, + backgroundRamp: BaseRampResult ) { return Array.from( new Set( [ @@ -315,7 +319,11 @@ function getStrokeReferences( ); } -function rebuildStrokes( ramp: RampResult, backgroundRamp: RampResult ) { +function rebuildStrokes( + ramp: BaseRampResult, + backgroundRamp: BaseRampResult, + purpose: AccentRampPurpose +) { const nextRamp = { ...ramp.ramp }; const references = getStrokeReferences( nextRamp, backgroundRamp ); nextRamp.stroke3 = findColorMeetingWcag( { @@ -324,37 +332,42 @@ function rebuildStrokes( ramp: RampResult, backgroundRamp: RampResult ) { target: SERIALIZED_STROKE_WCAG_TARGET, } ); - const stroke1Reference = to( clampToGamut( nextRamp.stroke1 ), OKLab ); - const stroke1To3Difference = deltaEOK2( - stroke1Reference, - nextRamp.stroke3 - ); - const authoredStroke2Difference = deltaEOK2( - stroke1Reference, - ramp.ramp.stroke2 - ); - const stroke1Lightness = getPerceptualLightness( nextRamp.stroke1 ); const stroke3Lightness = getPerceptualLightness( nextRamp.stroke3 ); - const getStroke2AtLightness = - createAnchoredColorForStepAtPerceptualLightness( { - ramp: nextRamp, - step: 'stroke2', - anchor: nextRamp.stroke1, - startLightness: stroke1Lightness, - endLightness: stroke3Lightness, - } ); - nextRamp.stroke2 = getColorString( - findColorAtDeltaE( { - reference: stroke1Reference, - getColorAtLightness: getStroke2AtLightness, - startLightness: stroke1Lightness, - endLightness: stroke3Lightness, - target: Math.min( - authoredStroke2Difference, - stroke1To3Difference * 0.95 - ), - } ) - ); + if ( purpose === 'full' ) { + const stroke1Reference = to( clampToGamut( nextRamp.stroke1 ), OKLab ); + const stroke1To3Difference = deltaEOK2( + stroke1Reference, + nextRamp.stroke3 + ); + const authoredStroke2Difference = deltaEOK2( + stroke1Reference, + ramp.ramp.stroke2 + ); + const stroke1Lightness = getPerceptualLightness( nextRamp.stroke1 ); + const getStroke2AtLightness = + createAnchoredColorForStepAtPerceptualLightness( { + ramp: nextRamp, + step: 'stroke2', + anchor: nextRamp.stroke1, + startLightness: stroke1Lightness, + endLightness: stroke3Lightness, + } ); + nextRamp.stroke2 = getColorString( + findColorAtDeltaE( { + reference: stroke1Reference, + getColorAtLightness: getStroke2AtLightness, + startLightness: stroke1Lightness, + endLightness: stroke3Lightness, + target: Math.min( + authoredStroke2Difference, + stroke1To3Difference * 0.95 + ), + } ) + ); + } + if ( purpose === 'status' ) { + return nextRamp; + } const stroke3Contrast = Math.abs( contrastAPCA( nextRamp.surface3, nextRamp.stroke3 ) @@ -411,18 +424,24 @@ function rebuildStrokes( ramp: RampResult, backgroundRamp: RampResult ) { * * @param ramp Base ramp to adjust. * @param backgroundRamp Background surfaces used by accent strokes. + * @param purpose Which accent steps need their final reconstruction. */ export function buildPerceptualSteps( - ramp: RampResult, - backgroundRamp?: RampResult -): RampResult { + ramp: BaseRampResult, + backgroundRamp?: BaseRampResult, + purpose: AccentRampPurpose = 'full' +): BaseRampResult { const surfaceResult = { ...ramp, - ramp: rebuildSurfaces( ramp ), + ramp: rebuildSurfaces( ramp, purpose ), }; return { ...surfaceResult, - ramp: rebuildStrokes( surfaceResult, backgroundRamp ?? surfaceResult ), + ramp: rebuildStrokes( + surfaceResult, + backgroundRamp ?? surfaceResult, + purpose + ), }; } diff --git a/packages/theme/src/color-ramps/lib/default-ramps.ts b/packages/theme/src/color-ramps/lib/default-ramps.ts index 3d37e464189139..42ec7ec910c3e4 100644 --- a/packages/theme/src/color-ramps/lib/default-ramps.ts +++ b/packages/theme/src/color-ramps/lib/default-ramps.ts @@ -15,21 +15,18 @@ export const DEFAULT_RAMPS: Record< surface5: '#e3e3e3', surface4: '#efefef', surface3: '#fff', - fgSurface5: '#141414', - fgSurface4: '#505050', fgSurface3: '#737373', fgSurface2: '#8d8d8d', - fgSurface1: '#aeaeae', stroke3: '#8d8d8d', stroke4: '#6e6e6e', stroke2: '#dbdbdb', stroke1: '#f0f0f0', - bgFillDark: '#1e1e1e', - fgFillDark: '#f0f0f0', bgFillInverted2: '#1e1e1e', bgFillInverted1: '#2d2d2d', fgFillInverted: '#f0f0f0', surface1: '#f9f9f9', + fgSurface4: '#505050', + fgSurface5: '#141414', }, direction: 'darker', }, @@ -43,21 +40,18 @@ export const DEFAULT_RAMPS: Record< surface5: '#dce3f4', surface4: '#e5eaf7', surface3: '#fff', - fgSurface5: '#03063b', - fgSurface4: '#222eda', fgSurface3: '#3f61ea', fgSurface2: '#5a82ff', - fgSurface1: '#86a9ff', stroke3: '#3858e9', stroke4: '#2337c8', stroke2: '#9aaad1', stroke1: '#aebbda', - bgFillDark: '#1a1e27', - fgFillDark: '#eff0f2', bgFillInverted2: '#1a1e27', bgFillInverted1: '#13009f', fgFillInverted: '#eff0f2', surface1: '#edf1fc', + fgSurface4: '#222eda', + fgSurface5: '#03063b', }, direction: 'darker', }, @@ -71,17 +65,12 @@ export const DEFAULT_RAMPS: Record< surface5: '#d3e4f7', surface4: '#deebfa', surface3: '#fff', - fgSurface5: '#00132a', - fgSurface4: '#005092', fgSurface3: '#0070c8', fgSurface2: '#008bfa', - fgSurface1: '#59b0ff', stroke3: '#006bd7', stroke4: '#004fa9', stroke2: '#95b5d9', stroke1: '#adc6e2', - bgFillDark: '#1a1f24', - fgFillDark: '#eef0f2', bgFillInverted2: '#1a1f24', bgFillInverted1: '#002a69', fgFillInverted: '#eef0f2', @@ -102,13 +91,10 @@ export const DEFAULT_RAMPS: Record< fgSurface4: '#002900', fgSurface3: '#008030', fgSurface2: '#2b9f4e', - fgSurface1: '#54c16e', stroke3: '#008030', stroke4: '#006113', stroke2: '#84c08f', stroke1: '#92d39d', - bgFillDark: '#1b1f1c', - fgFillDark: '#ecf2ec', bgFillInverted2: '#1b1f1c', bgFillInverted1: '#003701', fgFillInverted: '#ecf2ec', @@ -129,13 +115,10 @@ export const DEFAULT_RAMPS: Record< fgSurface4: '#281d00', fgSurface3: '#836a00', fgSurface2: '#a48700', - fgSurface1: '#c7a800', stroke3: '#836a00', stroke4: '#634f00', stroke2: '#bdb17e', stroke1: '#d0c28b', - bgFillDark: '#1f1e1b', - fgFillDark: '#faf1c9', bgFillInverted2: '#1f1e1b', bgFillInverted1: '#392c00', fgFillInverted: '#faf1c9', @@ -156,13 +139,10 @@ export const DEFAULT_RAMPS: Record< fgSurface4: '#2e1900', fgSurface3: '#936300', fgSurface2: '#b57f00', - fgSurface1: '#d7a02b', stroke3: '#936300', stroke4: '#704900', stroke2: '#c8ad7f', stroke1: '#e1bd7c', - bgFillDark: '#1f1e1b', - fgFillDark: '#f5efe5', bgFillInverted2: '#1f1e1b', bgFillInverted1: '#422800', fgFillInverted: '#f5efe5', @@ -180,21 +160,18 @@ export const DEFAULT_RAMPS: Record< surface5: '#f7dbd7', surface4: '#fae5e1', surface3: '#fff', - fgSurface5: '#260101', - fgSurface4: '#980f0f', fgSurface3: '#d91a1a', fgSurface2: '#f74c40', - fgSurface1: '#ff8879', stroke3: '#cc1818', stroke4: '#9d0000', stroke2: '#d99a91', stroke1: '#e0afa7', - bgFillDark: '#231c1b', - fgFillDark: '#f2efef', bgFillInverted2: '#231c1b', bgFillInverted1: '#640000', fgFillInverted: '#f2efef', surface1: '#feeeeb', + fgSurface4: '#980f0f', + fgSurface5: '#260101', }, direction: 'darker', }, diff --git a/packages/theme/src/color-ramps/lib/index.ts b/packages/theme/src/color-ramps/lib/index.ts index 42fb1921d9e1d2..934b44b3c3f054 100644 --- a/packages/theme/src/color-ramps/lib/index.ts +++ b/packages/theme/src/color-ramps/lib/index.ts @@ -16,6 +16,10 @@ import { solveWithBisect, } from './utils.ts'; import type { + AccentRampPurpose, + AccentRampResult, + BaseRamp, + BaseRampStep, FollowDirection, Ramp, RampDirection, @@ -48,16 +52,16 @@ function calculateRamp( { pinLightness, }: { seed: PlainColorObject; - sortedSteps: ( keyof Ramp )[]; + sortedSteps: BaseRampStep[]; config: RampStepsConfig; mainDir: RampDirection; oppDir: RampDirection; pinLightness?: { - stepName: keyof Ramp; + stepName: BaseRampStep; value: number; }; } ) { - const rampResults = {} as Record< keyof Ramp, string >; + const rampResults = {} as BaseRamp; let warnings: ( keyof Ramp )[] | undefined; let maxDeficit = -Infinity; let maxDeficitDirection: RampDirection = 'lighter'; @@ -209,6 +213,24 @@ function calculateRamp( { }; } +type BuildRampOptions = { + mainDirection?: RampDirection; + pinLightness?: { stepName: BaseRampStep; value: number }; + backgroundRamp?: RampResult; + rescaleToFitContrastTargets?: boolean; + purpose?: AccentRampPurpose; +}; + +export function buildRamp( + seedArg: string, + config: RampConfig, + options?: BuildRampOptions & { purpose?: 'full' } +): RampResult; +export function buildRamp( + seedArg: string, + config: RampConfig, + options: BuildRampOptions +): AccentRampResult; export function buildRamp( seedArg: string, config: RampConfig, @@ -345,12 +367,34 @@ export function buildRamp( warnings: bestWarnings, direction: mainDir, }, - backgroundRamp + backgroundRamp, + purpose ); - return buildForegroundScale( + if ( purpose === 'full' ) { + return buildForegroundScale( + rampResult, + backgroundRamp ?? rampResult, + config.foregroundScale + ); + } + const foregroundResult = buildForegroundScale( rampResult, backgroundRamp ?? rampResult, - config.foregroundScale + config.foregroundScale, + purpose !== 'status' ); + // Do not expose base colors whose final reconstruction was intentionally + // skipped. Their base constraints and spacing contributions remain intact. + const pruned: AccentRampResult = { + ...foregroundResult, + ramp: { ...foregroundResult.ramp }, + }; + delete pruned.ramp.surface6; + delete pruned.ramp.stroke2; + if ( purpose === 'status' ) { + delete pruned.ramp.stroke4; + delete pruned.ramp.fgSurface5; + } + return pruned; } diff --git a/packages/theme/src/color-ramps/lib/ramp-configs.ts b/packages/theme/src/color-ramps/lib/ramp-configs.ts index cf7163f0141574..9b6e4f97316cdf 100644 --- a/packages/theme/src/color-ramps/lib/ramp-configs.ts +++ b/packages/theme/src/color-ramps/lib/ramp-configs.ts @@ -52,18 +52,6 @@ const highContrastInvertedFillConfig: RampStepConfig = { taperChromaOptions: FG_TAPER_CHROMA, }; -const fgSurface5Config: RampStepConfig = { - // The foreground scale pass derives the strong endpoint from APCA targets. - // The base solver only needs to establish the WCAG floor and direction. - contrast: { - reference: 'surface3', - followDirection: 'main', - target: 4.5, - preferLighter: true, - }, - taperChromaOptions: FG_TAPER_CHROMA, -}; - const FOREGROUND_PERCEPTUAL_TARGETS = { // These are design targets, not accessibility thresholds. WCAG ratios remain // the hard gates for every surface on which a foreground can appear. @@ -80,11 +68,6 @@ const FOREGROUND_PERCEPTUAL_TARGETS = { } as const; const FOREGROUND_SCALE_STEPS = [ - { - name: 'fgSurface1', - preserveAnchor: true, - contrast: { references: [ 'surface3' ], target: 2 }, - }, { name: 'fgSurface2', preserveAnchor: true, @@ -187,7 +170,7 @@ const BG_RAMP_STEPS: RampConfig[ 'steps' ] = { bgFill2: { contrast: { reference: 'bgFill1', - followDirection: 'darker', + followDirection: 'main', target: 1.2, }, }, @@ -199,16 +182,6 @@ const BG_RAMP_STEPS: RampConfig[ 'steps' ] = { }, }, bgFillInverted2: highContrastInvertedFillConfig, - bgFillDark: { - contrast: { - reference: 'surface3', - followDirection: 'darker', // This is what causes the token to be always dark - target: 7, - ignoreWhenAdjustingSeed: true, - }, - lightness: lightnessConstraintForegroundHighContrast, - taperChromaOptions: FG_TAPER_CHROMA, - }, // Stroke stroke1: { contrast: { @@ -243,15 +216,6 @@ const BG_RAMP_STEPS: RampConfig[ 'steps' ] = { taperChromaOptions: STROKE_TAPER_CHROMA, }, // fgSurface - fgSurface1: { - contrast: { - reference: 'surface3', - followDirection: 'main', - target: 2, - preferLighter: true, - }, - taperChromaOptions: FG_TAPER_CHROMA, - }, fgSurface2: { contrast: { reference: 'surface3', @@ -271,17 +235,6 @@ const BG_RAMP_STEPS: RampConfig[ 'steps' ] = { lightness: lightnessConstraintForegroundMediumContrast, taperChromaOptions: FG_TAPER_CHROMA, }, - fgSurface4: { - contrast: { - reference: 'surface3', - followDirection: 'main', - target: 4.5, - preferLighter: true, - }, - taperChromaOptions: FG_TAPER_CHROMA, - sameAsIfPossible: 'fgSurface5', - }, - fgSurface5: fgSurface5Config, // fgFill fgFill: { contrast: { @@ -303,16 +256,6 @@ const BG_RAMP_STEPS: RampConfig[ 'steps' ] = { lightness: lightnessConstraintForegroundHighContrast, taperChromaOptions: FG_TAPER_CHROMA, }, - fgFillDark: { - contrast: { - reference: 'bgFillDark', - followDirection: 'best', - target: 4.5, - preferLighter: true, - }, - lightness: lightnessConstraintForegroundHighContrast, - taperChromaOptions: FG_TAPER_CHROMA, - }, }; export const BG_RAMP_CONFIG: RampConfig = { @@ -386,10 +329,6 @@ export const ACCENT_RAMP_CONFIG: RampConfig = { taperChromaOptions: undefined, }, // fgSurface: do not de-saturate - fgSurface1: { - ...BG_RAMP_CONFIG.steps.fgSurface1, - taperChromaOptions: undefined, - }, fgSurface2: { ...BG_RAMP_CONFIG.steps.fgSurface2, taperChromaOptions: undefined, @@ -399,14 +338,6 @@ export const ACCENT_RAMP_CONFIG: RampConfig = { taperChromaOptions: undefined, sameAsIfPossible: 'bgFill1', }, - fgSurface4: { - ...BG_RAMP_CONFIG.steps.fgSurface4, - taperChromaOptions: undefined, - }, - fgSurface5: { - ...BG_RAMP_CONFIG.steps.fgSurface5, - taperChromaOptions: undefined, - }, }, foregroundScale: { seed: 'bgFill1', diff --git a/packages/theme/src/color-ramps/lib/types.ts b/packages/theme/src/color-ramps/lib/types.ts index 7f514eca886e41..2118cc0385a093 100644 --- a/packages/theme/src/color-ramps/lib/types.ts +++ b/packages/theme/src/color-ramps/lib/types.ts @@ -18,9 +18,7 @@ export type Ramp = { bgFill2: string; bgFillInverted1: string; bgFillInverted2: string; - bgFillDark: string; // Foreground (text, icon) colors - fgSurface1: string; fgSurface2: string; fgSurface3: string; fgSurface4: string; @@ -28,20 +26,25 @@ export type Ramp = { // Foreground (text, icon) colors on top of bgFill fgFill: string; fgFillInverted: string; - fgFillDark: string; }; export type RampDirection = 'lighter' | 'darker'; +// FGS4/FGS5 are produced by the foreground pass, not the base constraint solve. +export type BaseRamp = Omit< Ramp, 'fgSurface4' | 'fgSurface5' >; +export type BaseRampStep = keyof BaseRamp; +// Internal calculation profiles. ThemeProvider chooses from semantic usage; +// full ramps remain available for generation and diagnostics. +export type AccentRampPurpose = 'full' | 'interactive' | 'status'; export type FollowDirection = 'main' | 'opposite' | 'best' | RampDirection; export type ContrastRequirement = { /** The reference color against which to calculate the contrast */ - reference: keyof Ramp | 'seed'; + reference: BaseRampStep | 'seed'; /** * Other colors against which the generated color must meet the same target. * The solver uses the reference with the least contrast headroom for the * selected direction. */ - additionalReferences?: readonly ( keyof Ramp | 'seed' )[]; + additionalReferences?: readonly ( BaseRampStep | 'seed' )[]; /** * Which direction should the algorithm search a matching color in: * - main: follow the same direction as the ramp's main direction @@ -77,13 +80,12 @@ export type RampStepConfig = { * the contrast requirements. This reduces the number of unique colors * in the ramp and improves consistency. */ - sameAsIfPossible?: keyof Ramp; + sameAsIfPossible?: BaseRampStep; }; -export type RampStepsConfig = Record< keyof Ramp, RampStepConfig >; +export type RampStepsConfig = Record< BaseRampStep, RampStepConfig >; export type ForegroundRampStep = - | 'fgSurface1' | 'fgSurface2' | 'fgSurface3' | 'fgSurface4' @@ -91,9 +93,9 @@ export type ForegroundRampStep = export type ForegroundScaleConfig = { /** Ramp step whose hue and chroma define the foreground scale. */ - seed: keyof Ramp; + seed: BaseRampStep; /** Background step used to measure the APCA contrast range. */ - perceptualReference: keyof Ramp; + perceptualReference: BaseRampStep; perceptualTargets: { /** Preferred APCA contrast for normal content and resting controls. */ normalContrast: number; @@ -112,15 +114,19 @@ export type ForegroundScaleConfig = { | { mode: 'gamut-relative'; }; - steps: readonly { - name: ForegroundRampStep; - /** Preserve the base solver's color when it meets every WCAG floor. */ - preserveAnchor?: boolean; + steps: readonly ( { contrast: { - references: readonly ( keyof Ramp )[]; + references: readonly BaseRampStep[]; target: number; }; - }[]; + } & ( + | { + name: Extract< ForegroundRampStep, BaseRampStep >; + /** Preserve a base color when it meets every WCAG floor. */ + preserveAnchor: true; + } + | { name: ForegroundRampStep; preserveAnchor?: false } + ) )[]; }; export type RampConfig = { @@ -128,8 +134,17 @@ export type RampConfig = { foregroundScale: ForegroundScaleConfig; }; -export type RampResult = { - ramp: Record< keyof Ramp, string >; +export type RampResult< Colors = Ramp > = { + ramp: Colors; warnings?: ( keyof Ramp )[]; direction: RampDirection; }; + +export type BaseRampResult = RampResult< BaseRamp >; + +export type AccentRampResult = RampResult< + Omit< Ramp, 'surface6' | 'stroke2' | 'stroke4' | 'fgSurface5' > & + Partial< + Pick< Ramp, 'surface6' | 'stroke2' | 'stroke4' | 'fgSurface5' > + > +>; diff --git a/packages/theme/src/color-ramps/lib/utils.ts b/packages/theme/src/color-ramps/lib/utils.ts index 9f75e8320d5f37..342d4b80d3ac09 100644 --- a/packages/theme/src/color-ramps/lib/utils.ts +++ b/packages/theme/src/color-ramps/lib/utils.ts @@ -8,7 +8,7 @@ import { MAX_BISECTION_ITERATIONS, CONTRAST_EPSILON, } from './constants.ts'; -import type { Ramp, RampStepsConfig, RampDirection } from './types.ts'; +import type { BaseRampStep, RampStepsConfig, RampDirection } from './types.ts'; import { getContrast } from './color-utils.ts'; /** @@ -16,24 +16,24 @@ import { getContrast } from './color-utils.ts'; * @param config - The steps configuration object */ function buildDependencyGraph( config: RampStepsConfig ): { - dependencies: Map< keyof Ramp, ( keyof Ramp | 'seed' )[] >; - dependents: Map< keyof Ramp | 'seed', ( keyof Ramp )[] >; + dependencies: Map< BaseRampStep, ( BaseRampStep | 'seed' )[] >; + dependents: Map< BaseRampStep | 'seed', BaseRampStep[] >; } { - const dependencies = new Map< keyof Ramp, ( keyof Ramp | 'seed' )[] >(); - const dependents = new Map< keyof Ramp | 'seed', ( keyof Ramp )[] >(); + const dependencies = new Map< BaseRampStep, ( BaseRampStep | 'seed' )[] >(); + const dependents = new Map< BaseRampStep | 'seed', BaseRampStep[] >(); // Initialize maps Object.keys( config ).forEach( ( step ) => { - dependencies.set( step as keyof Ramp, [] ); + dependencies.set( step as BaseRampStep, [] ); } ); dependents.set( 'seed', [] ); Object.keys( config ).forEach( ( step ) => { - dependents.set( step as keyof Ramp, [] ); + dependents.set( step as BaseRampStep, [] ); } ); // Build the graph Object.entries( config ).forEach( ( [ stepName, stepConfig ] ) => { - const step = stepName as keyof Ramp; + const step = stepName as BaseRampStep; const references = [ stepConfig.contrast.reference, ...( stepConfig.contrast.additionalReferences ?? [] ), @@ -58,13 +58,13 @@ function buildDependencyGraph( config: RampStepsConfig ): { * Topologically sort steps based on their dependencies * @param config - The steps configuration object */ -export function sortByDependency( config: RampStepsConfig ): ( keyof Ramp )[] { +export function sortByDependency( config: RampStepsConfig ): BaseRampStep[] { const { dependents } = buildDependencyGraph( config ); - const result: ( keyof Ramp )[] = []; - const visited = new Set< keyof Ramp | 'seed' >(); - const visiting = new Set< keyof Ramp | 'seed' >(); + const result: BaseRampStep[] = []; + const visited = new Set< BaseRampStep | 'seed' >(); + const visiting = new Set< BaseRampStep | 'seed' >(); - function visit( node: keyof Ramp | 'seed' ): void { + function visit( node: BaseRampStep | 'seed' ): void { if ( visiting.has( node ) ) { throw new Error( `Circular dependency detected involving step: ${ String( @@ -105,11 +105,11 @@ export function sortByDependency( config: RampStepsConfig ): ( keyof Ramp )[] { * @return Array of steps that `stepName` depends on. */ export function stepsForStep( - stepName: keyof Ramp, + stepName: BaseRampStep, config: RampStepsConfig -): ( keyof Ramp )[] { - const result = new Set< keyof Ramp >(); - function visit( step: keyof Ramp | 'seed' ) { +): BaseRampStep[] { + const result = new Set< BaseRampStep >(); + function visit( step: BaseRampStep | 'seed' ) { if ( step === 'seed' || result.has( step ) ) { return; } diff --git a/packages/theme/src/color-ramps/stories/ramp-table.tsx b/packages/theme/src/color-ramps/stories/ramp-table.tsx index 67c9dba5fa1b87..b1adb9c74ba617 100644 --- a/packages/theme/src/color-ramps/stories/ramp-table.tsx +++ b/packages/theme/src/color-ramps/stories/ramp-table.tsx @@ -25,19 +25,16 @@ const RAMP_TOKENS_ORDER: { tokenName: keyof Ramp; abbr: string }[] = [ { tokenName: 'bgFill2', abbr: 'BGF2' }, { tokenName: 'bgFillInverted1', abbr: 'BGFI1' }, { tokenName: 'bgFillInverted2', abbr: 'BGFI2' }, - { tokenName: 'bgFillDark', abbr: 'BGFD' }, { tokenName: 'stroke1', abbr: 'ST1' }, { tokenName: 'stroke2', abbr: 'ST2' }, { tokenName: 'stroke3', abbr: 'ST3' }, { tokenName: 'stroke4', abbr: 'ST4' }, - { tokenName: 'fgSurface1', abbr: 'FGS1' }, { tokenName: 'fgSurface2', abbr: 'FGS2' }, { tokenName: 'fgSurface3', abbr: 'FGS3' }, { tokenName: 'fgSurface4', abbr: 'FGS4' }, { tokenName: 'fgSurface5', abbr: 'FGS5' }, { tokenName: 'fgFill', abbr: 'FGF' }, { tokenName: 'fgFillInverted', abbr: 'FGFI' }, - { tokenName: 'fgFillDark', abbr: 'FGFD' }, ]; type RampTableProps = { @@ -308,7 +305,6 @@ export const RampTable = forwardRef< HTMLDivElement, RampTableProps >( 'surface3', 'bgFill1', 'bgFillInverted1', - 'bgFillDark', ].includes( tokenName ) ? ( ( > { tokenName === 'surface3' ? ( <> - ( ramp={ ramp } /> ) : null } - { tokenName === 'bgFillDark' ? ( - - ) : null } ) : null }
diff --git a/packages/theme/src/color-ramps/test/__snapshots__/index.test.ts.snap b/packages/theme/src/color-ramps/test/__snapshots__/index.test.ts.snap index 48816f67402f73..53a933cc5e3755 100644 --- a/packages/theme/src/color-ramps/test/__snapshots__/index.test.ts.snap +++ b/packages/theme/src/color-ramps/test/__snapshots__/index.test.ts.snap @@ -1545,14 +1545,11 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "direction": "lighter", "ramp": { "bgFill1": "#d1d1d1", - "bgFill2": "#bebebe", - "bgFillDark": "#000", + "bgFill2": "#e6e6e6", "bgFillInverted1": "#dbdbdb", "bgFillInverted2": "#f0f0f0", - "fgFill": "#4d4d4d", - "fgFillDark": "#f0f0f0", + "fgFill": "#595959", "fgFillInverted": "#1e1e1e", - "fgSurface1": "#454545", "fgSurface2": "#5e5e5e", "fgSurface3": "#757575", "fgSurface4": "#ddd", @@ -1588,14 +1585,11 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "direction": "lighter", "ramp": { "bgFill1": "#d1d1d1", - "bgFill2": "#bebebe", - "bgFillDark": "#000", + "bgFill2": "#e6e6e6", "bgFillInverted1": "#dbdbdb", "bgFillInverted2": "#f0f0f0", - "fgFill": "#4d4d4d", - "fgFillDark": "#f0f0f0", + "fgFill": "#595959", "fgFillInverted": "#1e1e1e", - "fgSurface1": "#464646", "fgSurface2": "#606060", "fgSurface3": "#797979", "fgSurface4": "#ddd", @@ -1631,14 +1625,11 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "direction": "lighter", "ramp": { "bgFill1": "#d1d1d1", - "bgFill2": "#bebebe", - "bgFillDark": "#000", + "bgFill2": "#e6e6e6", "bgFillInverted1": "#dbdbdb", "bgFillInverted2": "#f0f0f0", - "fgFill": "#4d4d4d", - "fgFillDark": "#f0f0f0", + "fgFill": "#595959", "fgFillInverted": "#1e1e1e", - "fgSurface1": "#4f4f4f", "fgSurface2": "#696969", "fgSurface3": "#868686", "fgSurface4": "#dedede", @@ -1674,14 +1665,11 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "direction": "lighter", "ramp": { "bgFill1": "#d1d1d1", - "bgFill2": "#bebebe", - "bgFillDark": "#000", + "bgFill2": "#e6e6e6", "bgFillInverted1": "#dbdbdb", "bgFillInverted2": "#f0f0f0", - "fgFill": "#4d4d4d", - "fgFillDark": "#f0f0f0", + "fgFill": "#595959", "fgFillInverted": "#1e1e1e", - "fgSurface1": "#606060", "fgSurface2": "#7c7c7c", "fgSurface3": "#9b9b9b", "fgSurface4": "#e4e4e4", @@ -1717,14 +1705,11 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "direction": "lighter", "ramp": { "bgFill1": "#d2d2d2", - "bgFill2": "#bebebe", - "bgFillDark": "#000", + "bgFill2": "#e6e6e6", "bgFillInverted1": "#dbdbdb", "bgFillInverted2": "#f0f0f0", - "fgFill": "#4d4d4d", - "fgFillDark": "#f0f0f0", + "fgFill": "#5a5a5a", "fgFillInverted": "#1e1e1e", - "fgSurface1": "#7c7c7c", "fgSurface2": "#9a9a9a", "fgSurface3": "#bebebe", "fgSurface4": "#e8e8e8", @@ -1761,13 +1746,10 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "ramp": { "bgFill1": "#fcfcfc", "bgFill2": "#e6e6e6", - "bgFillDark": "#050505", "bgFillInverted1": "#1e1e1e", "bgFillInverted2": "#050505", "fgFill": "#666", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", - "fgSurface1": "#656565", "fgSurface2": "#4b4b4b", "fgSurface3": "#313131", "fgSurface4": "#262626", @@ -1804,13 +1786,10 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "ramp": { "bgFill1": "#fcfcfc", "bgFill2": "#e6e6e6", - "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", "fgFill": "#666", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", - "fgSurface1": "#7e7e7e", "fgSurface2": "#626262", "fgSurface3": "#484848", "fgSurface4": "#3e3e3e", @@ -1847,13 +1826,10 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "ramp": { "bgFill1": "#fcfcfc", "bgFill2": "#e6e6e6", - "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", "fgFill": "#666", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", - "fgSurface1": "#969696", "fgSurface2": "#787878", "fgSurface3": "#5d5d5d", "fgSurface4": "#535353", @@ -1890,13 +1866,10 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "ramp": { "bgFill1": "#fcfcfc", "bgFill2": "#e6e6e6", - "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", "fgFill": "#666", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", - "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#767676", "fgSurface4": "#555", @@ -1934,14 +1907,12 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "direction": "lighter", "ramp": { "bgFill1": "#3858e9", - "bgFill2": "#2e49d9", - "bgFillDark": "#000", + "bgFill2": "#4367f9", "bgFillInverted1": "#c8dcff", "bgFillInverted2": "#eff0f2", "fgFill": "#fefeff", "fgFillDark": "#eff0f2", "fgFillInverted": "#1a1e27", - "fgSurface1": "#1e2dbf", "fgSurface2": "#314ede", "fgSurface3": "#8eb0ff", "fgSurface4": "#e4f0ff", @@ -1976,14 +1947,12 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "direction": "lighter", "ramp": { "bgFill1": "#3858e9", - "bgFill2": "#2e49d9", - "bgFillDark": "#000", + "bgFill2": "#4367f9", "bgFillInverted1": "#c8dcff", "bgFillInverted2": "#eff0f2", "fgFill": "#fefeff", "fgFillDark": "#eff0f2", "fgFillInverted": "#1a1e27", - "fgSurface1": "#1f2ec0", "fgSurface2": "#324fe0", "fgSurface3": "#8eb0ff", "fgSurface4": "#e4f0ff", @@ -2018,8 +1987,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "direction": "lighter", "ramp": { "bgFill1": "#3858e9", - "bgFill2": "#2e49d9", - "bgFillDark": "#000", + "bgFill2": "#4367f9", "bgFillInverted1": "#c8dcff", "bgFillInverted2": "#eff0f2", "fgFill": "#fefeff", @@ -2060,14 +2028,12 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "direction": "lighter", "ramp": { "bgFill1": "#3858e9", - "bgFill2": "#2e49d9", - "bgFillDark": "#000", + "bgFill2": "#4367f9", "bgFillInverted1": "#c8dcff", "bgFillInverted2": "#eff0f2", "fgFill": "#fefeff", "fgFillDark": "#eff0f2", "fgFillInverted": "#1a1e27", - "fgSurface1": "#314ede", "fgSurface2": "#486dff", "fgSurface3": "#8eb0ff", "fgSurface4": "#e4f0ff", @@ -2102,14 +2068,12 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "direction": "lighter", "ramp": { "bgFill1": "#3858e9", - "bgFill2": "#2e49d9", - "bgFillDark": "#000", + "bgFill2": "#4367f9", "bgFillInverted1": "#c8dcff", "bgFillInverted2": "#eff0f2", "fgFill": "#fefeff", "fgFillDark": "#eff0f2", "fgFillInverted": "#1a1e27", - "fgSurface1": "#466bfd", "fgSurface2": "#6a91ff", "fgSurface3": "#99b8ff", "fgSurface4": "#e4f0ff", @@ -2145,13 +2109,11 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "ramp": { "bgFill1": "#3858e9", "bgFill2": "#2e49d9", - "bgFillDark": "#030407", "bgFillInverted1": "#0b0070", "bgFillInverted2": "#030407", "fgFill": "#eff0f2", "fgFillDark": "#eff0f2", "fgFillInverted": "#eff0f2", - "fgSurface1": "#3655e6", "fgSurface2": "#2235c6", "fgSurface3": "#1504a6", "fgSurface4": "#010023", @@ -2187,13 +2149,10 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "ramp": { "bgFill1": "#3858e9", "bgFill2": "#2e49d9", - "bgFillDark": "#1a1e27", "bgFillInverted1": "#13009f", "bgFillInverted2": "#1a1e27", "fgFill": "#e7e8eb", - "fgFillDark": "#eff0f2", "fgFillInverted": "#eff0f2", - "fgSurface1": "#4b71ff", "fgSurface2": "#3351e2", "fgSurface3": "#2031c3", "fgSurface4": "#0b0070", @@ -2229,13 +2188,10 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "ramp": { "bgFill1": "#3858e9", "bgFill2": "#2e49d9", - "bgFillDark": "#1a1e27", "bgFillInverted1": "#13009f", "bgFillInverted2": "#1a1e27", "fgFill": "#e7e8eb", - "fgFillDark": "#eff0f2", "fgFillInverted": "#eff0f2", - "fgSurface1": "#6a91ff", "fgSurface2": "#466bfd", "fgSurface3": "#2f4cdc", "fgSurface4": "#0b0070", @@ -2271,13 +2227,10 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "ramp": { "bgFill1": "#3858e9", "bgFill2": "#2e49d9", - "bgFillDark": "#1a1e27", "bgFillInverted1": "#13009f", "bgFillInverted2": "#1a1e27", "fgFill": "#e7e8eb", - "fgFillDark": "#eff0f2", "fgFillInverted": "#eff0f2", - "fgSurface1": "#8daeff", "fgSurface2": "#6088ff", "fgSurface3": "#3858e9", "fgSurface4": "#0b0070", @@ -2314,14 +2267,11 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "direction": "lighter", "ramp": { "bgFill1": "#0090ff", - "bgFill2": "#007fed", - "bgFillDark": "#000", + "bgFill2": "#37a3ff", "bgFillInverted1": "#b8e0ff", "bgFillInverted2": "#eef0f2", - "fgFill": "#12161a", - "fgFillDark": "#eef0f2", + "fgFill": "#22282e", "fgFillInverted": "#1a1f24", - "fgSurface1": "#004192", "fgSurface2": "#005bbf", "fgSurface3": "#0090ff", "fgSurface4": "#dcf3ff", @@ -2356,14 +2306,11 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "direction": "lighter", "ramp": { "bgFill1": "#0090ff", - "bgFill2": "#007fed", - "bgFillDark": "#000", + "bgFill2": "#37a3ff", "bgFillInverted1": "#b8e0ff", "bgFillInverted2": "#eef0f2", - "fgFill": "#12161a", - "fgFillDark": "#eef0f2", + "fgFill": "#22282e", "fgFillInverted": "#1a1f24", - "fgSurface1": "#004394", "fgSurface2": "#005dc1", "fgSurface3": "#0090ff", "fgSurface4": "#dcf3ff", @@ -2398,12 +2345,10 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "direction": "lighter", "ramp": { "bgFill1": "#0090ff", - "bgFill2": "#007fed", - "bgFillDark": "#000", + "bgFill2": "#37a3ff", "bgFillInverted1": "#b8e0ff", "bgFillInverted2": "#eef0f2", - "fgFill": "#12161a", - "fgFillDark": "#eef0f2", + "fgFill": "#22282e", "fgFillInverted": "#1a1f24", "fgSurface1": "#004ba3", "fgSurface2": "#0065d0", @@ -2440,14 +2385,11 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "direction": "lighter", "ramp": { "bgFill1": "#0090ff", - "bgFill2": "#007fed", - "bgFillDark": "#000", + "bgFill2": "#37a3ff", "bgFillInverted1": "#b8e0ff", "bgFillInverted2": "#eef0f2", - "fgFill": "#12161a", - "fgFillDark": "#eef0f2", + "fgFill": "#22282e", "fgFillInverted": "#1a1f24", - "fgSurface1": "#005dc2", "fgSurface2": "#007be8", "fgSurface3": "#6bb9ff", "fgSurface4": "#dcf3ff", @@ -2482,14 +2424,11 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "direction": "lighter", "ramp": { "bgFill1": "#0090ff", - "bgFill2": "#007fed", - "bgFillDark": "#000", + "bgFill2": "#37a3ff", "bgFillInverted1": "#b8e0ff", "bgFillInverted2": "#eef0f2", - "fgFill": "#12161a", - "fgFillDark": "#eef0f2", + "fgFill": "#22282e", "fgFillInverted": "#1a1f24", - "fgSurface1": "#007be8", "fgSurface2": "#289eff", "fgSurface3": "#7fc3ff", "fgSurface4": "#dcf3ff", @@ -2525,13 +2464,10 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "ramp": { "bgFill1": "#0090ff", "bgFill2": "#007fed", - "bgFillDark": "#040609", "bgFillInverted1": "#001a4f", "bgFillInverted2": "#040609", "fgFill": "#12161a", - "fgFillDark": "#eef0f2", "fgFillInverted": "#eef0f2", - "fgSurface1": "#0062cb", "fgSurface2": "#00489e", "fgSurface3": "#002e70", "fgSurface4": "#000327", @@ -2567,13 +2503,10 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "ramp": { "bgFill1": "#0090ff", "bgFill2": "#007fed", - "bgFillDark": "#1a1f24", "bgFillInverted1": "#002a69", "bgFillInverted2": "#1a1f24", "fgFill": "#12161a", - "fgFillDark": "#eef0f2", "fgFillInverted": "#eef0f2", - "fgSurface1": "#007eeb", "fgSurface2": "#005fc6", "fgSurface3": "#004599", "fgSurface4": "#001b4f", @@ -2609,13 +2542,10 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "ramp": { "bgFill1": "#0090ff", "bgFill2": "#007fed", - "bgFillDark": "#1a1f24", "bgFillInverted1": "#002a69", "bgFillInverted2": "#1a1f24", "fgFill": "#12161a", - "fgFillDark": "#eef0f2", "fgFillInverted": "#eef0f2", - "fgSurface1": "#1c9aff", "fgSurface2": "#0077e4", "fgSurface3": "#005abc", "fgSurface4": "#001b4f", @@ -2651,13 +2581,10 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "ramp": { "bgFill1": "#0090ff", "bgFill2": "#007fed", - "bgFillDark": "#1a1f24", "bgFillInverted1": "#002a69", "bgFillInverted2": "#1a1f24", "fgFill": "#12161a", - "fgFillDark": "#eef0f2", "fgFillInverted": "#eef0f2", - "fgSurface1": "#64b5ff", "fgSurface2": "#0190ff", "fgSurface3": "#006fdb", "fgSurface4": "#001b4f", @@ -2694,14 +2621,12 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "direction": "lighter", "ramp": { "bgFill1": "#4ab866", - "bgFill2": "#37a756", - "bgFillDark": "#000", + "bgFill2": "#5dca77", "bgFillInverted1": "#86f29c", "bgFillInverted2": "#ecf2ec", "fgFill": "#1b1f1c", "fgFillDark": "#ecf2ec", "fgFillInverted": "#1b1f1c", - "fgSurface1": "#00510b", "fgSurface2": "#006e1e", "fgSurface3": "#4ab866", "fgSurface4": "#b9ffc6", @@ -2736,14 +2661,12 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "direction": "lighter", "ramp": { "bgFill1": "#4ab866", - "bgFill2": "#37a756", - "bgFillDark": "#000", + "bgFill2": "#5dca77", "bgFillInverted1": "#86f29c", "bgFillInverted2": "#ecf2ec", "fgFill": "#1b1f1c", "fgFillDark": "#ecf2ec", "fgFillInverted": "#1b1f1c", - "fgSurface1": "#00520c", "fgSurface2": "#007020", "fgSurface3": "#4ab866", "fgSurface4": "#b9ffc6", @@ -2778,8 +2701,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "direction": "lighter", "ramp": { "bgFill1": "#4ab866", - "bgFill2": "#37a756", - "bgFillDark": "#000", + "bgFill2": "#5dca77", "bgFillInverted1": "#86f29c", "bgFillInverted2": "#ecf2ec", "fgFill": "#1b1f1c", @@ -2820,14 +2742,12 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "direction": "lighter", "ramp": { "bgFill1": "#4ab866", - "bgFill2": "#37a756", - "bgFillDark": "#000", + "bgFill2": "#5dca77", "bgFillInverted1": "#86f29c", "bgFillInverted2": "#ecf2ec", "fgFill": "#1b1f1c", "fgFillDark": "#ecf2ec", "fgFillInverted": "#1b1f1c", - "fgSurface1": "#007223", "fgSurface2": "#149141", "fgSurface3": "#4ab866", "fgSurface4": "#b9ffc6", @@ -2862,8 +2782,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "direction": "lighter", "ramp": { "bgFill1": "#4ab866", - "bgFill2": "#37a756", - "bgFillDark": "#000", + "bgFill2": "#5dca77", "bgFillInverted1": "#86f29c", "bgFillInverted2": "#ecf2ec", "fgFill": "#1b1f1c", @@ -2905,13 +2824,11 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "ramp": { "bgFill1": "#4ab866", "bgFill2": "#37a756", - "bgFillDark": "#0d0f0d", "bgFillInverted1": "#002a00", "bgFillInverted2": "#0d0f0d", "fgFill": "#1b1f1c", "fgFillDark": "#ecf2ec", "fgFillInverted": "#ecf2ec", - "fgSurface1": "#007a2a", "fgSurface2": "#005c11", "fgSurface3": "#003e03", "fgSurface4": "#001300", @@ -2947,13 +2864,11 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "ramp": { "bgFill1": "#4ab866", "bgFill2": "#37a756", - "bgFillDark": "#1b1f1c", "bgFillInverted1": "#003701", "bgFillInverted2": "#1b1f1c", "fgFill": "#1b1f1c", "fgFillDark": "#ecf2ec", "fgFillInverted": "#ecf2ec", - "fgSurface1": "#1c9545", "fgSurface2": "#007627", "fgSurface3": "#00580f", "fgSurface4": "#002900", @@ -2989,13 +2904,11 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "ramp": { "bgFill1": "#4ab866", "bgFill2": "#37a756", - "bgFillDark": "#1b1f1c", "bgFillInverted1": "#003701", "bgFillInverted2": "#1b1f1c", "fgFill": "#1b1f1c", "fgFillDark": "#ecf2ec", "fgFillInverted": "#ecf2ec", - "fgSurface1": "#3fae5d", "fgSurface2": "#0f8e3f", "fgSurface3": "#007020", "fgSurface4": "#002900", @@ -3031,13 +2944,11 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "ramp": { "bgFill1": "#4ab866", "bgFill2": "#37a756", - "bgFillDark": "#1b1f1c", "bgFillInverted1": "#003701", "bgFillInverted2": "#1b1f1c", "fgFill": "#1b1f1c", "fgFillDark": "#ecf2ec", "fgFillInverted": "#ecf2ec", - "fgSurface1": "#58c572", "fgSurface2": "#31a352", "fgSurface3": "#008334", "fgSurface4": "#002900", @@ -3074,14 +2985,11 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "direction": "lighter", "ramp": { "bgFill1": "#f0d148", - "bgFill2": "#dcbd2e", - "bgFillDark": "#000", + "bgFill2": "#ffe762", "bgFillInverted1": "#f9da53", "bgFillInverted2": "#faf1ca", - "fgFill": "#4f4d47", - "fgFillDark": "#faf1ca", + "fgFill": "#5b5a53", "fgFillInverted": "#1f1e1b", - "fgSurface1": "#544300", "fgSurface2": "#725c00", "fgSurface3": "#f0d148", "fgSurface4": "#fff075", @@ -3116,14 +3024,11 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "direction": "lighter", "ramp": { "bgFill1": "#f0d148", - "bgFill2": "#dcbd2e", - "bgFillDark": "#000", + "bgFill2": "#ffe762", "bgFillInverted1": "#f9da53", "bgFillInverted2": "#faf1ca", - "fgFill": "#4f4d47", - "fgFillDark": "#faf1ca", + "fgFill": "#5b5a53", "fgFillInverted": "#1f1e1b", - "fgSurface1": "#564400", "fgSurface2": "#745e00", "fgSurface3": "#f0d148", "fgSurface4": "#fff075", @@ -3158,12 +3063,10 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "direction": "lighter", "ramp": { "bgFill1": "#f0d148", - "bgFill2": "#dcbd2e", - "bgFillDark": "#000", + "bgFill2": "#ffe762", "bgFillInverted1": "#f9da53", "bgFillInverted2": "#faf1ca", - "fgFill": "#4f4d47", - "fgFillDark": "#faf1ca", + "fgFill": "#5b5a53", "fgFillInverted": "#1f1e1b", "fgSurface1": "#604c00", "fgSurface2": "#7e6600", @@ -3200,14 +3103,11 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "direction": "lighter", "ramp": { "bgFill1": "#f0d148", - "bgFill2": "#dcbd2e", - "bgFillDark": "#000", + "bgFill2": "#ffe762", "bgFillInverted1": "#f9da53", "bgFillInverted2": "#faf1ca", - "fgFill": "#4f4d47", - "fgFillDark": "#faf1ca", + "fgFill": "#5b5a53", "fgFillInverted": "#1f1e1b", - "fgSurface1": "#745e01", "fgSurface2": "#957a00", "fgSurface3": "#f0d148", "fgSurface4": "#fff075", @@ -3242,12 +3142,10 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "direction": "lighter", "ramp": { "bgFill1": "#f0d148", - "bgFill2": "#dcbd2e", - "bgFillDark": "#000", + "bgFill2": "#ffe762", "bgFillInverted1": "#f9da53", "bgFillInverted2": "#faf1ca", - "fgFill": "#4f4d47", - "fgFillDark": "#faf1ca", + "fgFill": "#5b5a53", "fgFillInverted": "#1f1e1b", "fgSurface1": "#947901", "fgSurface2": "#b69800", @@ -3285,13 +3183,10 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "ramp": { "bgFill1": "#f0d149", "bgFill2": "#dcbe2f", - "bgFillDark": "#060605", "bgFillInverted1": "#281d00", "bgFillInverted2": "#060605", "fgFill": "#4f4e48", - "fgFillDark": "#faf1c9", "fgFillInverted": "#faf1c9", - "fgSurface1": "#7a6300", "fgSurface2": "#5c4900", "fgSurface3": "#3e3000", "fgSurface4": "#0b0500", @@ -3327,13 +3222,10 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "ramp": { "bgFill1": "#f0d149", "bgFill2": "#dcbe2f", - "bgFillDark": "#1f1e1b", "bgFillInverted1": "#392c00", "bgFillInverted2": "#1f1e1b", "fgFill": "#4f4e48", - "fgFillDark": "#faf1c9", "fgFillInverted": "#faf1c9", - "fgSurface1": "#977c00", "fgSurface2": "#776000", "fgSurface3": "#544814", "fgSurface4": "#493e10", @@ -3370,13 +3262,10 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "ramp": { "bgFill1": "#f0d149", "bgFill2": "#dcbe2f", - "bgFillDark": "#1f1e1b", "bgFillInverted1": "#392c00", "bgFillInverted2": "#1f1e1b", "fgFill": "#4f4e48", - "fgFillDark": "#faf1c9", "fgFillInverted": "#faf1c9", - "fgSurface1": "#b39400", "fgSurface2": "#907600", "fgSurface3": "#715b00", "fgSurface4": "#281d00", @@ -3412,13 +3301,10 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "ramp": { "bgFill1": "#f0d149", "bgFill2": "#dcbe2f", - "bgFillDark": "#1f1e1b", "bgFillInverted1": "#392c00", "bgFillInverted2": "#1f1e1b", "fgFill": "#4f4e48", - "fgFillDark": "#faf1c9", "fgFillInverted": "#faf1c9", - "fgSurface1": "#ccae0e", "fgSurface2": "#aa8c00", "fgSurface3": "#886f00", "fgSurface4": "#281d00", @@ -3455,14 +3341,11 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "direction": "lighter", "ramp": { "bgFill1": "#f0b849", - "bgFill2": "#dda633", - "bgFillDark": "#000", + "bgFill2": "#ffcd61", "bgFillInverted1": "#ffd670", "bgFillInverted2": "#f5efe5", - "fgFill": "#45423d", - "fgFillDark": "#f5efe5", + "fgFill": "#524e49", "fgFillInverted": "#1f1e1b", - "fgSurface1": "#603e00", "fgSurface2": "#815600", "fgSurface3": "#f0b849", "fgSurface4": "#ffebb0", @@ -3497,14 +3380,11 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "direction": "lighter", "ramp": { "bgFill1": "#f0b849", - "bgFill2": "#dda633", - "bgFillDark": "#000", + "bgFill2": "#ffcd61", "bgFillInverted1": "#ffd670", "bgFillInverted2": "#f5efe5", - "fgFill": "#45423d", - "fgFillDark": "#f5efe5", + "fgFill": "#524e49", "fgFillInverted": "#1f1e1b", - "fgSurface1": "#613f00", "fgSurface2": "#825700", "fgSurface3": "#f0b849", "fgSurface4": "#ffebb0", @@ -3539,12 +3419,10 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "direction": "lighter", "ramp": { "bgFill1": "#f0b849", - "bgFill2": "#dda633", - "bgFillDark": "#000", + "bgFill2": "#ffcd61", "bgFillInverted1": "#ffd670", "bgFillInverted2": "#f5efe5", - "fgFill": "#45423d", - "fgFillDark": "#f5efe5", + "fgFill": "#524e49", "fgFillInverted": "#1f1e1b", "fgSurface1": "#6c4700", "fgSurface2": "#8e5f00", @@ -3581,12 +3459,10 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "direction": "lighter", "ramp": { "bgFill1": "#f0b849", - "bgFill2": "#dda633", - "bgFillDark": "#000", + "bgFill2": "#ffcd61", "bgFillInverted1": "#ffd670", "bgFillInverted2": "#f5efe5", - "fgFill": "#45423d", - "fgFillDark": "#f5efe5", + "fgFill": "#524e49", "fgFillInverted": "#1f1e1b", "fgSurface1": "#835700", "fgSurface2": "#a67100", @@ -3623,12 +3499,10 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "direction": "lighter", "ramp": { "bgFill1": "#f0b849", - "bgFill2": "#dda633", - "bgFillDark": "#000", + "bgFill2": "#ffcd61", "bgFillInverted1": "#ffd670", "bgFillInverted2": "#f5efe5", - "fgFill": "#45423d", - "fgFillDark": "#f5efe5", + "fgFill": "#524e49", "fgFillInverted": "#1f1e1b", "fgSurface1": "#a57100", "fgSurface2": "#c69009", @@ -3666,13 +3540,11 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "ramp": { "bgFill1": "#f0b849", "bgFill2": "#dda633", - "bgFillDark": "#050504", "bgFillInverted1": "#2d1900", "bgFillInverted2": "#050504", "fgFill": "#1f1e1b", "fgFillDark": "#f5efe5", "fgFillInverted": "#f5efe5", - "fgSurface1": "#895c00", "fgSurface2": "#674300", "fgSurface3": "#462b00", "fgSurface4": "#0b0300", @@ -3708,13 +3580,10 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "ramp": { "bgFill1": "#f0b849", "bgFill2": "#dda633", - "bgFillDark": "#1f1e1b", "bgFillInverted1": "#422800", "bgFillInverted2": "#1f1e1b", "fgFill": "#45423d", - "fgFillDark": "#f5efe5", "fgFillInverted": "#f5efe5", - "fgSurface1": "#a87300", "fgSurface2": "#855900", "fgSurface3": "#5b4416", "fgSurface4": "#4f3b12", @@ -3751,13 +3620,10 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "ramp": { "bgFill1": "#f0b849", "bgFill2": "#dda633", - "bgFillDark": "#1f1e1b", "bgFillInverted1": "#422800", "bgFillInverted2": "#1f1e1b", "fgFill": "#45423d", - "fgFillDark": "#f5efe5", "fgFillInverted": "#f5efe5", - "fgSurface1": "#c38d00", "fgSurface2": "#a26e00", "fgSurface3": "#7f5400", "fgSurface4": "#2e1900", @@ -3793,13 +3659,10 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "ramp": { "bgFill1": "#f0b849", "bgFill2": "#dda633", - "bgFillDark": "#1f1e1b", "bgFillInverted1": "#422800", "bgFillInverted2": "#1f1e1b", "fgFill": "#45423d", - "fgFillDark": "#f5efe5", "fgFillInverted": "#f5efe5", - "fgSurface1": "#dea633", "fgSurface2": "#ba8500", "fgSurface3": "#986700", "fgSurface4": "#2e1900", @@ -3836,14 +3699,12 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "direction": "lighter", "ramp": { "bgFill1": "#cc1818", - "bgFill2": "#b90000", - "bgFillDark": "#000", + "bgFill2": "#dd3029", "bgFillInverted1": "#ffd0c7", "bgFillInverted2": "#f2efef", "fgFill": "#fefdfd", "fgFillDark": "#f2efef", "fgFillInverted": "#231c1b", - "fgSurface1": "#900000", "fgSurface2": "#c10008", "fgSurface3": "#ff8879", "fgSurface4": "#ffe6e0", @@ -3878,14 +3739,12 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "direction": "lighter", "ramp": { "bgFill1": "#cc1818", - "bgFill2": "#b90000", - "bgFillDark": "#000", + "bgFill2": "#dd3029", "bgFillInverted1": "#ffd0c7", "bgFillInverted2": "#f2efef", "fgFill": "#fefdfd", "fgFillDark": "#f2efef", "fgFillInverted": "#231c1b", - "fgSurface1": "#920000", "fgSurface2": "#c2000b", "fgSurface3": "#ff8879", "fgSurface4": "#ffe6e0", @@ -3920,8 +3779,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "direction": "lighter", "ramp": { "bgFill1": "#cc1818", - "bgFill2": "#b90000", - "bgFillDark": "#000", + "bgFill2": "#dd3029", "bgFillInverted1": "#ffd0c7", "bgFillInverted2": "#f2efef", "fgFill": "#fefdfd", @@ -3962,8 +3820,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "direction": "lighter", "ramp": { "bgFill1": "#cc1818", - "bgFill2": "#b90000", - "bgFillDark": "#000", + "bgFill2": "#dd3029", "bgFillInverted1": "#ffd0c7", "bgFillInverted2": "#f2efef", "fgFill": "#fefdfd", @@ -4004,14 +3861,12 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "direction": "lighter", "ramp": { "bgFill1": "#cc1818", - "bgFill2": "#b90000", - "bgFillDark": "#000", + "bgFill2": "#dd3029", "bgFillInverted1": "#ffd0c7", "bgFillInverted2": "#f2efef", "fgFill": "#fefdfd", "fgFillDark": "#f2efef", "fgFillInverted": "#231c1b", - "fgSurface1": "#e1342d", "fgSurface2": "#ff6153", "fgSurface3": "#ff9f91", "fgSurface4": "#ffe6e0", @@ -4047,13 +3902,11 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "ramp": { "bgFill1": "#fff5f1", "bgFill2": "#e9dfdb", - "bgFillDark": "#000", "bgFillInverted1": "#211917", "bgFillInverted2": "#000", "fgFill": "#221d1b", "fgFillDark": "#f9ede9", "fgFillInverted": "#f9ede9", - "fgSurface1": "#6a615e", "fgSurface2": "#4f4744", "fgSurface3": "#342d2a", "fgSurface4": "#000", @@ -4089,13 +3942,10 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "ramp": { "bgFill1": "#cc1818", "bgFill2": "#b90000", - "bgFillDark": "#231c1b", "bgFillInverted1": "#640000", "bgFillInverted2": "#231c1b", "fgFill": "#eae6e5", - "fgFillDark": "#f2efef", "fgFillInverted": "#f2efef", - "fgSurface1": "#e63a32", "fgSurface2": "#c3030d", "fgSurface3": "#930000", "fgSurface4": "#470000", @@ -4131,13 +3981,10 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "ramp": { "bgFill1": "#cc1818", "bgFill2": "#b90000", - "bgFillDark": "#231c1b", "bgFillInverted1": "#640000", "bgFillInverted2": "#231c1b", "fgFill": "#eae6e5", - "fgFillDark": "#f2efef", "fgFillInverted": "#f2efef", - "fgSurface1": "#ff6052", "fgSurface2": "#e1342d", "fgSurface3": "#bd0002", "fgSurface4": "#470000", @@ -4173,13 +4020,10 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "ramp": { "bgFill1": "#cc1818", "bgFill2": "#b90000", - "bgFillDark": "#231c1b", "bgFillInverted1": "#640000", "bgFillInverted2": "#231c1b", "fgFill": "#eae6e5", - "fgFillDark": "#f2efef", "fgFillInverted": "#f2efef", - "fgSurface1": "#ff9182", "fgSurface2": "#fd5145", "fgSurface3": "#cc1818", "fgSurface4": "#470000", @@ -15599,13 +15443,10 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "ramp": { "bgFill1": "#555", "bgFill2": "#484848", - "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", "fgFill": "#cbcbcb", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", - "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#767676", "fgSurface4": "#555", @@ -15635,13 +15476,10 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "ramp": { "bgFill1": "#555", "bgFill2": "#484848", - "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", "fgFill": "#cbcbcb", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", - "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#767676", "fgSurface4": "#555", @@ -15671,13 +15509,10 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "ramp": { "bgFill1": "#555", "bgFill2": "#484848", - "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", "fgFill": "#cbcbcb", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", - "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#767676", "fgSurface4": "#555", @@ -15707,13 +15542,10 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "ramp": { "bgFill1": "#555", "bgFill2": "#484848", - "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", "fgFill": "#cbcbcb", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", - "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#767676", "fgSurface4": "#555", @@ -15743,13 +15575,10 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "ramp": { "bgFill1": "#555", "bgFill2": "#484848", - "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", "fgFill": "#cbcbcb", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", - "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#767676", "fgSurface4": "#555", @@ -15779,13 +15608,10 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "ramp": { "bgFill1": "#555", "bgFill2": "#484848", - "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", "fgFill": "#cbcbcb", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", - "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#767676", "fgSurface4": "#555", @@ -15815,13 +15641,10 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "ramp": { "bgFill1": "#555", "bgFill2": "#484848", - "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", "fgFill": "#cbcbcb", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", - "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#767676", "fgSurface4": "#555", @@ -15851,13 +15674,10 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "ramp": { "bgFill1": "#555", "bgFill2": "#484848", - "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", "fgFill": "#cbcbcb", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", - "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#767676", "fgSurface4": "#555", @@ -15887,13 +15707,10 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "ramp": { "bgFill1": "#555", "bgFill2": "#484848", - "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", "fgFill": "#cbcbcb", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", - "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#767676", "fgSurface4": "#555", @@ -15923,13 +15740,10 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "ramp": { "bgFill1": "#555", "bgFill2": "#484848", - "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", "fgFill": "#cbcbcb", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", - "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#767676", "fgSurface4": "#555", @@ -15959,13 +15773,10 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "ramp": { "bgFill1": "#555", "bgFill2": "#484848", - "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", "fgFill": "#cbcbcb", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", - "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#767676", "fgSurface4": "#555", @@ -15995,13 +15806,10 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "ramp": { "bgFill1": "#555", "bgFill2": "#484848", - "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", "fgFill": "#cbcbcb", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", - "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#767676", "fgSurface4": "#555", @@ -16031,13 +15839,10 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "ramp": { "bgFill1": "#555", "bgFill2": "#484848", - "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", "fgFill": "#cbcbcb", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", - "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#767676", "fgSurface4": "#555", @@ -16067,13 +15872,10 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "ramp": { "bgFill1": "#555", "bgFill2": "#484848", - "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", "fgFill": "#cbcbcb", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", - "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#767676", "fgSurface4": "#555", @@ -16103,13 +15905,10 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "ramp": { "bgFill1": "#555", "bgFill2": "#484848", - "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", "fgFill": "#cbcbcb", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", - "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#767676", "fgSurface4": "#555", @@ -16139,13 +15938,10 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "ramp": { "bgFill1": "#555", "bgFill2": "#484848", - "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", "fgFill": "#cbcbcb", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", - "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#767676", "fgSurface4": "#555", @@ -16175,13 +15971,10 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "ramp": { "bgFill1": "#555", "bgFill2": "#484848", - "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", "fgFill": "#cbcbcb", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", - "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#767676", "fgSurface4": "#555", @@ -16211,13 +16004,10 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "ramp": { "bgFill1": "#555", "bgFill2": "#484848", - "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", "fgFill": "#cbcbcb", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", - "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#767676", "fgSurface4": "#555", @@ -16247,13 +16037,10 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "ramp": { "bgFill1": "#555", "bgFill2": "#484848", - "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", "fgFill": "#cbcbcb", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", - "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#767676", "fgSurface4": "#555", @@ -16283,13 +16070,10 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "ramp": { "bgFill1": "#555", "bgFill2": "#484848", - "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", "fgFill": "#cbcbcb", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", - "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#767676", "fgSurface4": "#555", @@ -16319,13 +16103,10 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "ramp": { "bgFill1": "#555", "bgFill2": "#484848", - "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", "fgFill": "#cbcbcb", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", - "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#767676", "fgSurface4": "#555", @@ -16355,13 +16136,10 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "ramp": { "bgFill1": "#555", "bgFill2": "#484848", - "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", "fgFill": "#cbcbcb", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", - "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#767676", "fgSurface4": "#555", @@ -16391,13 +16169,10 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "ramp": { "bgFill1": "#555", "bgFill2": "#484848", - "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", "fgFill": "#cbcbcb", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", - "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#767676", "fgSurface4": "#555", @@ -16427,13 +16202,10 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "ramp": { "bgFill1": "#555", "bgFill2": "#484848", - "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", "fgFill": "#cbcbcb", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", - "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#767676", "fgSurface4": "#555", @@ -16463,13 +16235,10 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "ramp": { "bgFill1": "#555", "bgFill2": "#484848", - "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", "fgFill": "#cbcbcb", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", - "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#767676", "fgSurface4": "#555", @@ -16499,13 +16268,10 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "ramp": { "bgFill1": "#555", "bgFill2": "#484848", - "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", "fgFill": "#cbcbcb", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", - "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#767676", "fgSurface4": "#555", @@ -16535,13 +16301,10 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "ramp": { "bgFill1": "#555", "bgFill2": "#484848", - "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", "fgFill": "#cbcbcb", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", - "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#767676", "fgSurface4": "#555", @@ -16571,13 +16334,10 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "ramp": { "bgFill1": "#555", "bgFill2": "#484848", - "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", "fgFill": "#cbcbcb", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", - "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#767676", "fgSurface4": "#555", @@ -16607,13 +16367,10 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "ramp": { "bgFill1": "#555", "bgFill2": "#484848", - "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", "fgFill": "#cbcbcb", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", - "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#767676", "fgSurface4": "#555", @@ -16643,13 +16400,10 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "ramp": { "bgFill1": "#555", "bgFill2": "#484848", - "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", "fgFill": "#cbcbcb", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", - "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#767676", "fgSurface4": "#555", @@ -16679,13 +16433,10 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "ramp": { "bgFill1": "#555", "bgFill2": "#484848", - "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", "fgFill": "#cbcbcb", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", - "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#767676", "fgSurface4": "#555", @@ -16715,13 +16466,10 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "ramp": { "bgFill1": "#555", "bgFill2": "#484848", - "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", "fgFill": "#cbcbcb", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", - "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#767676", "fgSurface4": "#555", @@ -16751,13 +16499,10 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "ramp": { "bgFill1": "#555", "bgFill2": "#484848", - "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", "fgFill": "#cbcbcb", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", - "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#767676", "fgSurface4": "#555", @@ -16787,13 +16532,10 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "ramp": { "bgFill1": "#555", "bgFill2": "#484848", - "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", "fgFill": "#cbcbcb", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", - "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#767676", "fgSurface4": "#555", @@ -16823,13 +16565,10 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "ramp": { "bgFill1": "#555", "bgFill2": "#484848", - "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", "fgFill": "#cbcbcb", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", - "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#767676", "fgSurface4": "#555", @@ -16859,13 +16598,10 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "ramp": { "bgFill1": "#555", "bgFill2": "#484848", - "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", "fgFill": "#cbcbcb", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", - "fgSurface1": "#b0b0b0", "fgSurface2": "#8f8f8f", "fgSurface3": "#767676", "fgSurface4": "#555", diff --git a/packages/theme/src/color-ramps/test/index.test.ts b/packages/theme/src/color-ramps/test/index.test.ts index a910cba6966bb4..b8a87f7201ae1b 100644 --- a/packages/theme/src/color-ramps/test/index.test.ts +++ b/packages/theme/src/color-ramps/test/index.test.ts @@ -53,7 +53,6 @@ function expectAccessibleFillStates( ramp: ReturnType< typeof buildRamp > ) { } const foregroundSteps = [ - 'fgSurface1', 'fgSurface2', 'fgSurface3', 'fgSurface4', @@ -83,9 +82,13 @@ function getPerceptualContrastMagnitude( } function expectAccessibleFillStates( ramp: ReturnType< typeof buildRamp > ) { - expect( getLuminance( ramp.ramp.bgFill2 ) ).toBeLessThan( - getLuminance( ramp.ramp.bgFill1 ) - ); + const restingLuminance = getLuminance( ramp.ramp.bgFill1 ); + const activeLuminance = getLuminance( ramp.ramp.bgFill2 ); + if ( ramp.direction === 'darker' ) { + expect( activeLuminance ).toBeLessThan( restingLuminance ); + } else { + expect( activeLuminance ).toBeGreaterThan( restingLuminance ); + } expect( getContrast( ramp.ramp.bgFill1, ramp.ramp.bgFill2 ) ).toBeGreaterThanOrEqual( 1.2 ); @@ -103,9 +106,9 @@ function getForegroundConstraintReferences( backgroundRamp: ReturnType< typeof buildBgRamp > ) { let surfaceNames: readonly ( keyof typeof ramp.ramp )[]; - if ( stepIndex < 2 ) { + if ( stepIndex === 0 ) { surfaceNames = [ 'surface3' ]; - } else if ( stepIndex < 3 ) { + } else if ( stepIndex === 1 ) { surfaceNames = [ 'surface1', 'surface2', 'surface3' ]; } else { surfaceNames = [ diff --git a/packages/theme/src/color-ramps/test/ramp-purpose.test.ts b/packages/theme/src/color-ramps/test/ramp-purpose.test.ts new file mode 100644 index 00000000000000..e305e2162ffd6c --- /dev/null +++ b/packages/theme/src/color-ramps/test/ramp-purpose.test.ts @@ -0,0 +1,126 @@ +import { describe, expect, it } from 'vitest'; +import { buildAccentRamp, buildBgRamp, DEFAULT_SEED_COLORS } from '..'; +import { getContrast } from '../lib/color-utils'; +import type { Ramp } from '../lib/types'; +import { ACCENT_RAMP_CONFIG, BG_RAMP_CONFIG } from '../lib/ramp-configs'; +import colorTokens from '../../prebuilt/ts/color-tokens'; + +const combinations = [ + { background: '#fcfcfc', primary: '#3858e9' }, + { background: '#1e1e1e', primary: '#3858e9' }, + { background: '#413256', primary: '#a3b745' }, + { background: '#777777', primary: '#d63638' }, + { background: '#fcfcfc', primary: '#ffd700' }, + { background: '#1e1e1e', primary: '#00ffff' }, +] as const; + +describe( 'ramp purpose', () => { + it( 'derives normal and interaction-state foregrounds without redundant base solves', () => { + for ( const config of [ BG_RAMP_CONFIG, ACCENT_RAMP_CONFIG ] ) { + expect( config.steps ).not.toHaveProperty( 'fgSurface4' ); + expect( config.steps ).not.toHaveProperty( 'fgSurface5' ); + } + const { ramp } = buildBgRamp( '#fcfcfc' ); + expect( ramp.fgSurface4 ).toBe( '#505050' ); + expect( ramp.fgSurface5 ).toBe( '#141414' ); + } ); + it( 'omits steps with no semantic consumer or downstream dependency', () => { + const background = buildBgRamp( '#fcfcfc' ); + const accent = buildAccentRamp( '#3858e9', background ); + + for ( const { ramp } of [ background, accent ] ) { + for ( const step of [ 'fgSurface1', 'bgFillDark', 'fgFillDark' ] ) { + expect( ramp ).not.toHaveProperty( step ); + } + } + } ); + + it.each( combinations )( + 'preserves semantic colors for every accent purpose over $background', + ( { background, primary } ) => { + const backgroundRamp = buildBgRamp( background ); + const seeds = { + ...DEFAULT_SEED_COLORS, + primary, + }; + + for ( const [ role, seed ] of Object.entries( seeds ) ) { + if ( role === 'background' ) { + continue; + } + const purpose = + role === 'primary' || role === 'error' + ? 'interactive' + : 'status'; + const full = buildAccentRamp( seed, backgroundRamp ); + const result = buildAccentRamp( seed, backgroundRamp, purpose ); + const mappedSteps = Object.entries( colorTokens ) + .filter( ( [ key ] ) => key.startsWith( `${ role }-` ) ) + .map( + ( [ key ] ) => + key.slice( role.length + 1 ) as keyof Ramp + ); + + for ( const step of mappedSteps ) { + expect( result.ramp[ step ] ).toBe( full.ramp[ step ] ); + } + expect( result.warnings ).toEqual( full.warnings ); + expect( result.direction ).toBe( full.direction ); + expect( result.ramp ).not.toHaveProperty( 'stroke2' ); + expect( result.ramp ).not.toHaveProperty( 'surface6' ); + if ( purpose === 'status' ) { + expect( result.ramp ).not.toHaveProperty( 'stroke4' ); + expect( result.ramp ).not.toHaveProperty( 'fgSurface5' ); + } + } + } + ); + + it.each( combinations )( + 'preserves foreground and stroke contrast for each purpose over $background', + ( { background, primary } ) => { + const backgroundRamp = buildBgRamp( background ); + for ( const purpose of [ 'interactive', 'status' ] as const ) { + const result = buildAccentRamp( + primary, + backgroundRamp, + purpose + ); + for ( const referenceRamp of [ backgroundRamp, result ] ) { + for ( const surface of [ + 'surface1', + 'surface2', + 'surface3', + ] as const ) { + expect( + getContrast( + referenceRamp.ramp[ surface ], + result.ramp.stroke3 + ) + ).toBeGreaterThanOrEqual( 3 ); + expect( + getContrast( + referenceRamp.ramp[ surface ], + result.ramp.fgSurface3 + ) + ).toBeGreaterThanOrEqual( 4.5 ); + } + for ( const surface of [ + 'surface1', + 'surface2', + 'surface3', + 'surface4', + 'surface5', + ] as const ) { + expect( + getContrast( + referenceRamp.ramp[ surface ], + result.ramp.fgSurface4 + ) + ).toBeGreaterThanOrEqual( 4.5 ); + } + } + } + } + ); +} ); diff --git a/packages/theme/src/prebuilt/ts/color-tokens.ts b/packages/theme/src/prebuilt/ts/color-tokens.ts index fc25a7c49ba0ba..e3dcc6c41af9fb 100644 --- a/packages/theme/src/prebuilt/ts/color-tokens.ts +++ b/packages/theme/src/prebuilt/ts/color-tokens.ts @@ -21,8 +21,6 @@ export default { 'foreground-interactive-brand-strong-active', ], 'primary-surface4': [ 'background-interactive-brand-weak-active' ], - 'primary-fgSurface5': [ 'foreground-interactive-brand-active' ], - 'primary-fgSurface4': [ 'foreground-interactive-brand' ], 'primary-stroke3': [ 'background-thumb-brand', 'background-thumb-brand-active', @@ -33,24 +31,26 @@ export default { 'primary-stroke4': [ 'stroke-interactive-brand-active' ], 'primary-stroke1': [ 'stroke-surface-brand' ], 'primary-surface1': [ 'background-surface-brand' ], + 'primary-fgSurface4': [ 'foreground-interactive-brand' ], + 'primary-fgSurface5': [ 'foreground-interactive-brand-active' ], 'info-surface2': [ 'background-surface-info-weak' ], 'info-surface4': [ 'background-surface-info' ], - 'info-fgSurface4': [ 'foreground-content-info' ], 'info-fgSurface3': [ 'foreground-content-info-weak' ], 'info-stroke3': [ 'stroke-surface-info-strong' ], 'info-stroke1': [ 'stroke-surface-info' ], + 'info-fgSurface4': [ 'foreground-content-info' ], 'success-surface2': [ 'background-surface-success-weak' ], 'success-surface4': [ 'background-surface-success' ], - 'success-fgSurface4': [ 'foreground-content-success' ], 'success-fgSurface3': [ 'foreground-content-success-weak' ], 'success-stroke3': [ 'stroke-surface-success-strong' ], 'success-stroke1': [ 'stroke-surface-success' ], + 'success-fgSurface4': [ 'foreground-content-success' ], 'warning-surface2': [ 'background-surface-warning-weak' ], 'warning-surface4': [ 'background-surface-warning' ], - 'warning-fgSurface4': [ 'foreground-content-warning' ], 'warning-fgSurface3': [ 'foreground-content-warning-weak' ], 'warning-stroke3': [ 'stroke-surface-warning-strong' ], 'warning-stroke1': [ 'stroke-surface-warning' ], + 'warning-fgSurface4': [ 'foreground-content-warning' ], 'error-bgFill1': [ 'background-interactive-error-strong' ], 'error-bgFill2': [ 'background-interactive-error-strong-active' ], 'error-fgFill': [ @@ -65,11 +65,6 @@ export default { 'background-interactive-error-weak-active', 'background-surface-error', ], - 'error-fgSurface5': [ 'foreground-interactive-error-active' ], - 'error-fgSurface4': [ - 'foreground-content-error', - 'foreground-interactive-error', - ], 'error-fgSurface3': [ 'foreground-content-error-weak' ], 'error-stroke3': [ 'stroke-interactive-error', @@ -78,6 +73,11 @@ export default { ], 'error-stroke4': [ 'stroke-interactive-error-active' ], 'error-stroke1': [ 'stroke-surface-error' ], + 'error-fgSurface4': [ + 'foreground-content-error', + 'foreground-interactive-error', + ], + 'error-fgSurface5': [ 'foreground-interactive-error-active' ], 'bg-surface2': [ 'background-surface-neutral' ], 'bg-surface5': [ 'background-interactive-brand-strong-disabled', @@ -86,15 +86,6 @@ export default { ], 'bg-surface4': [ 'background-interactive-neutral-weak-active' ], 'bg-surface3': [ 'background-surface-neutral-strong' ], - 'bg-fgSurface5': [ - 'foreground-interactive-neutral-active', - 'foreground-interactive-neutral-weak-active', - ], - 'bg-fgSurface4': [ - 'background-thumb-neutral', - 'foreground-content-neutral', - 'foreground-interactive-neutral', - ], 'bg-fgSurface3': [ 'foreground-content-neutral-weak', 'foreground-interactive-neutral-weak', @@ -138,10 +129,19 @@ export default { 'foreground-interactive-neutral-strong-active', ], 'bg-surface1': [ 'background-surface-neutral-weak' ], + 'bg-fgSurface4': [ + 'background-thumb-neutral', + 'foreground-content-neutral', + 'foreground-interactive-neutral', + ], + 'bg-fgSurface5': [ + 'foreground-interactive-neutral-active', + 'foreground-interactive-neutral-weak-active', + ], 'caution-surface2': [ 'background-surface-caution-weak' ], 'caution-surface4': [ 'background-surface-caution' ], - 'caution-fgSurface4': [ 'foreground-content-caution' ], 'caution-fgSurface3': [ 'foreground-content-caution-weak' ], 'caution-stroke3': [ 'stroke-surface-caution-strong' ], 'caution-stroke1': [ 'stroke-surface-caution' ], + 'caution-fgSurface4': [ 'foreground-content-caution' ], } as Record< string, string[] >; diff --git a/packages/theme/src/test/use-theme-provider-styles.jsdom.test.tsx b/packages/theme/src/test/use-theme-provider-styles.jsdom.test.tsx index 602239b62d5992..966563f1c095ed 100644 --- a/packages/theme/src/test/use-theme-provider-styles.jsdom.test.tsx +++ b/packages/theme/src/test/use-theme-provider-styles.jsdom.test.tsx @@ -18,7 +18,11 @@ import { renderHook } from '@testing-library/react'; import { type ReactNode } from 'react'; import { ThemeProvider } from '../theme-provider'; import { useThemeProviderStyles } from '../use-theme-provider-styles'; -import { DEFAULT_SEED_COLORS } from '../color-ramps'; +import { + buildAccentRamp, + buildBgRamp, + DEFAULT_SEED_COLORS, +} from '../color-ramps'; describe( 'useThemeProviderStyles', () => { describe( 'resolvedSettings', () => { @@ -109,6 +113,40 @@ describe( 'useThemeProviderStyles', () => { } ); describe( 'color styles', () => { + it( 'keeps interactive colors when a brand seed was previously cached as a status color', () => { + const background = '#fafafa'; + const { result, rerender } = renderHook( + ( { primary } ) => + useThemeProviderStyles( { + color: { primary, background }, + } ), + { initialProps: { primary: '#abcdef' } } + ); + + for ( const primary of [ + DEFAULT_SEED_COLORS.info, + DEFAULT_SEED_COLORS.success, + DEFAULT_SEED_COLORS.info, + ] ) { + rerender( { primary } ); + const full = buildAccentRamp( + primary, + buildBgRamp( background ) + ); + expect( + result.current.themeProviderStyles[ + '--wpds-color-foreground-interactive-brand-active' + ] + ).toBe( full.ramp.fgSurface5 ); + expect( + result.current.themeProviderStyles[ + '--wpds-color-stroke-interactive-brand-active' + ] + ).toBe( full.ramp.stroke4 ); + expect( result.current.colorWarnings ).toEqual( [] ); + } + } ); + it( 'omits color custom properties by default', () => { const { result } = renderHook( () => useThemeProviderStyles() ); diff --git a/packages/theme/src/theme-provider-color-warnings.ts b/packages/theme/src/theme-provider-color-warnings.ts index becb00566fcc83..962a5f93ade01b 100644 --- a/packages/theme/src/theme-provider-color-warnings.ts +++ b/packages/theme/src/theme-provider-color-warnings.ts @@ -1,6 +1,10 @@ import type { DEFAULT_SEED_COLORS } from './color-ramps/lib/constants.ts'; import { getContrast } from './color-ramps/lib/color-utils.ts'; -import type { Ramp, RampResult } from './color-ramps/lib/types.ts'; +import type { + AccentRampResult, + Ramp, + RampResult, +} from './color-ramps/lib/types.ts'; import { getSemanticColorCustomProperty, MINIMUM_TEXT_CONTRAST, @@ -16,7 +20,9 @@ export type ThemeProviderColorWarning = | { type: 'ramp'; ramp: ThemeProviderColorRampName; - step: keyof Ramp; + // Preserve the callback's published step names, including steps that + // are no longer generated because no semantic color uses them. + step: keyof Ramp | 'fgSurface1' | 'bgFillDark' | 'fgFillDark'; } | { type: 'contrast'; @@ -29,7 +35,10 @@ export type ThemeProviderColorWarning = }; export function collectThemeProviderColorWarnings( - ramps: ReadonlyMap< ThemeProviderColorRampName, RampResult >, + ramps: ReadonlyMap< + ThemeProviderColorRampName, + RampResult | AccentRampResult + >, colorValues: ReadonlyMap< string, string > ): ThemeProviderColorWarning[] { const warnings: ThemeProviderColorWarning[] = []; diff --git a/packages/theme/src/use-theme-provider-styles.ts b/packages/theme/src/use-theme-provider-styles.ts index 825a686b2c21f2..db7326a848a5c2 100644 --- a/packages/theme/src/use-theme-provider-styles.ts +++ b/packages/theme/src/use-theme-provider-styles.ts @@ -19,6 +19,7 @@ import { type RampResult, } from './color-ramps/index.ts'; import { getColorString } from './color-ramps/lib/color-utils.ts'; +import type { AccentRampResult } from './color-ramps/lib/types.ts'; import type { ThemeProviderProps } from './types.ts'; import { collectThemeProviderColorWarnings, @@ -130,7 +131,10 @@ function legacyWpAdminThemeOverridesCSS( accent: string ): Entry[] { } function colorTokensCSS( - computedColorRamps: Map< ThemeProviderColorRampName, RampResult > + computedColorRamps: Map< + ThemeProviderColorRampName, + RampResult | AccentRampResult + > ): Entry[] { const entries: Entry[] = []; @@ -181,7 +185,7 @@ function generateThemeProviderColors( }; const computedColorRamps = new Map< ThemeProviderColorRampName, - RampResult + RampResult | AccentRampResult >(); const bgRamp = getCachedBgRamp( seeds.background ); @@ -191,7 +195,13 @@ function generateThemeProviderColors( rampName, rampName === 'background' ? bgRamp - : getCachedAccentRamp( seed, bgRamp ) + : getCachedAccentRamp( + seed, + bgRamp, + rampName === 'primary' || rampName === 'error' + ? 'interactive' + : 'status' + ) ); } const colorEntries = colorTokensCSS( computedColorRamps ); diff --git a/packages/theme/tokens/color.json b/packages/theme/tokens/color.json index b2ef5a6bfaab90..a45d48f921a607 100644 --- a/packages/theme/tokens/color.json +++ b/packages/theme/tokens/color.json @@ -66,20 +66,6 @@ "hex": "#fff" } }, - "fgSurface5": { - "$value": { - "colorSpace": "srgb", - "components": [ 0.012, 0.024, 0.231 ], - "hex": "#03063b" - } - }, - "fgSurface4": { - "$value": { - "colorSpace": "srgb", - "components": [ 0.133, 0.18, 0.855 ], - "hex": "#222eda" - } - }, "fgSurface3": { "$value": { "colorSpace": "srgb", @@ -94,13 +80,6 @@ "hex": "#5a82ff" } }, - "fgSurface1": { - "$value": { - "colorSpace": "srgb", - "components": [ 0.525, 0.663, 1 ], - "hex": "#86a9ff" - } - }, "stroke3": { "$value": { "colorSpace": "srgb", @@ -129,20 +108,6 @@ "hex": "#aebbda" } }, - "bgFillDark": { - "$value": { - "colorSpace": "srgb", - "components": [ 0.102, 0.118, 0.153 ], - "hex": "#1a1e27" - } - }, - "fgFillDark": { - "$value": { - "colorSpace": "srgb", - "components": [ 0.937, 0.941, 0.949 ], - "hex": "#eff0f2" - } - }, "bgFillInverted2": { "$value": { "colorSpace": "srgb", @@ -170,6 +135,20 @@ "components": [ 0.929, 0.945, 0.988 ], "hex": "#edf1fc" } + }, + "fgSurface4": { + "$value": { + "colorSpace": "srgb", + "components": [ 0.133, 0.18, 0.855 ], + "hex": "#222eda" + } + }, + "fgSurface5": { + "$value": { + "colorSpace": "srgb", + "components": [ 0.012, 0.024, 0.231 ], + "hex": "#03063b" + } } }, "info": { @@ -229,20 +208,6 @@ "hex": "#fff" } }, - "fgSurface5": { - "$value": { - "colorSpace": "srgb", - "components": [ 0, 0.075, 0.165 ], - "hex": "#00132a" - } - }, - "fgSurface4": { - "$value": { - "colorSpace": "srgb", - "components": [ 0, 0.314, 0.573 ], - "hex": "#005092" - } - }, "fgSurface3": { "$value": { "colorSpace": "srgb", @@ -257,13 +222,6 @@ "hex": "#008bfa" } }, - "fgSurface1": { - "$value": { - "colorSpace": "srgb", - "components": [ 0.349, 0.69, 1 ], - "hex": "#59b0ff" - } - }, "stroke3": { "$value": { "colorSpace": "srgb", @@ -292,20 +250,6 @@ "hex": "#adc6e2" } }, - "bgFillDark": { - "$value": { - "colorSpace": "srgb", - "components": [ 0.102, 0.122, 0.141 ], - "hex": "#1a1f24" - } - }, - "fgFillDark": { - "$value": { - "colorSpace": "srgb", - "components": [ 0.933, 0.941, 0.949 ], - "hex": "#eef0f2" - } - }, "bgFillInverted2": { "$value": { "colorSpace": "srgb", @@ -333,6 +277,20 @@ "components": [ 0.906, 0.945, 0.992 ], "hex": "#e7f1fd" } + }, + "fgSurface4": { + "$value": { + "colorSpace": "srgb", + "components": [ 0, 0.314, 0.573 ], + "hex": "#005092" + } + }, + "fgSurface5": { + "$value": { + "colorSpace": "srgb", + "components": [ 0, 0.075, 0.165 ], + "hex": "#00132a" + } } }, "success": { @@ -392,20 +350,6 @@ "hex": "#fff" } }, - "fgSurface5": { - "$value": { - "colorSpace": "srgb", - "components": [ 0.016, 0.09, 0.031 ], - "hex": "#041708" - } - }, - "fgSurface4": { - "$value": { - "colorSpace": "srgb", - "components": [ 0.129, 0.357, 0.184 ], - "hex": "#215b2f" - } - }, "fgSurface3": { "$value": { "colorSpace": "srgb", @@ -420,13 +364,6 @@ "hex": "#2b9f4e" } }, - "fgSurface1": { - "$value": { - "colorSpace": "srgb", - "components": [ 0.329, 0.757, 0.431 ], - "hex": "#54c16e" - } - }, "stroke3": { "$value": { "colorSpace": "srgb", @@ -455,20 +392,6 @@ "hex": "#92d39d" } }, - "bgFillDark": { - "$value": { - "colorSpace": "srgb", - "components": [ 0.106, 0.122, 0.11 ], - "hex": "#1b1f1c" - } - }, - "fgFillDark": { - "$value": { - "colorSpace": "srgb", - "components": [ 0.925, 0.949, 0.925 ], - "hex": "#ecf2ec" - } - }, "bgFillInverted2": { "$value": { "colorSpace": "srgb", @@ -496,6 +419,20 @@ "components": [ 0.831, 0.984, 0.851 ], "hex": "#d4fbd9" } + }, + "fgSurface4": { + "$value": { + "colorSpace": "srgb", + "components": [ 0.129, 0.357, 0.184 ], + "hex": "#215b2f" + } + }, + "fgSurface5": { + "$value": { + "colorSpace": "srgb", + "components": [ 0.016, 0.09, 0.031 ], + "hex": "#041708" + } } }, "warning": { @@ -555,20 +492,6 @@ "hex": "#fff" } }, - "fgSurface5": { - "$value": { - "colorSpace": "srgb", - "components": [ 0.106, 0.071, 0.012 ], - "hex": "#1b1203" - } - }, - "fgSurface4": { - "$value": { - "colorSpace": "srgb", - "components": [ 0.396, 0.298, 0.098 ], - "hex": "#654c19" - } - }, "fgSurface3": { "$value": { "colorSpace": "srgb", @@ -583,13 +506,6 @@ "hex": "#b57f00" } }, - "fgSurface1": { - "$value": { - "colorSpace": "srgb", - "components": [ 0.843, 0.627, 0.169 ], - "hex": "#d7a02b" - } - }, "stroke3": { "$value": { "colorSpace": "srgb", @@ -618,20 +534,6 @@ "hex": "#e1bd7c" } }, - "bgFillDark": { - "$value": { - "colorSpace": "srgb", - "components": [ 0.122, 0.118, 0.106 ], - "hex": "#1f1e1b" - } - }, - "fgFillDark": { - "$value": { - "colorSpace": "srgb", - "components": [ 0.961, 0.937, 0.898 ], - "hex": "#f5efe5" - } - }, "bgFillInverted2": { "$value": { "colorSpace": "srgb", @@ -659,6 +561,20 @@ "components": [ 1, 0.925, 0.796 ], "hex": "#ffeccb" } + }, + "fgSurface4": { + "$value": { + "colorSpace": "srgb", + "components": [ 0.396, 0.298, 0.098 ], + "hex": "#654c19" + } + }, + "fgSurface5": { + "$value": { + "colorSpace": "srgb", + "components": [ 0.106, 0.071, 0.012 ], + "hex": "#1b1203" + } } }, "error": { @@ -718,20 +634,6 @@ "hex": "#fff" } }, - "fgSurface5": { - "$value": { - "colorSpace": "srgb", - "components": [ 0.149, 0.004, 0.004 ], - "hex": "#260101" - } - }, - "fgSurface4": { - "$value": { - "colorSpace": "srgb", - "components": [ 0.596, 0.059, 0.059 ], - "hex": "#980f0f" - } - }, "fgSurface3": { "$value": { "colorSpace": "srgb", @@ -746,13 +648,6 @@ "hex": "#f74c40" } }, - "fgSurface1": { - "$value": { - "colorSpace": "srgb", - "components": [ 1, 0.533, 0.475 ], - "hex": "#ff8879" - } - }, "stroke3": { "$value": { "colorSpace": "srgb", @@ -781,20 +676,6 @@ "hex": "#e0afa7" } }, - "bgFillDark": { - "$value": { - "colorSpace": "srgb", - "components": [ 0.137, 0.11, 0.106 ], - "hex": "#231c1b" - } - }, - "fgFillDark": { - "$value": { - "colorSpace": "srgb", - "components": [ 0.949, 0.937, 0.937 ], - "hex": "#f2efef" - } - }, "bgFillInverted2": { "$value": { "colorSpace": "srgb", @@ -822,6 +703,20 @@ "components": [ 0.996, 0.933, 0.922 ], "hex": "#feeeeb" } + }, + "fgSurface4": { + "$value": { + "colorSpace": "srgb", + "components": [ 0.596, 0.059, 0.059 ], + "hex": "#980f0f" + } + }, + "fgSurface5": { + "$value": { + "colorSpace": "srgb", + "components": [ 0.149, 0.004, 0.004 ], + "hex": "#260101" + } } }, "bg": { @@ -881,20 +776,6 @@ "hex": "#fff" } }, - "fgSurface5": { - "$value": { - "colorSpace": "srgb", - "components": [ 0.078, 0.078, 0.078 ], - "hex": "#141414" - } - }, - "fgSurface4": { - "$value": { - "colorSpace": "srgb", - "components": [ 0.314, 0.314, 0.314 ], - "hex": "#505050" - } - }, "fgSurface3": { "$value": { "colorSpace": "srgb", @@ -909,13 +790,6 @@ "hex": "#8d8d8d" } }, - "fgSurface1": { - "$value": { - "colorSpace": "srgb", - "components": [ 0.682, 0.682, 0.682 ], - "hex": "#aeaeae" - } - }, "stroke3": { "$value": { "colorSpace": "srgb", @@ -944,20 +818,6 @@ "hex": "#f0f0f0" } }, - "bgFillDark": { - "$value": { - "colorSpace": "srgb", - "components": [ 0.118, 0.118, 0.118 ], - "hex": "#1e1e1e" - } - }, - "fgFillDark": { - "$value": { - "colorSpace": "srgb", - "components": [ 0.941, 0.941, 0.941 ], - "hex": "#f0f0f0" - } - }, "bgFillInverted2": { "$value": { "colorSpace": "srgb", @@ -985,6 +845,20 @@ "components": [ 0.976, 0.976, 0.976 ], "hex": "#f9f9f9" } + }, + "fgSurface4": { + "$value": { + "colorSpace": "srgb", + "components": [ 0.314, 0.314, 0.314 ], + "hex": "#505050" + } + }, + "fgSurface5": { + "$value": { + "colorSpace": "srgb", + "components": [ 0.078, 0.078, 0.078 ], + "hex": "#141414" + } } }, "caution": { @@ -1044,20 +918,6 @@ "hex": "#fff" } }, - "fgSurface5": { - "$value": { - "colorSpace": "srgb", - "components": [ 0.094, 0.078, 0.008 ], - "hex": "#181402" - } - }, - "fgSurface4": { - "$value": { - "colorSpace": "srgb", - "components": [ 0.361, 0.31, 0.086 ], - "hex": "#5c4f16" - } - }, "fgSurface3": { "$value": { "colorSpace": "srgb", @@ -1072,13 +932,6 @@ "hex": "#a48700" } }, - "fgSurface1": { - "$value": { - "colorSpace": "srgb", - "components": [ 0.78, 0.659, 0 ], - "hex": "#c7a800" - } - }, "stroke3": { "$value": { "colorSpace": "srgb", @@ -1107,20 +960,6 @@ "hex": "#d0c28b" } }, - "bgFillDark": { - "$value": { - "colorSpace": "srgb", - "components": [ 0.122, 0.118, 0.106 ], - "hex": "#1f1e1b" - } - }, - "fgFillDark": { - "$value": { - "colorSpace": "srgb", - "components": [ 0.98, 0.945, 0.788 ], - "hex": "#faf1c9" - } - }, "bgFillInverted2": { "$value": { "colorSpace": "srgb", @@ -1148,6 +987,20 @@ "components": [ 1, 0.937, 0.663 ], "hex": "#ffefa9" } + }, + "fgSurface4": { + "$value": { + "colorSpace": "srgb", + "components": [ 0.361, 0.31, 0.086 ], + "hex": "#5c4f16" + } + }, + "fgSurface5": { + "$value": { + "colorSpace": "srgb", + "components": [ 0.094, 0.078, 0.008 ], + "hex": "#181402" + } } } }, From 26fb288d15b8f5d6c365f07def7310e33bb34493 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Thu, 3 Sep 2026 11:06:18 +0200 Subject: [PATCH 14/44] Theme: Preserve surface spacing near black and white --- .../internal/_wpds-token-fallbacks.scss | 4 +- packages/theme/CHANGELOG.md | 2 +- packages/theme/prebuilt/css/design-tokens.css | 2 +- .../prebuilt/js/design-token-fallbacks.mjs | 4 +- .../color-ramps/lib/build-perceptual-steps.ts | 17 +- .../src/color-ramps/lib/default-ramps.ts | 12 +- .../test/__snapshots__/index.test.ts.snap | 156 +++++++++--------- .../color-ramps/test/surface-spacing.test.ts | 99 +++++++++++ packages/theme/tokens/color.json | 24 +-- 9 files changed, 209 insertions(+), 111 deletions(-) create mode 100644 packages/theme/src/color-ramps/test/surface-spacing.test.ts diff --git a/packages/base-styles/internal/_wpds-token-fallbacks.scss b/packages/base-styles/internal/_wpds-token-fallbacks.scss index 091d1b4b154f99..6baf8eddfaceab 100644 --- a/packages/base-styles/internal/_wpds-token-fallbacks.scss +++ b/packages/base-styles/internal/_wpds-token-fallbacks.scss @@ -40,7 +40,7 @@ $fallbacks: ( "--wpds-color-background-interactive-neutral-weak-active": "#efefef", "--wpds-color-background-interactive-neutral-weak-disabled": "#0000", "--wpds-color-background-surface-brand": - "color-mix(in oklch, var(--wp-admin-theme-color, #3858e9) 9%, white)", + "color-mix(in oklch, var(--wp-admin-theme-color, #3858e9) 8%, white)", "--wpds-color-background-surface-caution": "#ffea96", "--wpds-color-background-surface-caution-weak": "#fff9ca", "--wpds-color-background-surface-error": "#fae5e1", @@ -97,7 +97,7 @@ $fallbacks: ( "--wpds-color-foreground-interactive-neutral-strong": "#f0f0f0", "--wpds-color-foreground-interactive-neutral-strong-active": "#f0f0f0", "--wpds-color-foreground-interactive-neutral-strong-disabled": "#8d8d8d", - "--wpds-color-foreground-interactive-neutral-weak": "#737373", + "--wpds-color-foreground-interactive-neutral-weak": "#717171", "--wpds-color-foreground-interactive-neutral-weak-active": "#141414", "--wpds-color-foreground-interactive-neutral-weak-disabled": "#8d8d8d", "--wpds-color-stroke-focus": "var(--wp-admin-theme-color, #3858e9)", diff --git a/packages/theme/CHANGELOG.md b/packages/theme/CHANGELOG.md index 74a24204b4de96..e02e04af6c9b07 100644 --- a/packages/theme/CHANGELOG.md +++ b/packages/theme/CHANGELOG.md @@ -58,7 +58,7 @@ ### Enhancements -- Generate perception-aware color ramps with ordered surface and stroke progressions, align normal content with resting interactive colors, reserve the fifth foreground step for interaction states, and keep WCAG contrast floors unchanged. ([#82294](https://github.com/WordPress/gutenberg/pull/82294)) +- Generate perception-aware color ramps with ordered surface and stroke progressions, preserve available surface spacing near black and white, align normal content with resting interactive colors, reserve the fifth foreground step for interaction states, and keep WCAG contrast floors unchanged. ([#82294](https://github.com/WordPress/gutenberg/pull/82294)) - Add a Lightning CSS visitor plugin that injects design system token fallbacks, available as `@wordpress/theme/lightningcss-plugins/lightningcss-ds-token-fallbacks`, with an optional peer range of `>=1.33.0 <2.0.0`. ([#80401](https://github.com/WordPress/gutenberg/pull/80401)) ### Bug Fixes diff --git a/packages/theme/prebuilt/css/design-tokens.css b/packages/theme/prebuilt/css/design-tokens.css index 9322a8c80d8902..1dd31b9e7d5ddb 100644 --- a/packages/theme/prebuilt/css/design-tokens.css +++ b/packages/theme/prebuilt/css/design-tokens.css @@ -132,7 +132,7 @@ /* Foreground color for content like text with normal emphasis. */ --wpds-color-foreground-content-neutral: #505050; /* Foreground color for content like text with weak emphasis. */ - --wpds-color-foreground-content-neutral-weak: #737373; + --wpds-color-foreground-content-neutral-weak: #717171; /* Foreground color for content like text with success tone and normal emphasis. */ --wpds-color-foreground-content-success: #215b2f; /* Foreground color for content like text with success tone and weak emphasis. */ diff --git a/packages/theme/prebuilt/js/design-token-fallbacks.mjs b/packages/theme/prebuilt/js/design-token-fallbacks.mjs index 2c64165bcdad3b..c15e96f380767e 100644 --- a/packages/theme/prebuilt/js/design-token-fallbacks.mjs +++ b/packages/theme/prebuilt/js/design-token-fallbacks.mjs @@ -38,7 +38,7 @@ export default { '--wpds-color-background-interactive-neutral-weak-active': '#efefef', '--wpds-color-background-interactive-neutral-weak-disabled': '#0000', '--wpds-color-background-surface-brand': - 'color-mix(in oklch, var(--wp-admin-theme-color, #3858e9) 9%, white)', + 'color-mix(in oklch, var(--wp-admin-theme-color, #3858e9) 8%, white)', '--wpds-color-background-surface-caution': '#ffea96', '--wpds-color-background-surface-caution-weak': '#fff9ca', '--wpds-color-background-surface-error': '#fae5e1', @@ -95,7 +95,7 @@ export default { '--wpds-color-foreground-interactive-neutral-strong': '#f0f0f0', '--wpds-color-foreground-interactive-neutral-strong-active': '#f0f0f0', '--wpds-color-foreground-interactive-neutral-strong-disabled': '#8d8d8d', - '--wpds-color-foreground-interactive-neutral-weak': '#737373', + '--wpds-color-foreground-interactive-neutral-weak': '#717171', '--wpds-color-foreground-interactive-neutral-weak-active': '#141414', '--wpds-color-foreground-interactive-neutral-weak-disabled': '#8d8d8d', '--wpds-color-stroke-focus': 'var(--wp-admin-theme-color, #3858e9)', diff --git a/packages/theme/src/color-ramps/lib/build-perceptual-steps.ts b/packages/theme/src/color-ramps/lib/build-perceptual-steps.ts index dadab9adb8b682..4fa1356c8bc746 100644 --- a/packages/theme/src/color-ramps/lib/build-perceptual-steps.ts +++ b/packages/theme/src/color-ramps/lib/build-perceptual-steps.ts @@ -225,16 +225,15 @@ function rebuildSurfaces( ramp: BaseRampResult, purpose: AccentRampPurpose ) { surface3Lightness - surface2Lightness ) / 2; - const elevationGap = Math.max( - 0.001, - Math.min( - authoredElevationGap, - surface2Lightness - 0.001, - 1 - surface2Lightness - 0.001 - ) + const elevationGap = Math.max( 0.001, authoredElevationGap ); + // Clip each side independently. Requiring equal gaps at black or white + // would also collapse the side that still has room. + const targetSurface1Lightness = clampUnitInterval( + surface2Lightness - elevationGap + ); + const targetSurface3Lightness = clampUnitInterval( + surface2Lightness + elevationGap ); - const targetSurface1Lightness = surface2Lightness - elevationGap; - const targetSurface3Lightness = surface2Lightness + elevationGap; const emphasisDirection = ramp.direction === 'lighter' ? 1 : -1; const sameDirectionElevationLightness = ramp.direction === 'lighter' diff --git a/packages/theme/src/color-ramps/lib/default-ramps.ts b/packages/theme/src/color-ramps/lib/default-ramps.ts index 42ec7ec910c3e4..a1be287ff87018 100644 --- a/packages/theme/src/color-ramps/lib/default-ramps.ts +++ b/packages/theme/src/color-ramps/lib/default-ramps.ts @@ -15,7 +15,7 @@ export const DEFAULT_RAMPS: Record< surface5: '#e3e3e3', surface4: '#efefef', surface3: '#fff', - fgSurface3: '#737373', + fgSurface3: '#717171', fgSurface2: '#8d8d8d', stroke3: '#8d8d8d', stroke4: '#6e6e6e', @@ -24,7 +24,7 @@ export const DEFAULT_RAMPS: Record< bgFillInverted2: '#1e1e1e', bgFillInverted1: '#2d2d2d', fgFillInverted: '#f0f0f0', - surface1: '#f9f9f9', + surface1: '#f7f7f7', fgSurface4: '#505050', fgSurface5: '#141414', }, @@ -49,7 +49,7 @@ export const DEFAULT_RAMPS: Record< bgFillInverted2: '#1a1e27', bgFillInverted1: '#13009f', fgFillInverted: '#eff0f2', - surface1: '#edf1fc', + surface1: '#edf1fb', fgSurface4: '#222eda', fgSurface5: '#03063b', }, @@ -65,7 +65,7 @@ export const DEFAULT_RAMPS: Record< surface5: '#d3e4f7', surface4: '#deebfa', surface3: '#fff', - fgSurface3: '#0070c8', + fgSurface3: '#006fc7', fgSurface2: '#008bfa', stroke3: '#006bd7', stroke4: '#004fa9', @@ -160,7 +160,7 @@ export const DEFAULT_RAMPS: Record< surface5: '#f7dbd7', surface4: '#fae5e1', surface3: '#fff', - fgSurface3: '#d91a1a', + fgSurface3: '#d81a1a', fgSurface2: '#f74c40', stroke3: '#cc1818', stroke4: '#9d0000', @@ -169,7 +169,7 @@ export const DEFAULT_RAMPS: Record< bgFillInverted2: '#231c1b', bgFillInverted1: '#640000', fgFillInverted: '#f2efef', - surface1: '#feeeeb', + surface1: '#fdedea', fgSurface4: '#980f0f', fgSurface5: '#260101', }, diff --git a/packages/theme/src/color-ramps/test/__snapshots__/index.test.ts.snap b/packages/theme/src/color-ramps/test/__snapshots__/index.test.ts.snap index 53a933cc5e3755..bdf3aea7666c88 100644 --- a/packages/theme/src/color-ramps/test/__snapshots__/index.test.ts.snap +++ b/packages/theme/src/color-ramps/test/__snapshots__/index.test.ts.snap @@ -1551,7 +1551,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgFill": "#595959", "fgFillInverted": "#1e1e1e", "fgSurface2": "#5e5e5e", - "fgSurface3": "#757575", + "fgSurface3": "#767676", "fgSurface4": "#ddd", "fgSurface5": "#fdfdfd", "stroke1": "#787878", @@ -1560,7 +1560,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "stroke4": "#fff", "surface1": "#000", "surface2": "#000", - "surface3": "#000", + "surface3": "#040404", "surface4": "#0b0b0b", "surface5": "#1a1a1a", "surface6": "#282828", @@ -1591,7 +1591,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgFill": "#595959", "fgFillInverted": "#1e1e1e", "fgSurface2": "#606060", - "fgSurface3": "#797979", + "fgSurface3": "#7a7a7a", "fgSurface4": "#ddd", "fgSurface5": "#fdfdfd", "stroke1": "#787878", @@ -1600,7 +1600,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "stroke4": "#fff", "surface1": "#000", "surface2": "#030303", - "surface3": "#0a0a0a", + "surface3": "#0d0d0d", "surface4": "#111", "surface5": "#1d1d1d", "surface6": "#292929", @@ -1871,14 +1871,14 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgFill": "#666", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#767676", + "fgSurface3": "#747474", "fgSurface4": "#555", "fgSurface5": "#141414", "stroke1": "#c9c9c9", "stroke2": "#b7b7b7", "stroke3": "#717171", "stroke4": "#565656", - "surface1": "#fff", + "surface1": "#fbfbfb", "surface2": "#fff", "surface3": "#fff", "surface4": "#f2f2f2", @@ -15448,14 +15448,14 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFill": "#cbcbcb", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#767676", + "fgSurface3": "#747474", "fgSurface4": "#555", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#fff", + "surface1": "#fbfbfb", "surface2": "#fff", "surface3": "#fff", "surface4": "#f2f2f2", @@ -15481,14 +15481,14 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFill": "#cbcbcb", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#767676", + "fgSurface3": "#747474", "fgSurface4": "#555", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#fff", + "surface1": "#fbfbfb", "surface2": "#fff", "surface3": "#fff", "surface4": "#f2f2f2", @@ -15514,14 +15514,14 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFill": "#cbcbcb", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#767676", + "fgSurface3": "#747474", "fgSurface4": "#555", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#fff", + "surface1": "#fbfbfb", "surface2": "#fff", "surface3": "#fff", "surface4": "#f2f2f2", @@ -15547,14 +15547,14 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFill": "#cbcbcb", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#767676", + "fgSurface3": "#747474", "fgSurface4": "#555", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#fff", + "surface1": "#fbfbfb", "surface2": "#fff", "surface3": "#fff", "surface4": "#f2f2f2", @@ -15580,14 +15580,14 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFill": "#cbcbcb", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#767676", + "fgSurface3": "#747474", "fgSurface4": "#555", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#fff", + "surface1": "#fbfbfb", "surface2": "#fff", "surface3": "#fff", "surface4": "#f2f2f2", @@ -15613,14 +15613,14 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFill": "#cbcbcb", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#767676", + "fgSurface3": "#747474", "fgSurface4": "#555", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#fff", + "surface1": "#fbfbfb", "surface2": "#fff", "surface3": "#fff", "surface4": "#f2f2f2", @@ -15646,14 +15646,14 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFill": "#cbcbcb", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#767676", + "fgSurface3": "#747474", "fgSurface4": "#555", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#fff", + "surface1": "#fbfbfb", "surface2": "#fff", "surface3": "#fff", "surface4": "#f2f2f2", @@ -15679,14 +15679,14 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFill": "#cbcbcb", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#767676", + "fgSurface3": "#747474", "fgSurface4": "#555", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#fff", + "surface1": "#fbfbfb", "surface2": "#fff", "surface3": "#fff", "surface4": "#f2f2f2", @@ -15712,14 +15712,14 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFill": "#cbcbcb", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#767676", + "fgSurface3": "#747474", "fgSurface4": "#555", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#fff", + "surface1": "#fbfbfb", "surface2": "#fff", "surface3": "#fff", "surface4": "#f2f2f2", @@ -15745,14 +15745,14 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFill": "#cbcbcb", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#767676", + "fgSurface3": "#747474", "fgSurface4": "#555", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#fff", + "surface1": "#fbfbfb", "surface2": "#fff", "surface3": "#fff", "surface4": "#f2f2f2", @@ -15778,14 +15778,14 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFill": "#cbcbcb", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#767676", + "fgSurface3": "#747474", "fgSurface4": "#555", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#fff", + "surface1": "#fbfbfb", "surface2": "#fff", "surface3": "#fff", "surface4": "#f2f2f2", @@ -15811,14 +15811,14 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFill": "#cbcbcb", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#767676", + "fgSurface3": "#747474", "fgSurface4": "#555", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#fff", + "surface1": "#fbfbfb", "surface2": "#fff", "surface3": "#fff", "surface4": "#f2f2f2", @@ -15844,14 +15844,14 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFill": "#cbcbcb", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#767676", + "fgSurface3": "#747474", "fgSurface4": "#555", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#fff", + "surface1": "#fbfbfb", "surface2": "#fff", "surface3": "#fff", "surface4": "#f2f2f2", @@ -15877,14 +15877,14 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFill": "#cbcbcb", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#767676", + "fgSurface3": "#747474", "fgSurface4": "#555", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#fff", + "surface1": "#fbfbfb", "surface2": "#fff", "surface3": "#fff", "surface4": "#f2f2f2", @@ -15910,14 +15910,14 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFill": "#cbcbcb", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#767676", + "fgSurface3": "#747474", "fgSurface4": "#555", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#fff", + "surface1": "#fbfbfb", "surface2": "#fff", "surface3": "#fff", "surface4": "#f2f2f2", @@ -15943,14 +15943,14 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFill": "#cbcbcb", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#767676", + "fgSurface3": "#747474", "fgSurface4": "#555", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#fff", + "surface1": "#fbfbfb", "surface2": "#fff", "surface3": "#fff", "surface4": "#f2f2f2", @@ -15976,14 +15976,14 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFill": "#cbcbcb", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#767676", + "fgSurface3": "#747474", "fgSurface4": "#555", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#fff", + "surface1": "#fbfbfb", "surface2": "#fff", "surface3": "#fff", "surface4": "#f2f2f2", @@ -16009,14 +16009,14 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFill": "#cbcbcb", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#767676", + "fgSurface3": "#747474", "fgSurface4": "#555", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#fff", + "surface1": "#fbfbfb", "surface2": "#fff", "surface3": "#fff", "surface4": "#f2f2f2", @@ -16042,14 +16042,14 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFill": "#cbcbcb", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#767676", + "fgSurface3": "#747474", "fgSurface4": "#555", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#fff", + "surface1": "#fbfbfb", "surface2": "#fff", "surface3": "#fff", "surface4": "#f2f2f2", @@ -16075,14 +16075,14 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFill": "#cbcbcb", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#767676", + "fgSurface3": "#747474", "fgSurface4": "#555", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#fff", + "surface1": "#fbfbfb", "surface2": "#fff", "surface3": "#fff", "surface4": "#f2f2f2", @@ -16108,14 +16108,14 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFill": "#cbcbcb", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#767676", + "fgSurface3": "#747474", "fgSurface4": "#555", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#fff", + "surface1": "#fbfbfb", "surface2": "#fff", "surface3": "#fff", "surface4": "#f2f2f2", @@ -16141,14 +16141,14 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFill": "#cbcbcb", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#767676", + "fgSurface3": "#747474", "fgSurface4": "#555", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#fff", + "surface1": "#fbfbfb", "surface2": "#fff", "surface3": "#fff", "surface4": "#f2f2f2", @@ -16174,14 +16174,14 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFill": "#cbcbcb", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#767676", + "fgSurface3": "#747474", "fgSurface4": "#555", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#fff", + "surface1": "#fbfbfb", "surface2": "#fff", "surface3": "#fff", "surface4": "#f2f2f2", @@ -16207,14 +16207,14 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFill": "#cbcbcb", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#767676", + "fgSurface3": "#747474", "fgSurface4": "#555", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#fff", + "surface1": "#fbfbfb", "surface2": "#fff", "surface3": "#fff", "surface4": "#f2f2f2", @@ -16240,14 +16240,14 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFill": "#cbcbcb", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#767676", + "fgSurface3": "#747474", "fgSurface4": "#555", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#fff", + "surface1": "#fbfbfb", "surface2": "#fff", "surface3": "#fff", "surface4": "#f2f2f2", @@ -16273,14 +16273,14 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFill": "#cbcbcb", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#767676", + "fgSurface3": "#747474", "fgSurface4": "#555", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#fff", + "surface1": "#fbfbfb", "surface2": "#fff", "surface3": "#fff", "surface4": "#f2f2f2", @@ -16306,14 +16306,14 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFill": "#cbcbcb", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#767676", + "fgSurface3": "#747474", "fgSurface4": "#555", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#fff", + "surface1": "#fbfbfb", "surface2": "#fff", "surface3": "#fff", "surface4": "#f2f2f2", @@ -16339,14 +16339,14 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFill": "#cbcbcb", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#767676", + "fgSurface3": "#747474", "fgSurface4": "#555", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#fff", + "surface1": "#fbfbfb", "surface2": "#fff", "surface3": "#fff", "surface4": "#f2f2f2", @@ -16372,14 +16372,14 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFill": "#cbcbcb", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#767676", + "fgSurface3": "#747474", "fgSurface4": "#555", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#fff", + "surface1": "#fbfbfb", "surface2": "#fff", "surface3": "#fff", "surface4": "#f2f2f2", @@ -16405,14 +16405,14 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFill": "#cbcbcb", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#767676", + "fgSurface3": "#747474", "fgSurface4": "#555", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#fff", + "surface1": "#fbfbfb", "surface2": "#fff", "surface3": "#fff", "surface4": "#f2f2f2", @@ -16438,14 +16438,14 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFill": "#cbcbcb", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#767676", + "fgSurface3": "#747474", "fgSurface4": "#555", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#fff", + "surface1": "#fbfbfb", "surface2": "#fff", "surface3": "#fff", "surface4": "#f2f2f2", @@ -16471,14 +16471,14 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFill": "#cbcbcb", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#767676", + "fgSurface3": "#747474", "fgSurface4": "#555", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#fff", + "surface1": "#fbfbfb", "surface2": "#fff", "surface3": "#fff", "surface4": "#f2f2f2", @@ -16504,14 +16504,14 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFill": "#cbcbcb", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#767676", + "fgSurface3": "#747474", "fgSurface4": "#555", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#fff", + "surface1": "#fbfbfb", "surface2": "#fff", "surface3": "#fff", "surface4": "#f2f2f2", @@ -16537,14 +16537,14 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFill": "#cbcbcb", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#767676", + "fgSurface3": "#747474", "fgSurface4": "#555", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#fff", + "surface1": "#fbfbfb", "surface2": "#fff", "surface3": "#fff", "surface4": "#f2f2f2", @@ -16570,14 +16570,14 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFill": "#cbcbcb", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#767676", + "fgSurface3": "#747474", "fgSurface4": "#555", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#fff", + "surface1": "#fbfbfb", "surface2": "#fff", "surface3": "#fff", "surface4": "#f2f2f2", @@ -16603,14 +16603,14 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFill": "#cbcbcb", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", - "fgSurface3": "#767676", + "fgSurface3": "#747474", "fgSurface4": "#555", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", "stroke3": "#8f8f8f", "stroke4": "#707070", - "surface1": "#fff", + "surface1": "#fbfbfb", "surface2": "#fff", "surface3": "#fff", "surface4": "#f2f2f2", diff --git a/packages/theme/src/color-ramps/test/surface-spacing.test.ts b/packages/theme/src/color-ramps/test/surface-spacing.test.ts new file mode 100644 index 00000000000000..9b39ffa505514e --- /dev/null +++ b/packages/theme/src/color-ramps/test/surface-spacing.test.ts @@ -0,0 +1,99 @@ +import { describe, expect, it } from 'vitest'; +import { get, OKLrab } from 'colorjs.io/fn'; +import { buildAccentRamp, buildBgRamp, checkAccessibleCombinations } from '..'; +import { DEFAULT_SEED_COLORS } from '../lib/constants'; + +const endpointSeeds = [ '#fff', '#fefefe', '#000', '#010101' ]; + +describe( 'surface spacing at lightness endpoints', () => { + it.each( [ '#fff', '#fefefe' ] )( + 'keeps useful darker surface spacing near white for %s', + ( seed ) => { + const { ramp } = buildBgRamp( seed ); + const lightness = ( color: string ) => + get( color, [ OKLrab, 'l' ] ); + + expect( ramp.surface2 ).toBe( seed ); + expect( lightness( ramp.surface3 ) ).toBeGreaterThanOrEqual( + lightness( ramp.surface2 ) + ); + // A single differing RGB value must not count as restored spacing. + expect( + lightness( ramp.surface2 ) - lightness( ramp.surface1 ) + ).toBeGreaterThan( 0.01 ); + } + ); + + it.each( [ '#000', '#010101' ] )( + 'keeps useful lighter surface spacing near black for %s', + ( seed ) => { + const { ramp } = buildBgRamp( seed ); + const lightness = ( color: string ) => + get( color, [ OKLrab, 'l' ] ); + + expect( ramp.surface2 ).toBe( seed ); + expect( lightness( ramp.surface1 ) ).toBeLessThanOrEqual( + lightness( ramp.surface2 ) + ); + expect( + lightness( ramp.surface3 ) - lightness( ramp.surface2 ) + ).toBeGreaterThan( 0.01 ); + } + ); + + it.each( endpointSeeds )( + 'keeps surface ordering and contrast floors at the %s endpoint', + ( seed ) => { + const backgroundRamp = buildBgRamp( seed ); + const accentRamps = [ + buildAccentRamp( DEFAULT_SEED_COLORS.primary, backgroundRamp ), + buildAccentRamp( DEFAULT_SEED_COLORS.error, backgroundRamp ), + ]; + + for ( const result of [ backgroundRamp, ...accentRamps ] ) { + const { ramp, direction, warnings } = result; + const orderedSteps = + direction === 'lighter' + ? ( [ + 'surface1', + 'surface2', + 'surface3', + 'surface4', + 'surface5', + 'surface6', + ] as const ) + : ( [ + 'surface6', + 'surface5', + 'surface4', + 'surface1', + 'surface2', + 'surface3', + ] as const ); + const lightnesses = orderedSteps.map( ( step ) => + get( ramp[ step ], [ OKLrab, 'l' ] ) + ); + + expect( lightnesses ).toEqual( + [ ...lightnesses ].sort( ( a, b ) => a - b ) + ); + // Only the side blocked by black or white may collapse. + const distinctSteps = + direction === 'lighter' + ? lightnesses.slice( 1 ) + : lightnesses.slice( 0, -1 ); + for ( let index = 1; index < distinctSteps.length; index++ ) { + expect( distinctSteps[ index ] ).toBeGreaterThan( + distinctSteps[ index - 1 ] + ); + } + expect( warnings ).toBeUndefined(); + // Check each ramp's own fill/text pairs. Cross-ramp fill pairs + // are not semantic combinations used by ThemeProvider. + expect( + checkAccessibleCombinations( { bgRamp: result } ) + ).toEqual( [] ); + } + } + ); +} ); diff --git a/packages/theme/tokens/color.json b/packages/theme/tokens/color.json index a45d48f921a607..bedb21bf64a1d1 100644 --- a/packages/theme/tokens/color.json +++ b/packages/theme/tokens/color.json @@ -132,8 +132,8 @@ "surface1": { "$value": { "colorSpace": "srgb", - "components": [ 0.929, 0.945, 0.988 ], - "hex": "#edf1fc" + "components": [ 0.929, 0.945, 0.984 ], + "hex": "#edf1fb" } }, "fgSurface4": { @@ -211,8 +211,8 @@ "fgSurface3": { "$value": { "colorSpace": "srgb", - "components": [ 0, 0.439, 0.784 ], - "hex": "#0070c8" + "components": [ 0, 0.435, 0.78 ], + "hex": "#006fc7" } }, "fgSurface2": { @@ -637,8 +637,8 @@ "fgSurface3": { "$value": { "colorSpace": "srgb", - "components": [ 0.851, 0.102, 0.102 ], - "hex": "#d91a1a" + "components": [ 0.847, 0.102, 0.102 ], + "hex": "#d81a1a" } }, "fgSurface2": { @@ -700,8 +700,8 @@ "surface1": { "$value": { "colorSpace": "srgb", - "components": [ 0.996, 0.933, 0.922 ], - "hex": "#feeeeb" + "components": [ 0.992, 0.929, 0.918 ], + "hex": "#fdedea" } }, "fgSurface4": { @@ -779,8 +779,8 @@ "fgSurface3": { "$value": { "colorSpace": "srgb", - "components": [ 0.451, 0.451, 0.451 ], - "hex": "#737373" + "components": [ 0.443, 0.443, 0.443 ], + "hex": "#717171" } }, "fgSurface2": { @@ -842,8 +842,8 @@ "surface1": { "$value": { "colorSpace": "srgb", - "components": [ 0.976, 0.976, 0.976 ], - "hex": "#f9f9f9" + "components": [ 0.969, 0.969, 0.969 ], + "hex": "#f7f7f7" } }, "fgSurface4": { From ad1625d62fc999ff5d609bc5effde138e593a0ee Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Thu, 3 Sep 2026 11:30:03 +0200 Subject: [PATCH 15/44] Theme: Document perceptual ramp solver contracts --- packages/theme/src/color-ramps/index.ts | 9 +++ .../color-ramps/lib/build-foreground-scale.ts | 69 +++++++++++++++++++ .../color-ramps/lib/build-perceptual-steps.ts | 57 +++++++++++++++ packages/theme/src/color-ramps/lib/index.ts | 11 +++ 4 files changed, 146 insertions(+) diff --git a/packages/theme/src/color-ramps/index.ts b/packages/theme/src/color-ramps/index.ts index 321a42a58d1d11..c2c284dd62ec2d 100644 --- a/packages/theme/src/color-ramps/index.ts +++ b/packages/theme/src/color-ramps/index.ts @@ -61,6 +61,15 @@ function hasAmbiguousEndpointContrast( seed: string ) { ); } +/** + * Try the opposite direction only near the black/white contrast crossover + * when the preferred ramp fails WCAG or lacks interaction-state spacing. + * Switching requires a passing alternate within the seed-drift allowance and + * either repaired WCAG failures or a useful improvement in the APCA gap. + * + * @param seed Original background seed used to compare color drift. + * @param preferred Ramp built in the initially preferred direction. + */ function selectBackgroundRampPolarity( seed: string, preferred: InternalRampResult diff --git a/packages/theme/src/color-ramps/lib/build-foreground-scale.ts b/packages/theme/src/color-ramps/lib/build-foreground-scale.ts index c5ab2f9da4ce9b..1ce8858b581363 100644 --- a/packages/theme/src/color-ramps/lib/build-foreground-scale.ts +++ b/packages/theme/src/color-ramps/lib/build-foreground-scale.ts @@ -52,6 +52,15 @@ function getChromaPreservingColorForLightness( } ); } +/** + * Create an OKLCH-lightness path using the configured chroma policy. Relative + * chroma keeps the seed's proportion of the available sRGB chroma; tapering + * retains the configured neutral treatment. The relative-chroma cache belongs + * to this path, so its colors must be treated as read-only. + * + * @param seed Color supplying hue and chroma. + * @param config Foreground chroma policy. + */ function createColorForLightness( seed: PlainColorObject, config: ForegroundScaleConfig @@ -135,6 +144,15 @@ function meetsContrastFloor( ); } +/** + * Return the smallest log contrast ratio against the padded WCAG target. + * Zero marks the search boundary; negative values mean at least one reference + * fails. Final output checks use the unpadded floor instead. + * + * @param color Candidate foreground. + * @param referenceLuminances Precomputed luminances of its backgrounds. + * @param target Unpadded WCAG contrast ratio. + */ function getContrastTargetMargin( color: PlainColorObject, referenceLuminances: readonly number[], @@ -163,6 +181,18 @@ function meetsContrastTarget( return getContrastTargetMargin( color, referenceLuminances, target ) >= 0; } +/** + * Search toward the stronger endpoint for the padded WCAG target. The caller + * supplies a path whose contrast increases in that direction. If the endpoint + * cannot meet the target, return it for the final warning check to assess. + * + * @param options Search inputs. + * @param options.getColorAtLightness Color path indexed by OKLCH lightness. + * @param options.weakColor Starting foreground. + * @param options.strongColor Strongest allowed foreground. + * @param options.referenceLuminances Background luminances to check together. + * @param options.target Unpadded WCAG contrast ratio. + */ function findColorAtContrastTarget( { getColorAtLightness, weakColor, @@ -205,6 +235,18 @@ function findColorAtContrastTarget( { ); } +/** + * Position a foreground by APCA magnitude on a single-polarity lightness path. + * Targets outside the endpoint range use the corresponding endpoint. This + * search does not enforce WCAG; the caller checks those constraints separately. + * + * @param options Search inputs. + * @param options.background Fixed background for APCA comparisons. + * @param options.getColorAtLightness Color path indexed by OKLCH lightness. + * @param options.weakColor Starting foreground. + * @param options.strongColor Color defining the strong lightness limit. + * @param options.target Requested APCA magnitude in Lc. + */ function findColorAtPerceptualContrast( { background, getColorAtLightness, @@ -247,6 +289,18 @@ function findColorAtPerceptualContrast( { ); } +/** + * Correct a WCAG shortfall introduced by color serialization. Search only + * toward the supplied strong color, then fall back to it if no sampled color + * passes. The caller must still validate the returned color for warnings. + * + * @param options Serialization inputs. + * @param options.color Foreground before serialization. + * @param options.getColorAtLightness Color path indexed by OKLCH lightness. + * @param options.strongColor Strongest allowed fallback. + * @param options.referenceLuminances Background luminances to check together. + * @param options.target Unpadded WCAG contrast floor. + */ function serializeColorMeetingContrast( { color, getColorAtLightness, @@ -282,6 +336,21 @@ function serializeColorMeetingContrast( { return getColorString( strongColor ); } +/** + * Restore the preferred FGS4-to-FGS5 APCA gap after serialization, without + * moving FGS4. Only extend FGS5 when the endpoint can meet both the gap and + * WCAG floor; otherwise retain the already compressed interaction color. + * + * @param options Serialization inputs. + * @param options.background Fixed background for APCA comparisons. + * @param options.normalColor Serialized FGS4. + * @param options.strongColor Serialized FGS5, already checked for WCAG. + * @param options.strongEndpoint Endpoint allowed by the foreground path. + * @param options.getColorAtLightness Color path indexed by OKLCH lightness. + * @param options.referenceLuminances Background luminances to check together. + * @param options.wcagTarget Unpadded WCAG contrast floor. + * @param options.perceptualTarget Preferred APCA gap in Lc. + */ function serializeColorMeetingPerceptualInterval( { background, normalColor, diff --git a/packages/theme/src/color-ramps/lib/build-perceptual-steps.ts b/packages/theme/src/color-ramps/lib/build-perceptual-steps.ts index 4fa1356c8bc746..3c00224ab04383 100644 --- a/packages/theme/src/color-ramps/lib/build-perceptual-steps.ts +++ b/packages/theme/src/color-ramps/lib/build-perceptual-steps.ts @@ -35,6 +35,12 @@ function getPerceptualLightness( color: string | PlainColorObject ) { return get( color, [ OKLrab, 'l' ] ); } +/** + * Create a path indexed by OKLr lightness, not OKLCH lightness. Preserve the + * seed's authored chroma and hue before gamut mapping, which may reduce chroma. + * + * @param seed Color supplying the authored chroma and hue. + */ function createColorAtPerceptualLightness( seed: string | PlainColorObject ): GetColorAtLightness { @@ -71,6 +77,18 @@ function getColorForPerceptualLightness( ); } +/** + * Blend from the anchor's chroma to the step's authored chroma as OKLr lightness + * advances. This lets a stroke move while retaining its configured taper. + * The step supplies the hue; chroma progress is clamped to the given interval. + * + * @param options Path inputs. + * @param options.ramp Ramp containing the authored stroke colors. + * @param options.step Stroke whose hue and final chroma are retained. + * @param options.anchor Color supplying the starting chroma. + * @param options.startLightness Starting OKLr lightness. + * @param options.endLightness Ending OKLr lightness. + */ function createAnchoredColorForStepAtPerceptualLightness( { ramp, step, @@ -112,6 +130,18 @@ function createAnchoredColorForStepAtPerceptualLightness( { }; } +/** + * Search a lightness interval for an OKLab color difference in DeltaEOK2 units. + * The path is expected to progress away from the reference. If the endpoint + * differences do not bracket the target, use the closer endpoint instead. + * + * @param options Search inputs. + * @param options.reference Color from which differences are measured. + * @param options.getColorAtLightness Color path indexed by OKLr lightness. + * @param options.startLightness Starting OKLr lightness. + * @param options.endLightness Ending OKLr lightness. + * @param options.target Requested DeltaEOK2 difference. + */ function findColorAtDeltaE( { reference, getColorAtLightness, @@ -148,6 +178,16 @@ function findColorAtDeltaE( { ); } +/** + * Keep a passing stroke or move it toward the endpoint with the best minimum + * contrast across all references. If neither endpoint passes, return the + * stronger fallback; this function alone does not guarantee a passing result. + * + * @param options Search inputs. + * @param options.color Authored stroke color. + * @param options.references Surfaces the stroke must contrast with. + * @param options.target WCAG ratio, including the caller's safety margin. + */ function findColorMeetingWcag( { color, references, @@ -214,6 +254,14 @@ function findColorMeetingWcag( { ); } +/** + * Balance SF1/SF3 around SF2 in OKLr lightness, clipping each endpoint + * independently. Space SF4-SF6 beyond the elevation steps in the ramp direction + * where room remains. SF6 still sets that range when its output is not needed. + * + * @param ramp Base ramp with SF1-SF3 already ordered by elevation. + * @param purpose Which surface outputs need reconstruction. + */ function rebuildSurfaces( ramp: BaseRampResult, purpose: AccentRampPurpose ) { const nextRamp = { ...ramp.ramp }; const surface1Lightness = getPerceptualLightness( ramp.ramp.surface1 ); @@ -318,6 +366,15 @@ function getStrokeReferences( ); } +/** + * Repair ST3 against the rebuilt surfaces, then reposition ST2 and ST4 only + * when their purpose needs them. Preserve authored stroke differences where + * possible; WCAG correction takes precedence over perceptual spacing. + * + * @param ramp Ramp with its surfaces already rebuilt. + * @param backgroundRamp Additional surfaces behind accent strokes. + * @param purpose Which stroke outputs need reconstruction. + */ function rebuildStrokes( ramp: BaseRampResult, backgroundRamp: BaseRampResult, diff --git a/packages/theme/src/color-ramps/lib/index.ts b/packages/theme/src/color-ramps/lib/index.ts index 934b44b3c3f054..5c7f5c0bb8584b 100644 --- a/packages/theme/src/color-ramps/lib/index.ts +++ b/packages/theme/src/color-ramps/lib/index.ts @@ -221,6 +221,17 @@ type BuildRampOptions = { purpose?: AccentRampPurpose; }; +/** + * Solve base constraints, rebuild surfaces and strokes, then position the + * foreground scale. Seed lightness may shift to make the base constraints fit. + * Purpose-specific outputs omit unused steps only after preserving their + * constraint dependencies and spacing contributions. + * + * @param seedArg Original opaque sRGB seed string. + * @param config Base constraints and foreground policy. + * @param options Direction, seed adjustment, background, and output profile. + * @return Generated colors, direction, and any remaining ramp warnings. + */ export function buildRamp( seedArg: string, config: RampConfig, From 22912e6c4cc24037b029a9d66621acb5289bdf9e Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Thu, 3 Sep 2026 11:46:28 +0200 Subject: [PATCH 16/44] Theme: Defer unused status foreground solves --- packages/theme/CHANGELOG.md | 2 +- .../color-ramps/lib/build-foreground-scale.ts | 83 ++++++----- .../test/build-foreground-scale.test.ts | 135 ++++++++++++++++++ 3 files changed, 183 insertions(+), 37 deletions(-) create mode 100644 packages/theme/src/color-ramps/test/build-foreground-scale.test.ts diff --git a/packages/theme/CHANGELOG.md b/packages/theme/CHANGELOG.md index e02e04af6c9b07..b603870daed6d5 100644 --- a/packages/theme/CHANGELOG.md +++ b/packages/theme/CHANGELOG.md @@ -75,7 +75,7 @@ - Reuse seed-dependent calculations during color-ramp constraint searches. ([#82545](https://github.com/WordPress/gutenberg/pull/82545)) - Generate the default ramps before derived token artifacts so one build uses the current ramp algorithm throughout. ([#82525](https://github.com/WordPress/gutenberg/pull/82525)) - Cache relative luminance calculations used by color-ramp contrast checks. ([#82445](https://github.com/WordPress/gutenberg/pull/82445)) -- Skip unused ramp outputs and redundant base foreground solves while preserving semantic colors and contrast checks. ([#82294](https://github.com/WordPress/gutenberg/pull/82294)) +- Skip unused ramp outputs and redundant base foreground solves, and calculate status interaction colors only when contrast correction needs them. Preserve semantic colors and contrast checks. ([#82294](https://github.com/WordPress/gutenberg/pull/82294)) - Generate the default ramps before derived token artifacts so one build uses the current ramp algorithm throughout. ([#82294](https://github.com/WordPress/gutenberg/pull/82294)) - Enforce NodeNext module resolution in the build project so future declaration imports are checked against the package's published ESM resolution rules. ([#82088](https://github.com/WordPress/gutenberg/pull/82088)) - Update the `@types/node` development dependency to v24, matching the Node.js version the repository builds and tests against ([#82616](https://github.com/WordPress/gutenberg/pull/82616)). diff --git a/packages/theme/src/color-ramps/lib/build-foreground-scale.ts b/packages/theme/src/color-ramps/lib/build-foreground-scale.ts index 1ce8858b581363..6f27ec44ad3d6b 100644 --- a/packages/theme/src/color-ramps/lib/build-foreground-scale.ts +++ b/packages/theme/src/color-ramps/lib/build-foreground-scale.ts @@ -297,20 +297,20 @@ function findColorAtPerceptualContrast( { * @param options Serialization inputs. * @param options.color Foreground before serialization. * @param options.getColorAtLightness Color path indexed by OKLCH lightness. - * @param options.strongColor Strongest allowed fallback. + * @param options.getStrongColor Resolve the strongest allowed fallback. * @param options.referenceLuminances Background luminances to check together. * @param options.target Unpadded WCAG contrast floor. */ function serializeColorMeetingContrast( { color, getColorAtLightness, - strongColor, + getStrongColor, referenceLuminances, target, }: { color: PlainColorObject; getColorAtLightness: GetColorForLightness; - strongColor: PlainColorObject; + getStrongColor: () => PlainColorObject; referenceLuminances: readonly number[]; target: number; } ) { @@ -319,6 +319,7 @@ function serializeColorMeetingContrast( { return serializedColor; } + const strongColor = getStrongColor(); const colorLightness = get( color, [ OKLCH, 'l' ] ); const strongLightness = get( strongColor, [ OKLCH, 'l' ] ); const direction = Math.sign( strongLightness - colorLightness ); @@ -588,46 +589,56 @@ export function buildForegroundScale( target: steps.fgSurface4.contrast.target, } ); } - const normalContrast = getPerceptualContrastMagnitude( - displayBackground, - normalColor - ); - const requestedStrongContrast = Math.min( - maximumStrongContrast, - Math.max( - normalContrast + normalToActiveTarget, - preferredActiveContrast - ) - ); - let strongColor = findColorAtPerceptualContrast( { - background: displayBackground, - getColorAtLightness, - weakColor: normalColor, - strongColor: strongEndpoint, - target: requestedStrongContrast, - } ); - if ( - ! meetsContrastTarget( - strongColor, - referenceLuminances.fgSurface5, - strongStep.contrast.target - ) - ) { - strongColor = findColorAtContrastTarget( { + // Status ramps need the spacing budget above, but only need this color as + // a bound when serialization requires correction. Reuse it across corrections. + let strongColor: PlainColorObject | undefined; + function getStrongColor(): PlainColorObject { + if ( strongColor ) { + return strongColor; + } + + const normalContrast = getPerceptualContrastMagnitude( + displayBackground, + normalColor + ); + const requestedStrongContrast = Math.min( + maximumStrongContrast, + Math.max( + normalContrast + normalToActiveTarget, + preferredActiveContrast + ) + ); + strongColor = findColorAtPerceptualContrast( { + background: displayBackground, getColorAtLightness, - weakColor: strongColor, + weakColor: normalColor, strongColor: strongEndpoint, - referenceLuminances: referenceLuminances.fgSurface5, - target: strongStep.contrast.target, + target: requestedStrongContrast, } ); + if ( + ! meetsContrastTarget( + strongColor, + referenceLuminances.fgSurface5, + strongStep.contrast.target + ) + ) { + strongColor = findColorAtContrastTarget( { + getColorAtLightness, + weakColor: strongColor, + strongColor: strongEndpoint, + referenceLuminances: referenceLuminances.fgSurface5, + target: strongStep.contrast.target, + } ); + } + return strongColor; } colors.set( 'fgSurface3', fgSurface3 ); colors.set( 'fgSurface4', normalColor ); - colors.set( 'fgSurface5', strongColor ); + if ( includeInteractionState ) { + colors.set( 'fgSurface5', getStrongColor() ); + } - // Keep the strong color and its budget above: even status ramps use them - // when positioning and serializing FGS3/FGS4. Only its own output is unused. const outputSteps = includeInteractionState ? config.steps : config.steps.filter( ( step ) => step.name !== 'fgSurface5' ); @@ -643,7 +654,7 @@ export function buildForegroundScale( lightness ) : getColorAtLightness, - strongColor, + getStrongColor, referenceLuminances: referenceLuminances[ step.name ], target: step.contrast.target, } ); diff --git a/packages/theme/src/color-ramps/test/build-foreground-scale.test.ts b/packages/theme/src/color-ramps/test/build-foreground-scale.test.ts new file mode 100644 index 00000000000000..33a8171231caa2 --- /dev/null +++ b/packages/theme/src/color-ramps/test/build-foreground-scale.test.ts @@ -0,0 +1,135 @@ +import { afterEach, describe, expect, it, vi } from 'vitest'; +import { buildAccentRamp, buildBgRamp } from '..'; +import { buildForegroundScale } from '../lib/build-foreground-scale'; +import * as colorUtils from '../lib/color-utils'; +import { ACCENT_RAMP_CONFIG } from '../lib/ramp-configs'; + +function createFixture( backgroundSeed: string, accentSeed = '#0090ff' ) { + const background = buildBgRamp( backgroundSeed ); + const full = buildAccentRamp( accentSeed, background ); + const { fgSurface4, fgSurface5, ...baseColors } = full.ramp; + const scale = ACCENT_RAMP_CONFIG.foregroundScale; + const strongStep = scale.steps.find( + ( step ) => step.name === 'fgSurface5' + )!; + // Reading this target marks the strong-color calculation, without replacing + // any of the color math or exposing the private solver to the test. + const readStrongTarget = vi.fn( () => strongStep.contrast.target ); + const config = { + ...scale, + steps: scale.steps.map( ( step ) => + step === strongStep + ? { + ...step, + contrast: { + ...step.contrast, + get target() { + return readStrongTarget(); + }, + }, + } + : step + ), + }; + return { + background, + base: { ...full, ramp: baseColors }, + config, + readStrongTarget, + expected: { fgSurface4, fgSurface5 }, + }; +} + +describe( 'status foreground calculation', () => { + afterEach( () => { + vi.restoreAllMocks(); + } ); + + it( 'skips the interaction color when retained colors serialize above their contrast floors', () => { + const { background, base, config, readStrongTarget, expected } = + createFixture( '#fcfcfc' ); + + const result = buildForegroundScale( base, background, config, false ); + + expect( readStrongTarget ).not.toHaveBeenCalled(); + expect( result.ramp ).not.toHaveProperty( 'fgSurface5' ); + expect( result.ramp.fgSurface4 ).toBe( expected.fgSurface4 ); + expect( result.warnings ).toBeUndefined(); + } ); + + it( 'defers the interaction color until a rounded foreground needs the existing correction', () => { + const { background, base, config, readStrongTarget } = + createFixture( '#a4a4a4' ); + const serialize = colorUtils.getColorString; + const solvesBeforeCorrection: number[] = []; + vi.spyOn( colorUtils, 'getColorString' ).mockImplementation( + ( color ) => { + const serialized = serialize( color ); + if ( serialized === '#003767' ) { + solvesBeforeCorrection.push( + readStrongTarget.mock.calls.length + ); + } + return serialized; + } + ); + + const result = buildForegroundScale( base, background, config, false ); + + expect( solvesBeforeCorrection[ 0 ] ).toBe( 0 ); + expect( readStrongTarget ).toHaveBeenCalledTimes( 1 ); + // This is the exact pre-optimization correction for this rounding edge. + expect( result.ramp.fgSurface3 ).toBe( '#003667' ); + expect( result.ramp.fgSurface4 ).toBe( '#002b53' ); + expect( result.warnings ).toBeUndefined(); + for ( const ramp of [ background.ramp, result.ramp ] ) { + for ( const surface of [ + 'surface1', + 'surface2', + 'surface3', + ] as const ) { + expect( + colorUtils.getContrast( + ramp[ surface ], + result.ramp.fgSurface3 + ) + ).toBeGreaterThanOrEqual( 4.5 ); + } + } + } ); + + it( 'reuses one interaction-color calculation for multiple serialization corrections', () => { + const { background, base, config, readStrongTarget } = createFixture( + '#dfdfdf', + '#4ab866' + ); + const serialize = colorUtils.getColorString; + const solvesBeforeCorrection = new Map< string, number >(); + vi.spyOn( colorUtils, 'getColorString' ).mockImplementation( + ( color ) => { + const serialized = serialize( color ); + if ( + [ '#236032', '#286b39' ].includes( serialized ) && + ! solvesBeforeCorrection.has( serialized ) + ) { + solvesBeforeCorrection.set( + serialized, + readStrongTarget.mock.calls.length + ); + } + return serialized; + } + ); + + const result = buildForegroundScale( base, background, config, false ); + + expect( [ ...solvesBeforeCorrection ] ).toEqual( [ + [ '#236032', 0 ], + [ '#286b39', 1 ], + ] ); + expect( readStrongTarget ).toHaveBeenCalledTimes( 1 ); + expect( result.ramp.fgSurface3 ).toBe( '#286a39' ); + expect( result.ramp.fgSurface4 ).toBe( '#235f32' ); + expect( result.warnings ).toBeUndefined(); + } ); +} ); From 8af7319a0c40ca0875d656fe5bfce35911032897 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Thu, 3 Sep 2026 12:01:37 +0200 Subject: [PATCH 17/44] Theme: Remove experiment-only benchmark artifacts --- packages/theme/benchmark/README.md | 37 -- packages/theme/benchmark/color-ramps.ts | 377 ------------------ packages/theme/benchmark/step-pruning.md | 178 --------- .../src/color-ramps/test/ramp-purpose.test.ts | 10 - 4 files changed, 602 deletions(-) delete mode 100644 packages/theme/benchmark/README.md delete mode 100644 packages/theme/benchmark/color-ramps.ts delete mode 100644 packages/theme/benchmark/step-pruning.md diff --git a/packages/theme/benchmark/README.md b/packages/theme/benchmark/README.md deleted file mode 100644 index 9d2dfc1e61a21e..00000000000000 --- a/packages/theme/benchmark/README.md +++ /dev/null @@ -1,37 +0,0 @@ -# Color ramp benchmark - -This benchmark calls the color-ramp builders directly, bypassing the -`ThemeProvider` whole-ramp memoization while retaining internal algorithm -caches. It covers one background ramp, one accent ramp against a precomputed -background, and the full `ThemeProvider` ramp workload of one background plus -six accent ramps. Accent calls use the same internal purpose as `ThemeProvider`: -`interactive` for brand/error and `status` for info/success/caution/warning. -Historical algorithms ignore this extra argument. Each case cycles through the -default light seed, a dark `#1e1e1e` background, and the Ectoplasm sample -(`#413256` background and `#a3b745` primary). - -Run it from the repository root: - -```sh -npm run benchmark:color-ramps --workspace @wordpress/theme -``` - -To compare two checkouts on the same machine, run the benchmark script from the -candidate checkout and point it at a prepared baseline worktree: - -```sh -npm run benchmark:color-ramps --workspace @wordpress/theme -- \ - --baseline-root /path/to/gutenberg-trunk \ - --baseline-label trunk \ - --label candidate -``` - -Both worktrees need their dependencies installed. Add `--json` for -machine-readable output. The checksum covers every generated ramp token. It -changes when unused steps are omitted, even if semantic colors are identical; -use semantic parity tests to distinguish pruning from color drift. Compare runs -made with the same Node.js version and similar system load. The benchmark reports measurements but -does not enforce a timing threshold because local and CI timings vary. - -See [the step-pruning report](./step-pruning.md) for the measured before/after -results and the optimizations retained or rejected within this PR. diff --git a/packages/theme/benchmark/color-ramps.ts b/packages/theme/benchmark/color-ramps.ts deleted file mode 100644 index 076bf143bfef76..00000000000000 --- a/packages/theme/benchmark/color-ramps.ts +++ /dev/null @@ -1,377 +0,0 @@ -/* eslint-disable no-console -- This command writes benchmark output. */ -import { spawnSync } from 'node:child_process'; -import { dirname, resolve } from 'node:path'; -import { performance } from 'node:perf_hooks'; -import { parseArgs } from 'node:util'; -import { fileURLToPath, pathToFileURL } from 'node:url'; - -type RampModule = typeof import('../src/color-ramps/index.ts'); -type RampOutput = ReturnType< RampModule[ 'buildAccentRamp' ] >; - -type BenchmarkResult = { - name: string; - iterationsPerSample: number; - medianMs: number; - p95Ms: number; - checksum: number; -}; - -type BenchmarkReport = { - label: string; - environment: { - node: string; - platform: NodeJS.Platform; - arch: string; - }; - warmupSamples: number; - measuredSamples: number; - fixtures: typeof FIXTURES; - results: BenchmarkResult[]; -}; - -type BenchmarkCase = { - name: string; - iterationsPerSample: number; - run: () => RampOutput | RampOutput[]; -}; - -const WARMUP_SAMPLES = 8; -const MEASURED_SAMPLES = 30; -const SCRIPT_PATH = fileURLToPath( import.meta.url ); -const DEFAULT_SOURCE_ROOT = resolve( dirname( SCRIPT_PATH ), '../../..' ); -const FIXTURES = [ - { - name: 'default-light', - background: '#fcfcfc', - primary: '#3858e9', - }, - { - name: 'default-dark', - background: '#1e1e1e', - primary: '#3858e9', - }, - { - name: 'ectoplasm', - background: '#413256', - primary: '#a3b745', - }, -] as const; - -const HELP = `Theme color ramp benchmark - -Usage: - npm run benchmark:color-ramps --workspace @wordpress/theme -- [options] - -Options: - --source-root Checkout to benchmark (default: current checkout) - --label Label for the candidate result - --baseline-root Checkout to benchmark as the baseline - --baseline-label Label for the baseline result - --json Print machine-readable output - --help, -h Show this help - -When --baseline-root is set, each checkout runs in a separate Node.js process -before the script reports the percentage difference.`; - -async function loadRampModule( sourceRoot: string ): Promise< RampModule > { - // Historical revisions relied on the full Color.js entry point to register - // these spaces. Register them here so the same benchmark can execute both - // sides without changing the measured ramp calls. - const { ColorSpace, OKLab, OKLCH, sRGB } = await import( 'colorjs.io/fn' ); - ColorSpace.register( sRGB ); - ColorSpace.register( OKLab ); - ColorSpace.register( OKLCH ); - - const modulePath = resolve( - sourceRoot, - 'packages/theme/src/color-ramps/index.ts' - ); - return import( pathToFileURL( modulePath ).href ) as Promise< RampModule >; -} - -function createCases( rampModule: RampModule ): BenchmarkCase[] { - const { buildAccentRamp, buildBgRamp, DEFAULT_SEED_COLORS } = rampModule; - const accentBackgroundRamps = FIXTURES.map( ( fixture ) => - buildBgRamp( fixture.background ) - ); - let backgroundIndex = 0; - let accentIndex = 0; - let themeIndex = 0; - - return [ - { - name: 'background-ramp', - iterationsPerSample: 20, - run: () => { - const fixture = FIXTURES[ backgroundIndex++ % FIXTURES.length ]; - return buildBgRamp( fixture.background ); - }, - }, - { - name: 'accent-ramp', - iterationsPerSample: 20, - run: () => { - const fixtureIndex = accentIndex++ % FIXTURES.length; - return buildAccentRamp( - FIXTURES[ fixtureIndex ].primary, - accentBackgroundRamps[ fixtureIndex ], - 'interactive' - ); - }, - }, - { - name: 'theme-color-ramps', - iterationsPerSample: 5, - run: () => { - const fixture = FIXTURES[ themeIndex++ % FIXTURES.length ]; - const backgroundRamp = buildBgRamp( fixture.background ); - const accentSeeds = { - primary: fixture.primary, - info: DEFAULT_SEED_COLORS.info, - success: DEFAULT_SEED_COLORS.success, - caution: DEFAULT_SEED_COLORS.caution, - warning: DEFAULT_SEED_COLORS.warning, - error: DEFAULT_SEED_COLORS.error, - }; - const result: RampOutput[] = [ backgroundRamp ]; - - for ( const [ role, seed ] of Object.entries( accentSeeds ) ) { - result.push( - buildAccentRamp( - seed, - backgroundRamp, - role === 'primary' || role === 'error' - ? 'interactive' - : 'status' - ) - ); - } - return result; - }, - }, - ]; -} - -function updateChecksum( checksum: number, output: RampOutput | RampOutput[] ) { - const ramps = Array.isArray( output ) ? output : [ output ]; - for ( const { ramp } of ramps ) { - for ( const color of Object.values( ramp ) ) { - for ( const character of color ) { - checksum = - ( checksum * 31 + ( character.codePointAt( 0 ) ?? 0 ) ) % - 2_147_483_647; - } - } - } - return checksum; -} - -function measure( benchmarkCase: BenchmarkCase ): BenchmarkResult { - let checksum = 0; - const runSample = () => { - let result: RampOutput | RampOutput[] | undefined; - const start = performance.now(); - for ( - let iteration = 0; - iteration < benchmarkCase.iterationsPerSample; - iteration++ - ) { - result = benchmarkCase.run(); - } - const duration = performance.now() - start; - - if ( result ) { - checksum = updateChecksum( checksum, result ); - } - return duration / benchmarkCase.iterationsPerSample; - }; - - for ( let sample = 0; sample < WARMUP_SAMPLES; sample++ ) { - runSample(); - } - - const samples = Array.from( { length: MEASURED_SAMPLES }, runSample ).sort( - ( a, b ) => a - b - ); - const middle = Math.floor( samples.length / 2 ); - const medianMs = - samples.length % 2 === 0 - ? ( samples[ middle - 1 ] + samples[ middle ] ) / 2 - : samples[ middle ]; - - return { - name: benchmarkCase.name, - iterationsPerSample: benchmarkCase.iterationsPerSample, - medianMs, - p95Ms: samples[ Math.ceil( samples.length * 0.95 ) - 1 ], - checksum, - }; -} - -async function runBenchmark( - sourceRoot: string, - label: string -): Promise< BenchmarkReport > { - const rampModule = await loadRampModule( sourceRoot ); - return { - label, - environment: { - node: process.version, - platform: process.platform, - arch: process.arch, - }, - warmupSamples: WARMUP_SAMPLES, - measuredSamples: MEASURED_SAMPLES, - fixtures: FIXTURES, - results: createCases( rampModule ).map( measure ), - }; -} - -function runIsolated( sourceRoot: string, label: string ) { - const result = spawnSync( - process.execPath, - [ - ...process.execArgv, - SCRIPT_PATH, - '--source-root', - sourceRoot, - '--label', - label, - '--json', - ], - { encoding: 'utf8' } - ); - - if ( result.error ) { - throw result.error; - } - if ( result.status !== 0 ) { - throw new Error( - `Benchmark failed for ${ label }:\n${ result.stderr }` - ); - } - return JSON.parse( result.stdout ) as BenchmarkReport; -} - -function compare( baseline: BenchmarkReport, candidate: BenchmarkReport ) { - return candidate.results.map( ( candidateResult ) => { - const baselineResult = baseline.results.find( - ( result ) => result.name === candidateResult.name - ); - if ( ! baselineResult ) { - throw new Error( - `Baseline result is missing ${ candidateResult.name }.` - ); - } - return { - name: candidateResult.name, - baselineMedianMs: baselineResult.medianMs, - candidateMedianMs: candidateResult.medianMs, - medianDeltaPercent: - ( candidateResult.medianMs / baselineResult.medianMs - 1 ) * - 100, - baselineP95Ms: baselineResult.p95Ms, - candidateP95Ms: candidateResult.p95Ms, - }; - } ); -} - -function printContext( report: BenchmarkReport ) { - console.log( - `${ report.environment.node } ${ report.environment.platform }/${ report.environment.arch }; ${ report.measuredSamples } samples after ${ report.warmupSamples } warmups` - ); - console.log( - `fixtures: ${ report.fixtures - .map( ( { name } ) => name ) - .join( ', ' ) }` - ); -} - -function printReport( report: BenchmarkReport ) { - console.log( `Theme color ramp benchmark: ${ report.label }` ); - printContext( report ); - console.table( - report.results.map( ( result ) => ( { - case: result.name, - 'median ms/op': result.medianMs.toFixed( 3 ), - 'p95 ms/op': result.p95Ms.toFixed( 3 ), - } ) ) - ); -} - -function printComparison( - baseline: BenchmarkReport, - candidate: BenchmarkReport, - comparison: ReturnType< typeof compare > -) { - console.log( - `Theme color ramp benchmark: ${ baseline.label } vs ${ candidate.label }` - ); - printContext( candidate ); - console.table( - comparison.map( ( result ) => ( { - case: result.name, - [ `${ baseline.label } median` ]: - result.baselineMedianMs.toFixed( 3 ), - [ `${ candidate.label } median` ]: - result.candidateMedianMs.toFixed( 3 ), - delta: `${ - result.medianDeltaPercent >= 0 ? '+' : '' - }${ result.medianDeltaPercent.toFixed( 1 ) }%`, - [ `${ baseline.label } p95` ]: result.baselineP95Ms.toFixed( 3 ), - [ `${ candidate.label } p95` ]: result.candidateP95Ms.toFixed( 3 ), - } ) ) - ); -} - -async function main() { - const { values } = parseArgs( { - options: { - 'source-root': { type: 'string' }, - label: { type: 'string' }, - 'baseline-root': { type: 'string' }, - 'baseline-label': { type: 'string' }, - json: { type: 'boolean' }, - help: { type: 'boolean', short: 'h' }, - }, - } ); - - if ( values.help ) { - console.log( HELP ); - return; - } - - const sourceRoot = resolve( - values[ 'source-root' ] ?? DEFAULT_SOURCE_ROOT - ); - const label = values.label ?? 'candidate'; - if ( values[ 'baseline-root' ] ) { - const baseline = runIsolated( - resolve( values[ 'baseline-root' ] ), - values[ 'baseline-label' ] ?? 'baseline' - ); - const candidate = runIsolated( sourceRoot, label ); - const comparison = compare( baseline, candidate ); - if ( values.json ) { - console.log( - JSON.stringify( { baseline, candidate, comparison }, null, 2 ) - ); - } else { - printComparison( baseline, candidate, comparison ); - } - return; - } - - const report = await runBenchmark( sourceRoot, label ); - if ( values.json ) { - console.log( JSON.stringify( report, null, 2 ) ); - } else { - printReport( report ); - } -} - -main().catch( ( error ) => { - console.error( error instanceof Error ? error.message : error ); - process.exitCode = 1; -} ); -/* eslint-enable no-console */ diff --git a/packages/theme/benchmark/step-pruning.md b/packages/theme/benchmark/step-pruning.md deleted file mode 100644 index c01502cd8456ca..00000000000000 --- a/packages/theme/benchmark/step-pruning.md +++ /dev/null @@ -1,178 +0,0 @@ -# Ramp step pruning - -## Goal and boundaries - -Reduce theme-generation work without changing any semantic color, its contrast -requirements, or the public ThemeProvider API. Keep the existing local -`bgFill2.followDirection: 'main'` change. - -## Plan - -1. Remove FGS1, `bgFillDark`, and `fgFillDark` from generation. No semantic token - uses them, and no retained color depends on them. Update internal diagnostics - and generated fixtures while preserving the public warning step-name type. -2. Add an internal accent-purpose string union, `full`, `interactive`, or - `status`, if the measured saving justifies it. Brand and error use - `interactive`; info, success, caution, and warning use `status`. Storybook - and full-ramp diagnostics retain `full`. Do not add a ThemeProvider prop. -3. Probe skipping accent ST2 reconstruction, then its base calculation if - removing that constraint preserves all retained outputs. -4. Probe skipping status ST4 repair and FGS5 final serialization/interval - correction. Keep foreground spacing and the strong color used as a bound - when serializing retained foregrounds. Do not replace required anchors with - approximate colors or weaken semantic contrast checks. -5. Consider skipping SF6's final reconstruction, but retain its authored - spacing contribution to SF4/SF5. Keep this separate from the first probes. -6. Keep only changes that preserve output and earn their complexity in repeated - benchmarks. Record discarded probes as well as retained changes. - -## Verification - -The author approved this regression coverage: - -- Omit unused steps. -- Preserve exact semantic colors for each ramp purpose. -- Preserve the existing contrast checks. -- Keep cached results distinct between ramp purposes. - -Compare against a frozen copy of the current working tree, including all 13 -Storybook samples, a 216-color background cube, and the same primary-color cube -against light, dark, and middle-gray backgrounds. Check all mapped values and -warning payloads. Run focused unit tests, formatting/lint, type checks, and -token generation. Public generated CSS/JS token values must not change. - -Benchmark before/after in separate, sequential Node processes. Report the -generation workload separately from cache hits and browser rendering. Use -semantic parity checks rather than expecting a raw all-step checksum to match -after internal steps have been removed. - -## Result - -Implemented locally on `codex/theme-feasibility-aware-perceptual-ramps`. -This comparison is **before versus after step pruning within this PR**, not -trunk versus this PR. The baseline is commit -`4bb53ec0db8705cb1cfbfc6df52434ad0f0e488b` plus the already-applied local -`bgFill2.followDirection: 'main'` change. Both sides use that direction change. - -### Work removed - -| Work | Decision | Reason | -| ------------------------------------------------- | ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| FGS1, `bgFillDark`, `fgFillDark` | Remove entirely | No semantic consumers or retained dependents. | -| Base FGS4/FGS5 solves | Remove | These intermediate colors were overwritten. Their base 4.5:1 constraint against SF3 was already covered by FGS3. The final foreground pass still constructs FGS4/FGS5 and checks their full surface constraints. | -| Accent ST2 final reconstruction | Skip for runtime accents | No accent semantic token uses ST2. Keep its base solve. | -| Accent SF6 final color conversion | Skip for runtime accents | Keep the base SF6 color and perceptual spacing calculation that position SF4/SF5. Only its final output conversion is unused. | -| Status ST4 final repair | Skip for status ramps | No status semantic token uses ST4. Its base constraint remains important. | -| Status FGS5 serialization and interval correction | Skip for status ramps | No status interaction-state token uses FGS5. Keep the strong foreground candidate and spacing budget that constrain FGS3/FGS4. | -| Accent ST2 base solve | Retain | Output parity passed, but the measured incremental saving was about 0.1%, insufficient to justify another conditional base configuration. | -| Status ST4 base solve | Retain | Removing it changed semantic results in 521 of 877 combinations. Its contrast constraint affects seed adjustment. | - -The enum-style option is an internal string union, not a new ThemeProvider prop: - -| Purpose | Caller | Returned steps per ramp | -| ---------------- | -------------------------------------------------------------- | ----------------------: | -| `full` (default) | Background, token generation, Storybook, full-ramp diagnostics | 20 | -| `interactive` | Primary and error runtime ramps | 18 | -| `status` | Info, success, caution, and warning runtime ramps | 16 | - -The runtime returns 120 color fields across seven ramps, down from 161. -This is not a count of independent solves: retained fields and hidden anchors -still require different amounts of work. Public semantic tokens are not removed, -including tokens with no current literal consumer in this repository. -FGS2–FGS5 keep their existing names; they are not renumbered after FGS1 removal. - -### Final production-path benchmark - -Each cell below is the median of five process-level results. The p95 columns -are medians of the five process-level p95 values, not pooled percentiles. - -| Workload | Before median | After median | Change | Before p95 | After p95 | -| ------------------------------------------ | ------------: | -----------: | -----: | ---------: | --------: | -| Background ramp | 0.482 ms | 0.421 ms | −12.6% | 0.528 ms | 0.477 ms | -| Interactive accent, precomputed background | 1.758 ms | 1.613 ms | −8.3% | 1.914 ms | 1.763 ms | -| Complete theme: background + six accents | 12.165 ms | 10.831 ms | −11.0% | 13.800 ms | 11.505 ms | - -The five paired full-theme medians were: - -| Run | Before | After | Change | -| --- | --------: | --------: | -----: | -| 1 | 12.082 ms | 10.899 ms | −9.8% | -| 2 | 12.151 ms | 10.773 ms | −11.3% | -| 3 | 12.203 ms | 10.741 ms | −12.0% | -| 4 | 12.165 ms | 10.831 ms | −11.0% | -| 5 | 12.195 ms | 10.905 ms | −10.6% | - -Environment: Node.js v20.20.2, macOS arm64. Each process used eight warmup -samples and 30 measured samples, with 20 ramp calls or five complete themes -per sample. Processes ran sequentially, alternating before/after order between -pairs, without concurrent builds or probes. Fixtures were default light, -default dark, and Ectoplasm (`#413256` / `#a3b745`) on both sides. - -These measurements bypass ThemeProvider's whole-ramp memoization and retain -internal color-algorithm caches. They do not measure browser rendering, a -cold page load, or the usual memoized cache-hit path. Raw all-step checksums -are stable within each version but differ between versions because fields -were removed and insertion order changed. Semantic equality was checked -separately. - -The checked-in benchmark now passes the same purpose arguments as the runtime. -Use it with a prepared baseline checkout, then repeat with the process order -reversed: - -```sh -npm run benchmark:color-ramps --workspace @wordpress/theme -- \ - --baseline-root /path/to/baseline \ - --baseline-label before-pruning \ - --label after-pruning -``` - -### Exploratory probes, not additive savings - -These earlier measurements used a diagnostic driver, before the final SF6 -skip. Each row reports the median of five process medians. The driver rebuilt -temporary configuration objects between calls, so use the production-path -table above for the final result. Differences between these rows are useful -for deciding which probes to pursue, not precise isolated cost estimates. - -| Cumulative probe | Full-theme median | Change versus its baseline | -| --------------------------------------------------- | ----------------: | -------------------------: | -| Baseline | 12.341 ms | — | -| Remove the three globally unused steps | 12.011 ms | −2.7% | -| Also skip accent ST2 and status ST4/FGS5 final work | 11.591 ms | −6.1% | -| Also omit accent ST2's base solve | 11.580 ms | −6.2% | -| Also omit base FGS4/FGS5 solves | 11.173 ms | −9.5% | - -The last two rows were probes, not the exact final implementation. The final -code keeps accent ST2's base solve, removes redundant base FGS4/FGS5 solves, -and skips accent SF6's final conversion. SF6's saving was not measured in -isolation. The status ST4 base-deletion probe was rejected on output parity -before using its timing to make a decision. - -Local raw results and frozen source snapshots are in -`/private/tmp/theme-step-pruning.20QyaP/`. The results file is -`benchmark-results.json`; these scratch files are not part of the PR. - -### Verification completed - -- 877 theme combinations / 6,139 ramps: all 13 Storybook samples, a 216-color - background cube, and a 216-color primary cube on light, dark, and middle-gray - backgrounds. Every retained color, semantic value, direction, and warning - payload matched the frozen baseline. No checked contrast failures or warnings. -- Both full-ramp snapshots match the baseline exactly after deleting only - FGS1, `bgFillDark`, and `fgFillDark` fields (1,377 removed snapshot lines). -- 174 theme Node/Vitest tests and 35 ThemeProvider/JSDOM tests pass. New tests - cover omission, semantic parity by purpose, foreground/stroke contrast, - redundant base foreground removal, and cache isolation between purposes. -- Theme type checks, changed-file JavaScript lint, and theme token generation - pass. No full-repository build or browser performance test was run here. -- Public prebuilt CSS/JS, token types, fallback SCSS, and token documentation - are unchanged. The generated internal alias map changes key order only; - its mappings are identical. The public warning step-name union is preserved. - -### Further scope - -Do not add a per-step bitmask or caller-supplied skip list. The three purposes -encode known semantic usage, and the parity tests will catch a future mapping -that needs an omitted step. More aggressive removal of status fills or -foreground anchors would need a separate dependency/constraint analysis; -being unmapped is not sufficient evidence that their calculations are unused. diff --git a/packages/theme/src/color-ramps/test/ramp-purpose.test.ts b/packages/theme/src/color-ramps/test/ramp-purpose.test.ts index e305e2162ffd6c..23221f326d1064 100644 --- a/packages/theme/src/color-ramps/test/ramp-purpose.test.ts +++ b/packages/theme/src/color-ramps/test/ramp-purpose.test.ts @@ -2,7 +2,6 @@ import { describe, expect, it } from 'vitest'; import { buildAccentRamp, buildBgRamp, DEFAULT_SEED_COLORS } from '..'; import { getContrast } from '../lib/color-utils'; import type { Ramp } from '../lib/types'; -import { ACCENT_RAMP_CONFIG, BG_RAMP_CONFIG } from '../lib/ramp-configs'; import colorTokens from '../../prebuilt/ts/color-tokens'; const combinations = [ @@ -15,15 +14,6 @@ const combinations = [ ] as const; describe( 'ramp purpose', () => { - it( 'derives normal and interaction-state foregrounds without redundant base solves', () => { - for ( const config of [ BG_RAMP_CONFIG, ACCENT_RAMP_CONFIG ] ) { - expect( config.steps ).not.toHaveProperty( 'fgSurface4' ); - expect( config.steps ).not.toHaveProperty( 'fgSurface5' ); - } - const { ramp } = buildBgRamp( '#fcfcfc' ); - expect( ramp.fgSurface4 ).toBe( '#505050' ); - expect( ramp.fgSurface5 ).toBe( '#141414' ); - } ); it( 'omits steps with no semantic consumer or downstream dependency', () => { const background = buildBgRamp( '#fcfcfc' ); const accent = buildAccentRamp( '#3858e9', background ); From 77f3a8d25d36fef0edd09564ca2f7b4db06355d7 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Thu, 3 Sep 2026 12:22:01 +0200 Subject: [PATCH 18/44] Theme: Document the color ramp pipeline --- packages/theme/README.md | 20 +++--- .../theme/bin/generate-default-ramps/index.ts | 2 + .../bin/generate-primitive-tokens/index.ts | 18 ++---- packages/theme/src/color-ramps/README.md | 48 ++++++++++++++ packages/theme/src/color-ramps/index.ts | 29 +++++---- .../color-ramps/lib/build-foreground-scale.ts | 4 +- .../theme/src/color-ramps/lib/constants.ts | 25 +++----- .../src/color-ramps/lib/default-ramps.ts | 2 + .../lib/find-color-with-constraints.ts | 48 ++++++-------- packages/theme/src/color-ramps/lib/index.ts | 51 ++++++--------- .../theme/src/color-ramps/lib/ramp-configs.ts | 15 ++--- packages/theme/src/color-ramps/lib/types.ts | 43 ++++++------- packages/theme/src/color-ramps/lib/utils.ts | 64 ++++++++++--------- .../src/semantic-color-contrast-pairs.ts | 4 +- .../src/theme-provider-color-warnings.ts | 7 ++ packages/theme/tokens/README.md | 8 +-- 16 files changed, 203 insertions(+), 185 deletions(-) create mode 100644 packages/theme/src/color-ramps/README.md diff --git a/packages/theme/README.md b/packages/theme/README.md index 1cf67d9e6294d6..e0e5bd38cede03 100644 --- a/packages/theme/README.md +++ b/packages/theme/README.md @@ -304,20 +304,16 @@ The public token surface is the semantic `--wpds-*` custom properties documented ### Building -This package is built in two steps. When `npm run build` is run at the root of the repo, it will first run the "prebuild" step of this package, which is defined in the `build` script of this package's package.json. +For the color algorithm's goals, configuration, and build stages, see [Building color ramps](./src/color-ramps/README.md). For token source files and aliases, see the [Design Tokens Maintainer's Guide](./tokens/README.md). -This step will: +Run `npm run build --workspace @wordpress/theme` from the repository root to regenerate: -1. Generate default color ramps. -2. Generate primitive tokens. -3. Build CSS and JavaScript token files. -4. Update the design tokens documentation. -5. Generate complete Figma border collection mode files in `prebuilt/figma`. -6. Format all generated files. +1. Default color ramps. +2. Primitive colors and derived CSS, JavaScript, and TypeScript token files. +3. Token fallbacks and the design tokens reference. +4. Complete Figma border collection mode files in `prebuilt/figma`. -The files generated in this step will all be committed to the repo. - -After the prebuild step, the package will be built into its final form via the repo's standard package build script. +Commit the generated changes. The root `npm run build` runs this generation step before compiling the packages. ## Stylelint Plugins @@ -352,7 +348,7 @@ Reports manual fallbacks that can drift from the generated values. ## Build Plugins -The build plugins inject generated fallbacks into bare `var(--wpds-*)` references so components still render when the design tokens stylesheet is unavailable. For example, `var(--wpds-color-foreground-content-neutral)` becomes `var(--wpds-color-foreground-content-neutral, #1e1e1e)`. +The build plugins inject generated fallbacks into bare `var(--wpds-*)` references so components still render when the design tokens stylesheet is unavailable. For example, `var(--wpds-color-foreground-content-neutral)` becomes `var(--wpds-color-foreground-content-neutral, #505050)` with the default theme. `@wordpress/build` already applies these plugins automatically when `@wordpress/theme` is installed. You only need to configure them manually for custom build setups. diff --git a/packages/theme/bin/generate-default-ramps/index.ts b/packages/theme/bin/generate-default-ramps/index.ts index 88a6d5366f97f6..84ec6b0022914e 100644 --- a/packages/theme/bin/generate-default-ramps/index.ts +++ b/packages/theme/bin/generate-default-ramps/index.ts @@ -27,6 +27,8 @@ const outputPath = join( ); const content = ` +// Generated by bin/generate-default-ramps/index.ts. Do not edit directly. +// Regenerate from the repository root: npm run build --workspace @wordpress/theme import type { RampResult } from './types.ts'; import type { DEFAULT_SEED_COLORS } from './constants.ts'; diff --git a/packages/theme/bin/generate-primitive-tokens/index.ts b/packages/theme/bin/generate-primitive-tokens/index.ts index 021d709c896f1c..336dc150eae635 100644 --- a/packages/theme/bin/generate-primitive-tokens/index.ts +++ b/packages/theme/bin/generate-primitive-tokens/index.ts @@ -12,13 +12,10 @@ import { getColorString } from '../../src/color-ramps/lib/color-utils.ts'; const __filename = fileURLToPath( import.meta.url ); const __dirname = path.dirname( __filename ); -// Path to the color.json file const colorJsonPath = path.join( __dirname, '../../tokens/color.json' ); -// 3 decimal places is the minimum precision for lossless hex serialization. -// With 3 decimal places rounding to the nearest 0.001, the maximum rounding -// error is 0.0005. With 256 possible hex values, 0.0005 × 256 = 0.128, -// guaranteeing the rounded value stays within 0.5 of the original value. +// Three decimals preserve 8-bit sRGB channels on round-trip to hex: +// the maximum rounding error is 0.0005 × 255, below half a channel step. const HEX_ROUNDING_PRECISION = 3; const transformColorStringToDTCGValue = ( color: string ) => { @@ -33,18 +30,16 @@ const transformColorStringToDTCGValue = ( color: string ) => { }; }; -// Main function +// Replace primitive colors only; semantic aliases remain hand-authored. function generatePrimitiveColorTokens() { const startTime = performance.now(); console.log( '🎨 Starting primitive color tokens generation...' ); try { - // Read the color.json file const colorJson = JSON.parse( fs.readFileSync( colorJsonPath, 'utf8' ) ); - // Build the ramps const bgRamp = buildBgRamp( DEFAULT_SEED_COLORS.background ); const accentRamps = [ ...Object.entries( DEFAULT_SEED_COLORS ) ] .filter( ( [ scaleName ] ) => scaleName !== 'background' ) @@ -53,10 +48,8 @@ function generatePrimitiveColorTokens() { ramp: buildAccentRamp( seed, bgRamp ), } ) ); - // Convert the ramp values in a DTCG compatible format. - // Note: the background seed maps to the `bg` primitive ramp group, - // whose name is kept abbreviated even though the semantic tokens it - // feeds are exposed under the spelled-out `background` group. + // The background seed uses the abbreviated `bg` primitive group. + // Public semantic tokens use the spelled-out `background` group. [ { scaleName: 'bg', @@ -75,7 +68,6 @@ function generatePrimitiveColorTokens() { } } ); - // Write the updated JSON back to the file with proper formatting fs.writeFileSync( colorJsonPath, JSON.stringify( colorJson, null, '\t' ) diff --git a/packages/theme/src/color-ramps/README.md b/packages/theme/src/color-ramps/README.md new file mode 100644 index 00000000000000..12f17e4de9edb4 --- /dev/null +++ b/packages/theme/src/color-ramps/README.md @@ -0,0 +1,48 @@ +# Building color ramps + +For the public API, see the [package README](../../README.md). This guide covers the algorithm behind the semantic tokens. + +## Goals + +A ramp contains related surface, stroke, fill, and foreground colors. The goals are balanced-looking steps across light and dark themes, retained seed character, and a stronger foreground for interaction states. + +Perceptual spacing is a preference; configured WCAG contrast floors take priority. A seed is a starting point, not a promised output color. Its lightness may shift to make the base constraints fit. Searches are bounded, so difficult seeds can still produce compressed steps or contrast warnings. + +## Configuration + +[`lib/ramp-configs.ts`](./lib/ramp-configs.ts) defines background and accent ramps. Backgrounds start from `surface2`; accents start from `bgFill1`. + +| Setting | Purpose | +| ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | +| `steps` | Base colors: contrast references, WCAG ratios, direction, optional lightness preferences, and tapering of chroma, or color intensity. | +| `sameAsIfPossible` | Reuse a base color only if it passes the step's contrast checks. | +| `foregroundScale` | Foreground seed, background reference, chroma policy, and WCAG floors. | +| `foregroundScale.perceptualTargets` | Preferred foreground contrast and gaps, measured with APCA in Lc. These are not accessibility thresholds. | + +`main` follows the ramp's lighter or darker direction; `opposite` reverses it. `best` chooses using contrast against black and white. A base target of `1` copies its reference. Base lightness preferences apply only when contrast permits; the accent surface anchor uses an explicit lightness pin. + +## Build flow + +1. **Choose the background direction.** [`buildBgRamp`](./index.ts) starts with the black or white endpoint offering more WCAG contrast. Near the crossover, it can compare both directions, accepting a switch only within the seed-drift and contrast checks. +2. **Solve base colors.** [`buildRamp`](./lib/index.ts) builds dependencies first and searches OKLCH lightness for each contrast target. Chroma tapering preserves the authored desaturation choices. If a required target fails, it searches for an adjusted seed lightness and rebuilds the base ramp. +3. **Rebalance surfaces and strokes.** [`buildPerceptualSteps`](./lib/build-perceptual-steps.ts) owns these spacing rules. Surfaces use OKLr lightness, retaining each step's hue and chroma before fitting it into sRGB. SF1–SF3 run from darker to lighter around SF2. SF4–SF6 extend beyond them in the ramp's main direction. Strokes use OKLab to measure color differences, with WCAG checks and ST1 as an anchor. +4. **Place foregrounds.** [`buildForegroundScale`](./lib/build-foreground-scale.ts) uses APCA, which treats light-on-dark and dark-on-light contrast differently. It compares foregrounds to the background ramp's reference. Neutral foregrounds retain tapering; accents preserve the seed's share of available chroma. WCAG floors apply to configured surfaces in both ramps. If the preferred gaps cannot fit, FGS4 moves toward its WCAG floor to leave range for FGS5. Rounded hex output is checked and corrected where possible. +5. **Build accents, then map tokens.** Accents run the same passes, inheriting the completed background's direction and a bounded version of its SF2 lightness. [`use-theme-provider-styles.ts`](../use-theme-provider-styles.ts) maps the results to semantic CSS properties using the generated alias map. + +Surface foregrounds use FGS2 for disabled controls, FGS3 for weak emphasis, FGS4 for normal content and resting controls, and FGS5 for interaction states. FGS5 can approach black or white at the expense of chroma. FGS1 is no longer generated. Strong fills use separate foregrounds. See [`tokens/color.json`](../../tokens/color.json) for exact mappings. + +Runtime accent profiles skip output work that semantic tokens do not use: `interactive` for primary/error and `status` for the other accents. They retain dependencies and spacing budgets that affect visible colors. Generation and diagnostics use `full` ramps. + +## Checks and generated files + +Ramp warnings report unmet step constraints. [`theme-provider-color-warnings.ts`](../theme-provider-color-warnings.ts) also checks listed semantic text/background pairs. The separate `checkAccessibleCombinations` diagnostic checks a broader matrix, including cross-ramp pairs that semantic tokens may not use. Neither warning collector repairs colors or proves every possible pairing accessible. + +After changing configuration or the algorithm, run this from the repository root: + +```sh +npm run build --workspace @wordpress/theme +``` + +This regenerates default ramps first, then primitive colors in `tokens/color.json`, CSS, alias maps, fallbacks, and token documentation. Commit the generated changes; do not edit them directly. Semantic aliases in `tokens/color.json` remain hand-authored. See the [token maintainer guide](../../tokens/README.md) for that layer. + +Check the color-ramp tests and Storybook's **Theme Provider → Color Scales → Sample Combinations**. Inspect ordering, seed character, foreground state differences, and warnings across light, dark, mid-gray, and saturated seeds. diff --git a/packages/theme/src/color-ramps/index.ts b/packages/theme/src/color-ramps/index.ts index c2c284dd62ec2d..67d45d019268e9 100644 --- a/packages/theme/src/color-ramps/index.ts +++ b/packages/theme/src/color-ramps/index.ts @@ -29,7 +29,7 @@ ColorSpace.register( OKLab ); ColorSpace.register( OKLCH ); // Only solve both polarities near the black/white WCAG crossover. This keeps -// the extra work and possible direction change away from decisive seeds. +// light and dark seeds keep their initial direction without a second solve. const MAXIMUM_AMBIGUOUS_ENDPOINT_CONTRAST_DIFFERENCE = 1; // Allow the alternate direction only when its semantic anchor remains nearly // as faithful to the supplied seed as the initially preferred direction. @@ -116,8 +116,10 @@ function selectBackgroundRampPolarity( } /** - * Creates a background ramp. - * @param seed The seed color for the background ramp. + * Build the neutral ramp, including seed adjustment and a bounded comparison + * of foreground directions near the black/white contrast crossover. + * + * @param seed Opaque sRGB seed for the background. */ export function buildBgRamp( seed: string ) { if ( typeof seed !== 'string' || seed.trim() === '' ) { @@ -153,10 +155,11 @@ function getBgRampInfo( ramp: InternalRampResult ): { } /** - * Creates an accent ramp (ie used by primary, success, info, warning and error - * ramps). - * @param seed The seed color for the accent ramp. - * @param bgRamp The ramp of the background on which the accent is shown. + * Build an accent ramp. When a background is supplied, inherit its direction + * and pin accent SF2 to a bounded version of the background's SF2 lightness. + * + * @param seed Opaque sRGB seed for the accent. + * @param bgRamp Completed background ramp on which the accent appears. * @param purpose Internal profile selected by the caller's semantic usage. */ export function buildAccentRamp( @@ -186,11 +189,13 @@ export function buildAccentRamp( } /** - * Checks that all bg/fg combinations generated by the ramps meet contrast - * targets. - * @param params - * @param params.bgRamp - * @param params.accentRamps + * Report WCAG failures in the full-ramp diagnostic matrix, within each ramp + * and for accent foregrounds against background-ramp colors. Includes pairs + * that semantic tokens may not use. This check does not repair colors. + * + * @param params Full ramps to inspect. + * @param params.bgRamp Background ramp. + * @param params.accentRamps Accent ramps; purpose-pruned outputs are not supported. */ export function checkAccessibleCombinations( { bgRamp, diff --git a/packages/theme/src/color-ramps/lib/build-foreground-scale.ts b/packages/theme/src/color-ramps/lib/build-foreground-scale.ts index 6f27ec44ad3d6b..621a09753dd1ee 100644 --- a/packages/theme/src/color-ramps/lib/build-foreground-scale.ts +++ b/packages/theme/src/color-ramps/lib/build-foreground-scale.ts @@ -423,8 +423,8 @@ function serializeColorMeetingPerceptualInterval( { /** * Rebuild the foreground part of an otherwise complete ramp. APCA positions - * the intermediate steps within the visible contrast range, while WCAG ratios - * remain hard floors across the surfaces where each step is used. + * the intermediate steps within the available contrast range. WCAG floors + * take priority across the configured surfaces; unresolved failures warn. * * @param ramp Ramp whose foreground steps will be rebuilt. * @param backgroundRamp Background ramp on which the colors will appear. diff --git a/packages/theme/src/color-ramps/lib/constants.ts b/packages/theme/src/color-ramps/lib/constants.ts index 5020d94622250c..689017dcb9f542 100644 --- a/packages/theme/src/color-ramps/lib/constants.ts +++ b/packages/theme/src/color-ramps/lib/constants.ts @@ -12,34 +12,27 @@ export const BLACK: PlainColorObject = { alpha: 1, }; -// Margin added to target contrasts to counter for algorithm approximations and rounding errors. -// - the `CONTRAST_EPSILON` value is 0.004, so the real contrast can be lower by this amount. -// - the max contrast between adjacent RGB values is 1.016, so 0.016 is the maximum total rounding error between two values. -// - the sum is 0.02: the margin we add to ensure that the target contrast is met after all the rounding. +// Empirical WCAG ratio margin for search and rounding error. Final serialized +// colors still need contrast checks; this margin alone is not a guarantee. export const UNIVERSAL_CONTRAST_TOPUP = 0.02; -// When enabling "lighter direction" bias, this is the amount by which -// black text contrast needs to be greater than white text contrast. -// The higher the value, the stronger the preference for white text. -// The current value has been determined empirically as the highest value -// that won't cause the algo not to be able to correctly solve all contrasts. +// With preferLighter, choose black only when its minimum contrast exceeds +// white's by this amount. This changes the choice, not the required contrast. export const WHITE_TEXT_CONTRAST_MARGIN = 3.1; -// These values are used as thresholds when trying to match the background -// ramp's lightness while calculating an accent ramp. They prevent the accent -// scale from being pinned to lightness values in the middle of the range, -// which would cause the algorithm to struggle to satisfy the accent scale -// constraints and therefore produce unexpected results. +// Keep the accent SF2 anchor out of the middle-lightness range, where there +// is little contrast room for either foreground direction. export const ACCENT_SCALE_BASE_LIGHTNESS_THRESHOLDS = { lighter: { min: 0.2, max: 0.4 }, darker: { min: 0.75, max: 0.98 }, } as const; -// Contrast precision we aim for. Approximately 1/256, resolution of an 8-bit number. +// Signed search-error tolerance. Its units depend on the caller's metric. export const CONTRAST_EPSILON = 4e-3; export const MAX_BISECTION_ITERATIONS = 10; +// Full-ramp diagnostic pairs, broader than the semantic token pair checks. export const CONTRAST_COMBINATIONS: { bgs: ( keyof Ramp )[]; fgs: ( keyof Ramp )[]; @@ -72,7 +65,7 @@ export const CONTRAST_COMBINATIONS: { }, ]; -// Used when generating the DTCG tokens and the static color ramps. +// Shared defaults for token generation and runtime ThemeProvider calculations. export const DEFAULT_SEED_COLORS = { background: '#fcfcfc', primary: '#3858e9', diff --git a/packages/theme/src/color-ramps/lib/default-ramps.ts b/packages/theme/src/color-ramps/lib/default-ramps.ts index a1be287ff87018..18002c86b56554 100644 --- a/packages/theme/src/color-ramps/lib/default-ramps.ts +++ b/packages/theme/src/color-ramps/lib/default-ramps.ts @@ -1,3 +1,5 @@ +// Generated by bin/generate-default-ramps/index.ts. Do not edit directly. +// Regenerate from the repository root: npm run build --workspace @wordpress/theme import type { RampResult } from './types.ts'; import type { DEFAULT_SEED_COLORS } from './constants.ts'; diff --git a/packages/theme/src/color-ramps/lib/find-color-with-constraints.ts b/packages/theme/src/color-ramps/lib/find-color-with-constraints.ts index ca41f637e6749b..29b3af7705c994 100644 --- a/packages/theme/src/color-ramps/lib/find-color-with-constraints.ts +++ b/packages/theme/src/color-ramps/lib/find-color-with-constraints.ts @@ -5,30 +5,29 @@ import { clampToGamut, getContrast } from './color-utils.ts'; import { createChromaTaper, type TaperChromaOptions } from './taper-chroma.ts'; /** - * Difference of contrast values that grows linearly with the Y luminance. - * We get more precise linear interpolations when we use this. - * @param c1 First contrast. - * @param c2 Second contrast. - * @return Difference of logarithms. + * Signed log contrast ratio used as search error. Zero meets the target. + * + * @param c1 Achieved contrast. + * @param c2 Target contrast. */ function cdiff( c1: number, c2: number ) { return Math.log( c1 / c2 ); } /** - * Solve for L such that: - * - the L applied to the seed meets the contrast target against the reference - * - the search is performed in one direction (ie lighter / darker) - * - more constraints can be applied around lightness - * @param reference - * @param seed - * @param target - * @param direction - * @param options - * @param options.lightnessConstraint - * @param options.lightnessConstraint.type - * @param options.lightnessConstraint.value - * @param options.taperChromaOptions + * Search OKLCH lightness toward black or white for a WCAG target, using the + * seed's hue and optional chroma taper. If the endpoint cannot reach the target, + * return it with a deficit. Results use search tolerance, not final hex checks. + * + * @param reference Color to contrast against. + * @param seed OKLCH seed supplying hue and chroma. + * @param target WCAG contrast ratio, already padded by the caller. + * @param direction Endpoint to search toward. + * @param options Optional lightness and chroma settings. + * @param options.lightnessConstraint Exact lightness to try before searching. + * @param options.lightnessConstraint.type Whether to force lightness even if contrast fails. + * @param options.lightnessConstraint.value OKLCH lightness to try. + * @param options.taperChromaOptions Chroma taper; omitted to retain seed chroma before gamut mapping. */ export function findColorMeetingRequirements( reference: PlainColorObject, @@ -51,8 +50,7 @@ export function findColorMeetingRequirements( achieved: number; deficit?: number; } { - // A target of 1 means same color. - // A target lower than 1 doesn't make sense. + // A target of 1 means reuse, not a lightness search. if ( target <= 1 ) { return { color: reference, @@ -77,7 +75,6 @@ export function findColorMeetingRequirements( newL = tapered.l; newC = tapered.c; } else { - // It's already a ColorObject, return it directly return tapered; } } @@ -89,15 +86,11 @@ export function findColorMeetingRequirements( } ); } - // Set the boundary based on the direction. const mostContrastingL = direction === 'lighter' ? 1 : 0; const mostContrastingColor = direction === 'lighter' ? WHITE : BLACK; const highestContrast = getContrast( reference, mostContrastingColor ); if ( lightnessConstraint ) { - // Apply a specific L value. - // Useful when pinning a step to a specific lightness, of to specify - // min/max L values. const colorWithExactL = getColorForL( lightnessConstraint.value ); const exactLContrast = getContrast( reference, colorWithExactL ); const exactLContrastMeetsTarget = @@ -131,10 +124,7 @@ export function findColorMeetingRequirements( }; } - // Bracket: low fails, high meets. - // Originally this was seed.oklch.l — although it's an assumption that works - // only when we know for sure the direction of the search. - // TODO: can we bring this back to seed.oklch.l ? + // Search from the reference lightness toward the selected endpoint. const lowerL = get( reference, [ OKLCH, 'l' ] ); const lowerContrast = cdiff( 1, target ); const upperL = mostContrastingL; diff --git a/packages/theme/src/color-ramps/lib/index.ts b/packages/theme/src/color-ramps/lib/index.ts index 5c7f5c0bb8584b..4cd0ce57416ecb 100644 --- a/packages/theme/src/color-ramps/lib/index.ts +++ b/packages/theme/src/color-ramps/lib/index.ts @@ -30,18 +30,18 @@ import type { import { BLACK, WHITE, CONTRAST_EPSILON } from './constants.ts'; /** - * Calculate a complete color ramp based on the provided configuration. + * Solve the base steps and track the largest constraint deficit for seed + * adjustment. Perceptual reconstruction happens after this pass. * - * @param params - The calculation parameters - * @param params.seed - The base color to build the ramp from - * @param params.sortedSteps - Steps sorted in dependency order - * @param params.config - Ramp configuration defining contrast requirements - * @param params.mainDir - Primary direction for the ramp (lighter/darker) - * @param params.oppDir - Opposite direction from mainDir - * @param params.pinLightness - Optional lightness override for a given step - * @param params.pinLightness.stepName - * @param params.pinLightness.value - * @return Object containing ramp results and satisfaction status + * @param params Base-pass inputs. + * @param params.seed OKLCH seed, possibly adjusted by a prior search. + * @param params.sortedSteps Steps to build, with dependencies first. + * @param params.config Base contrast and chroma settings. + * @param params.mainDir Ramp's foreground direction. + * @param params.oppDir Opposite of mainDir. + * @param params.pinLightness Lightness override, even if contrast fails. + * @param params.pinLightness.stepName Step to pin. + * @param params.pinLightness.value Pinned OKLCH lightness. */ function calculateRamp( { seed, @@ -67,8 +67,7 @@ function calculateRamp( { let maxDeficitDirection: RampDirection = 'lighter'; let maxDeficitStep; - // Keep track of the calculated colors, as they are going to be useful - // when other colors reference them. + // Dependencies use unrounded colors; only the output ramp is serialized. const calculatedColors = new Map< keyof Ramp | 'seed', PlainColorObject >(); calculatedColors.set( 'seed', seed ); @@ -94,7 +93,6 @@ function calculateRamp( { return referenceColor; } ); - // Check if we can reuse color from the `sameAsIfPossible` config option if ( sameAsIfPossible ) { const candidateColor = calculatedColors.get( sameAsIfPossible ); if ( ! candidateColor ) { @@ -109,13 +107,11 @@ function calculateRamp( { getContrast( referenceColor, candidateColor ) >= adjustedTarget ); - // If the candidate meets every contrast requirement, use it. if ( candidateMeetsTarget ) { - // Store the reused color calculatedColors.set( stepName, candidateColor ); rampResults[ stepName ] = getColorString( candidateColor ); - continue; // Skip to next step + continue; } } @@ -154,7 +150,6 @@ function calculateRamp( { const adjustedTarget = adjustContrastTarget( contrast.target ); - // Define the lightness constraint, if needed. let lightnessConstraint; if ( pinLightness?.stepName === stepName ) { lightnessConstraint = { @@ -168,7 +163,6 @@ function calculateRamp( { } as const; } - // Calculate the color meeting the requirements const searchResults = findColorMeetingRequirements( referenceColor, seed, @@ -180,8 +174,8 @@ function calculateRamp( { } ); - // When the target contrast is not met, take note of it and use - // that information to guide the ramp calculation bisection. + // The tightest constraint guides seed adjustment; negative deficits + // describe unused contrast room. if ( ! contrast.ignoreWhenAdjustingSeed && searchResults.deficit && @@ -192,10 +186,8 @@ function calculateRamp( { maxDeficitStep = stepName; } - // Store calculated color for future dependencies calculatedColors.set( stepName, searchResults.color ); - // Add to results rampResults[ stepName ] = getColorString( searchResults.color ); if ( ! searchResults.reached && ! contrast.ignoreWhenAdjustingSeed ) { @@ -286,10 +278,8 @@ export function buildRamp( oppDir = worse; } - // Get the correct calculation order based on dependencies const sortedSteps = sortByDependency( config.steps ); - // Calculate the ramp with the initial seed. const { rampResults, warnings, @@ -325,10 +315,8 @@ export function buildRamp( pinLightness, } ); - // If the constraints start failing in the opposite direction to the original - // iteration's direction, that means we've moved too far away from the target. - // Don't use the `maxDeficit` value because it's not related to our search, - // and might even be positive, which would confuse the bisection algorithm. + // Opposing failures mean the search has gone too far. Reverse the + // error sign instead of treating that unrelated deficit as progress. return iterationResults.maxDeficitDirection === maxDeficitDirection ? iterationResults.maxDeficit : -maxDeficit; @@ -363,9 +351,8 @@ export function buildRamp( bestWarnings = finalResult.warnings; } - // Swap surface1 and surface3 for darker ramps to maintain visual elevation hierarchy. - // This ensures surface1 appears "behind" surface2, and surface3 appears "in front", - // regardless of the ramp's main direction. + // Elevation always runs from darker SF1 to lighter SF3, independent of the + // foreground direction used to solve the base constraints. if ( mainDir === 'darker' ) { const tmpSurface1 = bestRamp.surface1; bestRamp.surface1 = bestRamp.surface3; diff --git a/packages/theme/src/color-ramps/lib/ramp-configs.ts b/packages/theme/src/color-ramps/lib/ramp-configs.ts index 9b6e4f97316cdf..1425608fd06497 100644 --- a/packages/theme/src/color-ramps/lib/ramp-configs.ts +++ b/packages/theme/src/color-ramps/lib/ramp-configs.ts @@ -39,8 +39,7 @@ const ACCENT_SURFACE_TAPER_CHROMA: TaperChromaOptions = { radiusLight: 0.01, }; -// Keep the existing inverted active-fill behavior independent from the new -// foreground endpoint policy. +// Inverted fills retain their own high-contrast policy, separate from FGS5. const highContrastInvertedFillConfig: RampStepConfig = { contrast: { reference: 'surface3', @@ -53,10 +52,8 @@ const highContrastInvertedFillConfig: RampStepConfig = { }; const FOREGROUND_PERCEPTUAL_TARGETS = { - // These are design targets, not accessibility thresholds. WCAG ratios remain - // the hard gates for every surface on which a foreground can appear. - // Prefer strong normal text, but lower it when that is necessary to preserve - // a visible active state before the gamut endpoint. + // APCA design targets, not accessibility thresholds. Lower normal text when + // needed to leave a visible interaction state; keep the configured WCAG floors. normalContrast: 86, // Let the interaction-state endpoint approach black or white when that makes // the state change more visible. WCAG remains a hard floor. @@ -272,8 +269,8 @@ export const BG_RAMP_CONFIG: RampConfig = { }, }; -// BG_RAMP: seed => surface2 => {bgFill, surface3 => all other tokens} -// ACCENT_RAMP: seed => bgFill1 => surface2 => surface3 => all other tokens +// Unlike the background ramp's SF2 anchor, accents start from the strong fill. +// The subsequent perceptual passes are shared; see ../README.md for the flow. export const ACCENT_RAMP_CONFIG: RampConfig = { steps: { ...BG_RAMP_CONFIG.steps, @@ -328,7 +325,7 @@ export const ACCENT_RAMP_CONFIG: RampConfig = { ...BG_RAMP_CONFIG.steps.stroke4, taperChromaOptions: undefined, }, - // fgSurface: do not de-saturate + // Preserve chroma in the base foreground anchors. fgSurface2: { ...BG_RAMP_CONFIG.steps.fgSurface2, taperChromaOptions: undefined, diff --git a/packages/theme/src/color-ramps/lib/types.ts b/packages/theme/src/color-ramps/lib/types.ts index 2118cc0385a093..c8c0667794dc1e 100644 --- a/packages/theme/src/color-ramps/lib/types.ts +++ b/packages/theme/src/color-ramps/lib/types.ts @@ -1,7 +1,7 @@ import type { TaperChromaOptions } from './taper-chroma.ts'; export type Ramp = { - // Backgrounds for surfaces (nuanced, slight variations compared to bg) + // Surface backgrounds: elevation (1–3) and emphasis (4–6). surface1: string; surface2: string; surface3: string; @@ -18,7 +18,7 @@ export type Ramp = { bgFill2: string; bgFillInverted1: string; bgFillInverted2: string; - // Foreground (text, icon) colors + // Surface foregrounds: disabled (2), weak (3), normal (4), interaction (5). fgSurface2: string; fgSurface3: string; fgSurface4: string; @@ -37,7 +37,7 @@ export type BaseRampStep = keyof BaseRamp; export type AccentRampPurpose = 'full' | 'interactive' | 'status'; export type FollowDirection = 'main' | 'opposite' | 'best' | RampDirection; export type ContrastRequirement = { - /** The reference color against which to calculate the contrast */ + /** Color against which to calculate the WCAG contrast ratio. */ reference: BaseRampStep | 'seed'; /** * Other colors against which the generated color must meet the same target. @@ -46,39 +46,32 @@ export type ContrastRequirement = { */ additionalReferences?: readonly ( BaseRampStep | 'seed' )[]; /** - * Which direction should the algorithm search a matching color in: - * - main: follow the same direction as the ramp's main direction - * - opposite: follow the opposite direction of the ramp - * - best: pick the direction that has the most contrast headroom - * - hardcoded ramp direction (useful for generating colors that always - * light/dark regardless of the ramp direction) + * Search direction: follow the ramp (`main`), reverse it (`opposite`), + * choose by endpoint contrast (`best`), or always use `lighter` / `darker`. */ followDirection: FollowDirection; /** - * Prefer "lighter" direction when searching for a contrasting color. - * Especially useful for foreground color to counter the poor results that the - * WCAG algo gives when contrasting white text over mid-lightness backgrounds. + * Bias `best` toward white text. This affects direction selection, not the + * required contrast ratio, and does not force the lighter direction. */ preferLighter?: boolean; /** - * The contrast target to meet. + * Required WCAG contrast ratio. A target of 1 copies the reference color. */ target: number; /** - * When true, the algorithm won't count a failure in meeting the contrast - * target as a reason to recalculate the ramp. + * Exclude this target from seed adjustment and base-pass warnings. */ ignoreWhenAdjustingSeed?: boolean; }; export type RampStepConfig = { contrast: ContrastRequirement; + /** Preferred OKLCH lightness, used only when it meets the contrast target. */ lightness?: ( direction: RampDirection ) => number; taperChromaOptions?: TaperChromaOptions; /** - * If specified, try to reuse the color from this step if it meets - * the contrast requirements. This reduces the number of unique colors - * in the ramp and improves consistency. + * Reuse this base step's color if it meets all contrast requirements. */ sameAsIfPossible?: BaseRampStep; }; @@ -94,16 +87,17 @@ export type ForegroundRampStep = export type ForegroundScaleConfig = { /** Ramp step whose hue and chroma define the foreground scale. */ seed: BaseRampStep; - /** Background step used to measure the APCA contrast range. */ + /** Step in the background ramp used to measure the APCA contrast range. */ perceptualReference: BaseRampStep; + /** Preferred APCA magnitudes and gaps in Lc, not accessibility thresholds. */ perceptualTargets: { /** Preferred APCA contrast for normal content and resting controls. */ normalContrast: number; - /** APCA contrast left unused unless the foreground intervals need it. */ + /** Preferred gap below black/white; spent if the foreground gaps need it. */ endpointReserve: number; - /** Minimum APCA interval from weak to normal emphasis. */ + /** Preferred gap from weak to normal emphasis; compresses if necessary. */ weakToNormal: number; - /** Minimum APCA interval from a resting control to its active state. */ + /** Preferred gap from resting to interaction-state foreground. */ normalToActive: number; }; chroma: @@ -116,7 +110,9 @@ export type ForegroundScaleConfig = { }; steps: readonly ( { contrast: { + /** Check these steps in both the current and background ramps. */ references: readonly BaseRampStep[]; + /** Required WCAG contrast ratio, including after hex serialization. */ target: number; }; } & ( @@ -130,12 +126,15 @@ export type ForegroundScaleConfig = { }; export type RampConfig = { + /** Dependency-based WCAG solve, before perceptual reconstruction. */ steps: RampStepsConfig; + /** Foreground placement after surfaces and strokes have been rebuilt. */ foregroundScale: ForegroundScaleConfig; }; export type RampResult< Colors = Ramp > = { ramp: Colors; + /** Steps still failing their checked constraints; not all possible pairs. */ warnings?: ( keyof Ramp )[]; direction: RampDirection; }; diff --git a/packages/theme/src/color-ramps/lib/utils.ts b/packages/theme/src/color-ramps/lib/utils.ts index 342d4b80d3ac09..52cec626447d67 100644 --- a/packages/theme/src/color-ramps/lib/utils.ts +++ b/packages/theme/src/color-ramps/lib/utils.ts @@ -12,8 +12,9 @@ import type { BaseRampStep, RampStepsConfig, RampDirection } from './types.ts'; import { getContrast } from './color-utils.ts'; /** - * Build a dependency graph from the steps configuration - * @param config - The steps configuration object + * Track contrast and reuse dependencies between base steps. + * + * @param config Base step configuration. */ function buildDependencyGraph( config: RampStepsConfig ): { dependencies: Map< BaseRampStep, ( BaseRampStep | 'seed' )[] >; @@ -22,7 +23,6 @@ function buildDependencyGraph( config: RampStepsConfig ): { const dependencies = new Map< BaseRampStep, ( BaseRampStep | 'seed' )[] >(); const dependents = new Map< BaseRampStep | 'seed', BaseRampStep[] >(); - // Initialize maps Object.keys( config ).forEach( ( step ) => { dependencies.set( step as BaseRampStep, [] ); } ); @@ -31,7 +31,6 @@ function buildDependencyGraph( config: RampStepsConfig ): { dependents.set( step as BaseRampStep, [] ); } ); - // Build the graph Object.entries( config ).forEach( ( [ stepName, stepConfig ] ) => { const step = stepName as BaseRampStep; const references = [ @@ -44,7 +43,6 @@ function buildDependencyGraph( config: RampStepsConfig ): { dependents.get( reference )!.push( step ); } - // Add dependency for sameAsIfPossible if ( stepConfig.sameAsIfPossible ) { dependencies.get( step )!.push( stepConfig.sameAsIfPossible ); dependents.get( stepConfig.sameAsIfPossible )!.push( step ); @@ -55,8 +53,9 @@ function buildDependencyGraph( config: RampStepsConfig ): { } /** - * Topologically sort steps based on their dependencies - * @param config - The steps configuration object + * Order base steps so their references are built first. Reject cycles. + * + * @param config Base step configuration. */ export function sortByDependency( config: RampStepsConfig ): BaseRampStep[] { const { dependents } = buildDependencyGraph( config ); @@ -99,10 +98,10 @@ export function sortByDependency( config: RampStepsConfig ): BaseRampStep[] { return result; } /** - * Return minimal set of steps that are needed to calculate `stepName` from the seed. + * Return the requested step and its dependencies, in build order. + * * @param stepName Name of the step. * @param config Configuration of the ramp. - * @return Array of steps that `stepName` depends on. */ export function stepsForStep( stepName: BaseRampStep, @@ -132,12 +131,11 @@ export function stepsForStep( } /** - * Finds out whether a lighter or a darker foreground color achieves a better - * contrast against every reference color - * @param references - * @param preferLighter Whether the check should favor white foreground color - * @return An object with "better" and "worse" properties, each holding a - * ramp direction value. + * Choose black or white by its weakest WCAG contrast across the references. + * This selects a direction; it does not prove a contrast target is reachable. + * + * @param references Colors the foreground must contrast against. + * @param preferLighter Bias the comparison toward white. */ export function computeBetterFgColorDirection( references: string | PlainColorObject | readonly PlainColorObject[], @@ -167,22 +165,24 @@ export function computeBetterFgColorDirection( : { better: 'lighter', worse: 'darker' }; } +/** + * Pad a WCAG target for rounding, except 1, which means reuse the reference. + * + * @param target Unpadded WCAG ratio. + */ export function adjustContrastTarget( target: number ) { if ( target === 1 ) { return 1; } - // Add a little top up to take into account any rounding error and algo imprecisions. return target + UNIVERSAL_CONTRAST_TOPUP; } /** - * Prevent the accent scale from referencing a lightness value that - * would prevent the algorithm from complying with the requirements - * and cause it to generate unexpected results. - * @param rawLightness - * @param direction - * @return The clamped lightness value + * Bound the accent surface anchor to leave room for contrasting foregrounds. + * + * @param rawLightness Background SF2 lightness in OKLCH. + * @param direction Ramp's foreground direction. */ export function clampAccentScaleReferenceLightness( rawLightness: number, @@ -193,15 +193,17 @@ export function clampAccentScaleReferenceLightness( } /** - * Find the value of `L` (luminance) that produces a `C` (color) that has a - * `value` (contrast delta) equal to zero. - * @param calculateC Calculate `C` from a given `L`. - * @param calculateValue Calculate value (delta) for a given `C`. - * @param initLowerL Initial lower value of `L`. - * @param initLowerValue Initial lower delta (negative). - * @param initUpperL Initial upper value of `L`. - * @param initUpperValue Initial upper delta (positive). - * @return Resulting value of type `C`. + * Search for zero signed error between two bounds. "Lower" and "upper" refer + * to error signs, not numeric lightness order. Stops at the tolerance or + * iteration limit; callers must check any strict output requirements. + * + * @param calculateC Build a candidate from the search parameter (usually lightness). + * @param calculateValue Signed error for a candidate. + * @param initLowerL Parameter at the negative-error bound. + * @param initLowerValue Initial negative error. + * @param initUpperL Parameter at the positive-error bound. + * @param initUpperValue Initial positive error. + * @return Last sampled candidate. */ export function solveWithBisect< C >( calculateC: ( l: number ) => C, diff --git a/packages/theme/src/semantic-color-contrast-pairs.ts b/packages/theme/src/semantic-color-contrast-pairs.ts index 3dcc6f818c5ee7..4a064a213459ad 100644 --- a/packages/theme/src/semantic-color-contrast-pairs.ts +++ b/packages/theme/src/semantic-color-contrast-pairs.ts @@ -1,11 +1,11 @@ +export const MINIMUM_TEXT_CONTRAST = 4.5; + /** * Semantic color token paths used together for normal text. * * Paths omit the shared `wpds-color` prefix so build configuration and runtime * tests can adapt the same pairs to DTCG token IDs and CSS custom properties. */ -export const MINIMUM_TEXT_CONTRAST = 4.5; - export const SEMANTIC_COLOR_CONTRAST_PAIRS = [ { background: 'background.interactive.neutral', diff --git a/packages/theme/src/theme-provider-color-warnings.ts b/packages/theme/src/theme-provider-color-warnings.ts index 962a5f93ade01b..2b69c9195fa045 100644 --- a/packages/theme/src/theme-provider-color-warnings.ts +++ b/packages/theme/src/theme-provider-color-warnings.ts @@ -34,6 +34,13 @@ export type ThemeProviderColorWarning = achievedContrast: number; }; +/** + * Combine ramp warnings with WCAG checks for listed semantic text/background + * pairs. Missing pairs are skipped; this does not test every token combination. + * + * @param ramps Generated ramps and their remaining step warnings. + * @param colorValues Semantic CSS custom properties and serialized colors. + */ export function collectThemeProviderColorWarnings( ramps: ReadonlyMap< ThemeProviderColorRampName, diff --git a/packages/theme/tokens/README.md b/packages/theme/tokens/README.md index 2edd814c99eddb..d2d861f311ba7a 100644 --- a/packages/theme/tokens/README.md +++ b/packages/theme/tokens/README.md @@ -25,11 +25,9 @@ Semantic tokens follow a consistent naming pattern that encodes the token's purp ## Primitive and Semantic Tokens -**Primitive tokens** are internal, reusable, raw values emitted by the design system's theming. They are not part of the public API, but instead are referenced by semantic tokens, which use the underlying values and provide purpose and meaning to how those values are used. +**Primitive tokens** are internal raw values, such as a color or spacing size. They are not part of the public API. -**Semantic tokens** are the public API of the design system's tokens. They map to primitive token values, but the values are incidental, and a consumer is expected to choose a semantic token based on their specific use-case. The design system provides semantic tokens to cover a breadth of use-cases that are standardized at a design systems level. - -This structure is meant to **shift the emphasis away from the values themselves and toward the meaning and purpose that the tokens represent**. Ultimately, the tokens still map to raw values that affect how a component is styled and those values should be internally consistent, but the primitive layer is an incidental concern of the theming internals and not a consideration of the end-user of the design system. +**Semantic tokens** are the public names that reference those values and describe their intended use. Choose a token by its purpose, not by the value it happens to have in the default theme. Example: @@ -64,7 +62,7 @@ In the example above, the CSS properties generated from these tokens would inclu --wpds-dimension-size-lg: 40px; ``` -Someone using the design system should never see or concern themselves with either the `primitive.space.80` token or the underlying 4 pixel base unit, and instead focus on the semantics of how element size tokens apply to their component. In this example, a large token being used for a component that follows the size of buttons and inputs in the system. +Consumers use `--wpds-dimension-size-lg` for elements that follow the large button and input size, without depending on the internal `primitive.space.80` name or its value. ## Custom Extensions From 019dc00ed23c7be34630609f17967360b2e801d8 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Thu, 3 Sep 2026 12:44:42 +0200 Subject: [PATCH 19/44] Theme: Register color spaces only when needed --- packages/theme/CHANGELOG.md | 1 + packages/theme/src/color-ramps/index.ts | 7 +- .../color-ramps/lib/build-foreground-scale.ts | 10 +- .../color-ramps/lib/build-perceptual-steps.ts | 6 +- .../src/color-ramps/stories/ramp-table.tsx | 6 +- .../test/color-space-registration.test.ts | 96 +++++++++++++++++++ 6 files changed, 106 insertions(+), 20 deletions(-) create mode 100644 packages/theme/src/color-ramps/test/color-space-registration.test.ts diff --git a/packages/theme/CHANGELOG.md b/packages/theme/CHANGELOG.md index b603870daed6d5..16068a0a5fd369 100644 --- a/packages/theme/CHANGELOG.md +++ b/packages/theme/CHANGELOG.md @@ -75,6 +75,7 @@ - Reuse seed-dependent calculations during color-ramp constraint searches. ([#82545](https://github.com/WordPress/gutenberg/pull/82545)) - Generate the default ramps before derived token artifacts so one build uses the current ramp algorithm throughout. ([#82525](https://github.com/WordPress/gutenberg/pull/82525)) - Cache relative luminance calculations used by color-ramp contrast checks. ([#82445](https://github.com/WordPress/gutenberg/pull/82445)) +- Register color spaces only when calculating colors, avoiding module-import side effects. ([#82294](https://github.com/WordPress/gutenberg/pull/82294)) - Skip unused ramp outputs and redundant base foreground solves, and calculate status interaction colors only when contrast correction needs them. Preserve semantic colors and contrast checks. ([#82294](https://github.com/WordPress/gutenberg/pull/82294)) - Generate the default ramps before derived token artifacts so one build uses the current ramp algorithm throughout. ([#82294](https://github.com/WordPress/gutenberg/pull/82294)) - Enforce NodeNext module resolution in the build project so future declaration imports are checked against the package's published ESM resolution rules. ([#82088](https://github.com/WordPress/gutenberg/pull/82088)) diff --git a/packages/theme/src/color-ramps/index.ts b/packages/theme/src/color-ramps/index.ts index 67d45d019268e9..8c116b887a2f19 100644 --- a/packages/theme/src/color-ramps/index.ts +++ b/packages/theme/src/color-ramps/index.ts @@ -3,7 +3,6 @@ import { contrastAPCA, deltaEOK2, get, - OKLab, OKLCH, sRGB, } from 'colorjs.io/fn'; @@ -24,10 +23,6 @@ import { getContrast } from './lib/color-utils.ts'; import { BLACK, CONTRAST_COMBINATIONS, WHITE } from './lib/constants.ts'; export { DEFAULT_SEED_COLORS } from './lib/constants.ts'; -ColorSpace.register( sRGB ); -ColorSpace.register( OKLab ); -ColorSpace.register( OKLCH ); - // Only solve both polarities near the black/white WCAG crossover. This keeps // light and dark seeds keep their initial direction without a second solve. const MAXIMUM_AMBIGUOUS_ENDPOINT_CONTRAST_DIFFERENCE = 1; @@ -39,6 +34,7 @@ const MAXIMUM_ALTERNATE_POLARITY_DELTA_E_DRIFT = 0.02; const MINIMUM_ALTERNATE_POLARITY_APCA_IMPROVEMENT = 1; function getInteractionStateInterval( ramp: InternalRampResult ) { + ColorSpace.register( sRGB ); const background = ramp.ramp[ BG_RAMP_CONFIG.foregroundScale.perceptualReference ]; return ( @@ -141,6 +137,7 @@ function getBgRampInfo( ramp: InternalRampResult ): { value: number; }; } { + ColorSpace.register( sRGB ); return { mainDirection: ramp.direction, backgroundRamp: ramp, diff --git a/packages/theme/src/color-ramps/lib/build-foreground-scale.ts b/packages/theme/src/color-ramps/lib/build-foreground-scale.ts index 621a09753dd1ee..394d398f9399dc 100644 --- a/packages/theme/src/color-ramps/lib/build-foreground-scale.ts +++ b/packages/theme/src/color-ramps/lib/build-foreground-scale.ts @@ -2,7 +2,6 @@ import { ColorSpace, contrastAPCA, get, - OKLab, OKLCH, OKLCH_sRGB as OklchSrgb, sRGB, @@ -28,11 +27,6 @@ import type { } from './types.ts'; import { solveWithBisect } from './utils.ts'; -ColorSpace.register( sRGB ); -ColorSpace.register( OKLab ); -ColorSpace.register( OKLCH ); -ColorSpace.register( OklchSrgb ); - type GetColorForLightness = ( lightness: number ) => PlainColorObject; const PERCEPTUAL_INTERVAL_SERIALIZATION_TOPUP = 0.25; @@ -85,6 +79,8 @@ function createColorForLightness( }; } + // Relative-chroma gamut searches resolve "oklch.c" by name internally. + ColorSpace.register( OKLCH ); const relativeSeed = to( seed, OklchSrgb ); const relativeChroma = get( relativeSeed, [ OklchSrgb, 'c' ] ); const hue = get( relativeSeed, [ OklchSrgb, 'h' ] ); @@ -449,6 +445,8 @@ export function buildForegroundScale( config: ForegroundScaleConfig, includeInteractionState = true ): AccentRampResult { + // APCA resolves sRGB by name, even for color objects. + ColorSpace.register( sRGB ); const seed = clampToGamut( ramp.ramp[ config.seed ] ); const getColorAtLightness = createColorForLightness( seed, config ); // Parse and convert this once. APCA evaluates it for every intermediate diff --git a/packages/theme/src/color-ramps/lib/build-perceptual-steps.ts b/packages/theme/src/color-ramps/lib/build-perceptual-steps.ts index 3c00224ab04383..868c30b44aff92 100644 --- a/packages/theme/src/color-ramps/lib/build-perceptual-steps.ts +++ b/packages/theme/src/color-ramps/lib/build-perceptual-steps.ts @@ -15,11 +15,6 @@ import { UNIVERSAL_CONTRAST_TOPUP } from './constants.ts'; import type { AccentRampPurpose, BaseRamp, BaseRampResult } from './types.ts'; import { solveWithBisect } from './utils.ts'; -ColorSpace.register( sRGB ); -ColorSpace.register( OKLab ); -ColorSpace.register( OKLCH ); -ColorSpace.register( OKLrab ); - type GetColorAtLightness = ( lightness: number ) => PlainColorObject; const MINIMUM_CROSS_LANE_LIGHTNESS_GAP = 0.005; @@ -487,6 +482,7 @@ export function buildPerceptualSteps( backgroundRamp?: BaseRampResult, purpose: AccentRampPurpose = 'full' ): BaseRampResult { + ColorSpace.register( sRGB ); const surfaceResult = { ...ramp, ramp: rebuildSurfaces( ramp, purpose ), diff --git a/packages/theme/src/color-ramps/stories/ramp-table.tsx b/packages/theme/src/color-ramps/stories/ramp-table.tsx index b1adb9c74ba617..896b356b30fba8 100644 --- a/packages/theme/src/color-ramps/stories/ramp-table.tsx +++ b/packages/theme/src/color-ramps/stories/ramp-table.tsx @@ -1,5 +1,5 @@ import { forwardRef } from '@wordpress/element'; -import { ColorSpace, deltaEOK2, OKLab, sRGB, toGamut } from 'colorjs.io/fn'; +import { ColorSpace, deltaEOK2, sRGB, toGamut } from 'colorjs.io/fn'; import type { ThemeProviderColorRampName, ThemeProviderColorWarning, @@ -8,9 +8,6 @@ import colorTokenAliases from '../../prebuilt/ts/color-tokens'; import { getContrast } from '../lib/color-utils'; import type { Ramp } from '../lib/types'; -ColorSpace.register( sRGB ); -ColorSpace.register( OKLab ); - const MEANINGFUL_SEED_DELTA_E = 0.002; // TODO: show token groups better @@ -116,6 +113,7 @@ export function hasColorWarningForRamp( } function isSeedAdjusted( seed: string, generatedAnchor: string ) { + ColorSpace.register( sRGB ); return deltaEOK2( seed, generatedAnchor ) > MEANINGFUL_SEED_DELTA_E; } diff --git a/packages/theme/src/color-ramps/test/color-space-registration.test.ts b/packages/theme/src/color-ramps/test/color-space-registration.test.ts new file mode 100644 index 00000000000000..fba7a191bde17c --- /dev/null +++ b/packages/theme/src/color-ramps/test/color-space-registration.test.ts @@ -0,0 +1,96 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; +import { ColorSpace } from 'colorjs.io/fn'; + +describe( 'color-space registration', () => { + const originalRegistry = ColorSpace.registry; + + beforeEach( () => { + vi.resetModules(); + ColorSpace.registry = {}; + } ); + + afterEach( () => { + ColorSpace.registry = originalRegistry; + } ); + + it( 'imports the ramp builders without registering color spaces', async () => { + await import( '..' ); + expect( Object.keys( ColorSpace.registry ) ).toEqual( [] ); + } ); + + it.each( [ + 'assertValidSeedColor', + 'clampToGamut', + 'getColorString', + 'getContrast', + 'getRelativeLuminance', + ] as const )( '%s works without prior registration', async ( name ) => { + const utils = await import( '../lib/color-utils' ); + // Do not let an import-time registration satisfy the utility's needs. + ColorSpace.registry = {}; + + switch ( name ) { + case 'assertValidSeedColor': + expect( () => + utils.assertValidSeedColor( '#3858e9' ) + ).not.toThrow(); + break; + case 'clampToGamut': + expect( utils.clampToGamut( '#3858e9' ).space.id ).toBe( + 'oklch' + ); + break; + case 'getColorString': + expect( utils.getColorString( '#3858e9' ) ).toBe( '#3858e9' ); + break; + case 'getContrast': + expect( utils.getContrast( '#000000', '#ffffff' ) ).toBeCloseTo( + 21 + ); + break; + case 'getRelativeLuminance': + expect( utils.getRelativeLuminance( '#ffffff' ) ).toBeCloseTo( + 1 + ); + break; + } + } ); + + it( 'builds a background ramp without prior registration', async () => { + const { buildBgRamp } = await import( '..' ); + ColorSpace.registry = {}; + + const result = buildBgRamp( '#fcfcfc' ); + + expect( result.ramp.surface2 ).toBe( '#fcfcfc' ); + expect( result.warnings ).toBeUndefined(); + } ); + + it( 'rebuilds surfaces and strokes without prior registration', async () => { + const { buildBgRamp } = await import( '..' ); + const { buildPerceptualSteps } = await import( + '../lib/build-perceptual-steps' + ); + const background = buildBgRamp( '#fcfcfc' ); + ColorSpace.registry = {}; + + const result = buildPerceptualSteps( background ); + + expect( result.ramp.surface2 ).toBe( background.ramp.surface2 ); + expect( result.warnings ).toBeUndefined(); + } ); + + it.each( [ 'full', 'interactive', 'status' ] as const )( + 'builds %s accent ramps from a supplied background without prior registration', + async ( purpose ) => { + const { buildBgRamp, buildAccentRamp } = await import( '..' ); + const background = buildBgRamp( '#fcfcfc' ); + ColorSpace.registry = {}; + + const result = buildAccentRamp( '#3858e9', background, purpose ); + + expect( result.ramp.fgSurface4 ).toMatch( /^#[\da-f]{6}$/ ); + expect( result.warnings ).toBeUndefined(); + } + ); +} ); From cce8f26a865940454cb365c6c73c157d7ef486bd Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Fri, 4 Sep 2026 14:15:05 +0200 Subject: [PATCH 20/44] Theme: Extract contrast caching --- .../color-ramps/lib/build-foreground-scale.ts | 118 +++++++----------- .../theme/src/color-ramps/lib/color-utils.ts | 9 +- .../test/color-space-registration.test.ts | 6 - 3 files changed, 49 insertions(+), 84 deletions(-) diff --git a/packages/theme/src/color-ramps/lib/build-foreground-scale.ts b/packages/theme/src/color-ramps/lib/build-foreground-scale.ts index 394d398f9399dc..a425251cce7fc5 100644 --- a/packages/theme/src/color-ramps/lib/build-foreground-scale.ts +++ b/packages/theme/src/color-ramps/lib/build-foreground-scale.ts @@ -8,12 +8,7 @@ import { to, type PlainColorObject, } from 'colorjs.io/fn'; -import { - clampToGamut, - getColorString, - getContrastFromLuminances, - getRelativeLuminance, -} from './color-utils.ts'; +import { clampToGamut, getColorString, getContrast } from './color-utils.ts'; import { UNIVERSAL_CONTRAST_TOPUP } from './constants.ts'; import { taperChroma } from './taper-chroma.ts'; import type { @@ -112,7 +107,7 @@ function getPerceptualContrastMagnitude( return Math.abs( contrastAPCA( background, foreground ) ); } -function getConstraintReferenceLuminances( +function getConstraintReferences( step: ForegroundScaleConfig[ 'steps' ][ number ], ramp: BaseRampResult, backgroundRamp: BaseRampResult @@ -123,20 +118,17 @@ function getConstraintReferenceLuminances( ...step.contrast.references.map( ( name ) => backgroundRamp.ramp[ name ] ), - ] ), - ( reference ) => getRelativeLuminance( reference ) + ] ) ); } function meetsContrastFloor( color: string | PlainColorObject, - referenceLuminances: readonly number[], + references: readonly string[], target: number ) { - const luminance = getRelativeLuminance( color ); - return referenceLuminances.every( - ( referenceLuminance ) => - getContrastFromLuminances( referenceLuminance, luminance ) >= target + return references.every( + ( reference ) => getContrast( reference, color ) >= target ); } @@ -145,36 +137,29 @@ function meetsContrastFloor( * Zero marks the search boundary; negative values mean at least one reference * fails. Final output checks use the unpadded floor instead. * - * @param color Candidate foreground. - * @param referenceLuminances Precomputed luminances of its backgrounds. - * @param target Unpadded WCAG contrast ratio. + * @param color Candidate foreground. + * @param references Background colors to check together. + * @param target Unpadded WCAG contrast ratio. */ function getContrastTargetMargin( color: PlainColorObject, - referenceLuminances: readonly number[], + references: readonly string[], target: number ) { const adjustedTarget = target + UNIVERSAL_CONTRAST_TOPUP; - const luminance = getRelativeLuminance( color ); - let minimumMargin = Infinity; - for ( const referenceLuminance of referenceLuminances ) { - minimumMargin = Math.min( - minimumMargin, - Math.log( - getContrastFromLuminances( referenceLuminance, luminance ) / - adjustedTarget - ) - ); - } - return minimumMargin; + return Math.min( + ...references.map( ( reference ) => + Math.log( getContrast( reference, color ) / adjustedTarget ) + ) + ); } function meetsContrastTarget( color: PlainColorObject, - referenceLuminances: readonly number[], + references: readonly string[], target: number ) { - return getContrastTargetMargin( color, referenceLuminances, target ) >= 0; + return getContrastTargetMargin( color, references, target ) >= 0; } /** @@ -186,34 +171,30 @@ function meetsContrastTarget( * @param options.getColorAtLightness Color path indexed by OKLCH lightness. * @param options.weakColor Starting foreground. * @param options.strongColor Strongest allowed foreground. - * @param options.referenceLuminances Background luminances to check together. + * @param options.references Background colors to check together. * @param options.target Unpadded WCAG contrast ratio. */ function findColorAtContrastTarget( { getColorAtLightness, weakColor, strongColor, - referenceLuminances, + references, target, }: { getColorAtLightness: GetColorForLightness; weakColor: PlainColorObject; strongColor: PlainColorObject; - referenceLuminances: readonly number[]; + references: readonly string[]; target: number; } ) { - const weakMargin = getContrastTargetMargin( - weakColor, - referenceLuminances, - target - ); + const weakMargin = getContrastTargetMargin( weakColor, references, target ); if ( weakMargin >= 0 ) { return weakColor; } const strongMargin = getContrastTargetMargin( strongColor, - referenceLuminances, + references, target ); if ( strongMargin < 0 ) { @@ -222,8 +203,7 @@ function findColorAtContrastTarget( { return solveWithBisect( getColorAtLightness, - ( color ) => - getContrastTargetMargin( color, referenceLuminances, target ), + ( color ) => getContrastTargetMargin( color, references, target ), get( weakColor, [ OKLCH, 'l' ] ), weakMargin, get( strongColor, [ OKLCH, 'l' ] ), @@ -294,24 +274,24 @@ function findColorAtPerceptualContrast( { * @param options.color Foreground before serialization. * @param options.getColorAtLightness Color path indexed by OKLCH lightness. * @param options.getStrongColor Resolve the strongest allowed fallback. - * @param options.referenceLuminances Background luminances to check together. + * @param options.references Background colors to check together. * @param options.target Unpadded WCAG contrast floor. */ function serializeColorMeetingContrast( { color, getColorAtLightness, getStrongColor, - referenceLuminances, + references, target, }: { color: PlainColorObject; getColorAtLightness: GetColorForLightness; getStrongColor: () => PlainColorObject; - referenceLuminances: readonly number[]; + references: readonly string[]; target: number; } ) { const serializedColor = getColorString( color ); - if ( meetsContrastFloor( serializedColor, referenceLuminances, target ) ) { + if ( meetsContrastFloor( serializedColor, references, target ) ) { return serializedColor; } @@ -325,7 +305,7 @@ function serializeColorMeetingContrast( { const strengthened = getColorString( getColorAtLightness( colorLightness + direction * offset ) ); - if ( meetsContrastFloor( strengthened, referenceLuminances, target ) ) { + if ( meetsContrastFloor( strengthened, references, target ) ) { return strengthened; } } @@ -344,7 +324,7 @@ function serializeColorMeetingContrast( { * @param options.strongColor Serialized FGS5, already checked for WCAG. * @param options.strongEndpoint Endpoint allowed by the foreground path. * @param options.getColorAtLightness Color path indexed by OKLCH lightness. - * @param options.referenceLuminances Background luminances to check together. + * @param options.references Background colors to check together. * @param options.wcagTarget Unpadded WCAG contrast floor. * @param options.perceptualTarget Preferred APCA gap in Lc. */ @@ -354,7 +334,7 @@ function serializeColorMeetingPerceptualInterval( { strongColor, strongEndpoint, getColorAtLightness, - referenceLuminances, + references, wcagTarget, perceptualTarget, }: { @@ -363,7 +343,7 @@ function serializeColorMeetingPerceptualInterval( { strongColor: string; strongEndpoint: PlainColorObject; getColorAtLightness: GetColorForLightness; - referenceLuminances: readonly number[]; + references: readonly string[]; wcagTarget: number; perceptualTarget: number; } ) { @@ -384,11 +364,7 @@ function serializeColorMeetingPerceptualInterval( { if ( getPerceptualContrastMagnitude( background, serializedEndpoint ) < requiredStrongContrast || - ! meetsContrastFloor( - serializedEndpoint, - referenceLuminances, - wcagTarget - ) + ! meetsContrastFloor( serializedEndpoint, references, wcagTarget ) ) { return strongColor; } @@ -408,7 +384,7 @@ function serializeColorMeetingPerceptualInterval( { if ( getPerceptualContrastMagnitude( background, strengthened ) >= requiredStrongContrast && - meetsContrastFloor( strengthened, referenceLuminances, wcagTarget ) + meetsContrastFloor( strengthened, references, wcagTarget ) ) { return strengthened; } @@ -461,12 +437,12 @@ export function buildForegroundScale( ForegroundRampStep, ForegroundScaleConfig[ 'steps' ][ number ] >; - const referenceLuminances = Object.fromEntries( + const references = Object.fromEntries( config.steps.map( ( step ) => [ step.name, - getConstraintReferenceLuminances( step, ramp, backgroundRamp ), + getConstraintReferences( step, ramp, backgroundRamp ), ] ) - ) as Record< ForegroundRampStep, number[] >; + ) as Record< ForegroundRampStep, string[] >; const strongStep = steps.fgSurface5; const strongEndpoint = getColorAtLightness( ramp.direction === 'lighter' ? 1 : 0 @@ -481,7 +457,7 @@ export function buildForegroundScale( step.name, meetsContrastTarget( currentColor, - referenceLuminances[ step.name ], + references[ step.name ], step.contrast.target ) ? currentColor @@ -493,7 +469,7 @@ export function buildForegroundScale( ), weakColor: currentColor, strongColor: strongEndpoint, - referenceLuminances: referenceLuminances[ step.name ], + references: references[ step.name ], target: step.contrast.target, } ) ); @@ -504,7 +480,7 @@ export function buildForegroundScale( getColorAtLightness, weakColor: getColorAtLightness( get( fgSurface2, [ OKLCH, 'l' ] ) ), strongColor: strongEndpoint, - referenceLuminances: referenceLuminances.fgSurface3, + references: references.fgSurface3, target: steps.fgSurface3.contrast.target, } ); const fgSurface3Contrast = getPerceptualContrastMagnitude( @@ -515,7 +491,7 @@ export function buildForegroundScale( getColorAtLightness, weakColor: fgSurface3, strongColor: strongEndpoint, - referenceLuminances: referenceLuminances.fgSurface4, + references: references.fgSurface4, target: steps.fgSurface4.contrast.target, } ); const minimumFgSurface4Contrast = getPerceptualContrastMagnitude( @@ -575,7 +551,7 @@ export function buildForegroundScale( if ( ! meetsContrastTarget( normalColor, - referenceLuminances.fgSurface4, + references.fgSurface4, steps.fgSurface4.contrast.target ) ) { @@ -583,7 +559,7 @@ export function buildForegroundScale( getColorAtLightness, weakColor: normalColor, strongColor: strongEndpoint, - referenceLuminances: referenceLuminances.fgSurface4, + references: references.fgSurface4, target: steps.fgSurface4.contrast.target, } ); } @@ -616,7 +592,7 @@ export function buildForegroundScale( if ( ! meetsContrastTarget( strongColor, - referenceLuminances.fgSurface5, + references.fgSurface5, strongStep.contrast.target ) ) { @@ -624,7 +600,7 @@ export function buildForegroundScale( getColorAtLightness, weakColor: strongColor, strongColor: strongEndpoint, - referenceLuminances: referenceLuminances.fgSurface5, + references: references.fgSurface5, target: strongStep.contrast.target, } ); } @@ -653,7 +629,7 @@ export function buildForegroundScale( ) : getColorAtLightness, getStrongColor, - referenceLuminances: referenceLuminances[ step.name ], + references: references[ step.name ], target: step.contrast.target, } ); } @@ -673,7 +649,7 @@ export function buildForegroundScale( strongColor: nextRamp.fgSurface5!, strongEndpoint, getColorAtLightness, - referenceLuminances: referenceLuminances.fgSurface5, + references: references.fgSurface5, wcagTarget: strongStep.contrast.target, perceptualTarget: normalToActive, } ); @@ -689,7 +665,7 @@ export function buildForegroundScale( if ( ! meetsContrastFloor( nextRamp[ step.name ]!, - referenceLuminances[ step.name ], + references[ step.name ], step.contrast.target ) ) { diff --git a/packages/theme/src/color-ramps/lib/color-utils.ts b/packages/theme/src/color-ramps/lib/color-utils.ts index 21073ff83ffef3..531e1b7d9c0bf1 100644 --- a/packages/theme/src/color-ramps/lib/color-utils.ts +++ b/packages/theme/src/color-ramps/lib/color-utils.ts @@ -56,9 +56,7 @@ export function getContrast( * * @param color Color to measure. */ -export function getRelativeLuminance( - color: string | PlainColorObject -): number { +function getRelativeLuminance( color: string | PlainColorObject ): number { if ( typeof color === 'string' ) { ColorSpace.register( sRGB ); return Math.max( getLuminance( color ), 0 ); @@ -83,10 +81,7 @@ export function getRelativeLuminance( * @param first First relative luminance. * @param second Second relative luminance. */ -export function getContrastFromLuminances( - first: number, - second: number -): number { +function getContrastFromLuminances( first: number, second: number ): number { return first > second ? ( first + 0.05 ) / ( second + 0.05 ) : ( second + 0.05 ) / ( first + 0.05 ); diff --git a/packages/theme/src/color-ramps/test/color-space-registration.test.ts b/packages/theme/src/color-ramps/test/color-space-registration.test.ts index fba7a191bde17c..c2bf6aa9b0d179 100644 --- a/packages/theme/src/color-ramps/test/color-space-registration.test.ts +++ b/packages/theme/src/color-ramps/test/color-space-registration.test.ts @@ -23,7 +23,6 @@ describe( 'color-space registration', () => { 'clampToGamut', 'getColorString', 'getContrast', - 'getRelativeLuminance', ] as const )( '%s works without prior registration', async ( name ) => { const utils = await import( '../lib/color-utils' ); // Do not let an import-time registration satisfy the utility's needs. @@ -48,11 +47,6 @@ describe( 'color-space registration', () => { 21 ); break; - case 'getRelativeLuminance': - expect( utils.getRelativeLuminance( '#ffffff' ) ).toBeCloseTo( - 1 - ); - break; } } ); From c83c30716fe17fbe356a480ab3e89d01c0ffe208 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Mon, 7 Sep 2026 11:43:50 +0200 Subject: [PATCH 21/44] Theme: Preserve existing color ramp comments --- .../internal/_wpds-token-fallbacks.scss | 37 +-- .../theme/bin/generate-default-ramps/index.ts | 2 - .../bin/generate-primitive-tokens/index.ts | 18 +- packages/theme/prebuilt/css/design-tokens.css | 20 +- .../prebuilt/js/design-token-fallbacks.mjs | 37 +-- packages/theme/src/color-ramps/index.ts | 2 +- .../theme/src/color-ramps/lib/constants.ts | 25 +- .../src/color-ramps/lib/default-ramps.ts | 10 +- .../lib/find-color-with-constraints.ts | 48 ++-- packages/theme/src/color-ramps/lib/index.ts | 51 ++-- .../theme/src/color-ramps/lib/ramp-configs.ts | 15 +- packages/theme/src/color-ramps/lib/types.ts | 43 ++-- packages/theme/src/color-ramps/lib/utils.ts | 64 +++-- .../test/__snapshots__/index.test.ts.snap | 102 ++++---- .../theme/src/prebuilt/ts/color-tokens.ts | 21 +- .../src/semantic-color-contrast-pairs.ts | 4 +- packages/theme/tokens/README.md | 8 +- packages/theme/tokens/color.json | 220 +++++++++--------- 18 files changed, 394 insertions(+), 333 deletions(-) diff --git a/packages/base-styles/internal/_wpds-token-fallbacks.scss b/packages/base-styles/internal/_wpds-token-fallbacks.scss index 6baf8eddfaceab..679f68b4bc08c7 100644 --- a/packages/base-styles/internal/_wpds-token-fallbacks.scss +++ b/packages/base-styles/internal/_wpds-token-fallbacks.scss @@ -25,7 +25,7 @@ $fallbacks: ( "color-mix(in oklch, var(--wp-admin-theme-color, #3858e9) 12%, white)", "--wpds-color-background-interactive-brand-weak-disabled": "#0000", "--wpds-color-background-interactive-error": "#0000", - "--wpds-color-background-interactive-error-active": "#fff6f5", + "--wpds-color-background-interactive-error-active": "#f8f8f8", "--wpds-color-background-interactive-error-disabled": "#0000", "--wpds-color-background-interactive-error-strong": "#cc1818", "--wpds-color-background-interactive-error-strong-active": "#b90000", @@ -33,6 +33,9 @@ $fallbacks: ( "--wpds-color-background-interactive-error-weak": "#0000", "--wpds-color-background-interactive-error-weak-active": "#fae5e1", "--wpds-color-background-interactive-error-weak-disabled": "#0000", + "--wpds-color-background-interactive-neutral": "#fff", + "--wpds-color-background-interactive-neutral-active": "#fff", + "--wpds-color-background-interactive-neutral-disabled": "#fff", "--wpds-color-background-interactive-neutral-strong": "#2d2d2d", "--wpds-color-background-interactive-neutral-strong-active": "#1e1e1e", "--wpds-color-background-interactive-neutral-strong-disabled": "#e3e3e3", @@ -78,27 +81,27 @@ $fallbacks: ( "--wpds-color-foreground-content-warning": "#2e1900", "--wpds-color-foreground-content-warning-weak": "#936300", "--wpds-color-foreground-interactive-brand": - "color-mix(in oklch, var(--wp-admin-theme-color, #3858e9) 88%, black)", + "color-mix(in oklch, var(--wp-admin-theme-color, #3858e9) 96%, white)", "--wpds-color-foreground-interactive-brand-active": - "color-mix(in oklch, var(--wp-admin-theme-color, #3858e9) 35%, black)", + "color-mix(in oklch, var(--wp-admin-theme-color, #3858e9) 88%, black)", "--wpds-color-foreground-interactive-brand-disabled": "#8d8d8d", "--wpds-color-foreground-interactive-brand-strong": "#fff", "--wpds-color-foreground-interactive-brand-strong-active": "#fff", "--wpds-color-foreground-interactive-brand-strong-disabled": "#8d8d8d", - "--wpds-color-foreground-interactive-error": "#980f0f", - "--wpds-color-foreground-interactive-error-active": "#260101", + "--wpds-color-foreground-interactive-error": "#d71a1a", + "--wpds-color-foreground-interactive-error-active": "#980f0f", "--wpds-color-foreground-interactive-error-disabled": "#8d8d8d", - "--wpds-color-foreground-interactive-error-strong": "#eae6e5", - "--wpds-color-foreground-interactive-error-strong-active": "#eae6e5", + "--wpds-color-foreground-interactive-error-strong": "#eae6e6", + "--wpds-color-foreground-interactive-error-strong-active": "#eae6e6", "--wpds-color-foreground-interactive-error-strong-disabled": "#8d8d8d", "--wpds-color-foreground-interactive-neutral": "#505050", - "--wpds-color-foreground-interactive-neutral-active": "#141414", + "--wpds-color-foreground-interactive-neutral-active": "#505050", "--wpds-color-foreground-interactive-neutral-disabled": "#8d8d8d", "--wpds-color-foreground-interactive-neutral-strong": "#f0f0f0", "--wpds-color-foreground-interactive-neutral-strong-active": "#f0f0f0", "--wpds-color-foreground-interactive-neutral-strong-disabled": "#8d8d8d", "--wpds-color-foreground-interactive-neutral-weak": "#717171", - "--wpds-color-foreground-interactive-neutral-weak-active": "#141414", + "--wpds-color-foreground-interactive-neutral-weak-active": "#505050", "--wpds-color-foreground-interactive-neutral-weak-disabled": "#8d8d8d", "--wpds-color-stroke-focus": "var(--wp-admin-theme-color, #3858e9)", "--wpds-color-stroke-interactive-brand": @@ -118,19 +121,19 @@ $fallbacks: ( "color-mix(in oklch, var(--wp-admin-theme-color, #3858e9) 40%, white)", "--wpds-color-stroke-surface-brand-strong": "var(--wp-admin-theme-color, #3858e9)", - "--wpds-color-stroke-surface-caution": "#d0c28b", - "--wpds-color-stroke-surface-caution-strong": "#836a00", - "--wpds-color-stroke-surface-error": "#e0afa7", + "--wpds-color-stroke-surface-caution": "#d0c38b", + "--wpds-color-stroke-surface-caution-strong": "#836b00", + "--wpds-color-stroke-surface-error": "#dfafa8", "--wpds-color-stroke-surface-error-strong": "#cc1818", - "--wpds-color-stroke-surface-info": "#adc6e2", + "--wpds-color-stroke-surface-info": "#aac6e5", "--wpds-color-stroke-surface-info-strong": "#006bd7", "--wpds-color-stroke-surface-neutral": "#dbdbdb", "--wpds-color-stroke-surface-neutral-strong": "#8d8d8d", "--wpds-color-stroke-surface-neutral-weak": "#f0f0f0", - "--wpds-color-stroke-surface-success": "#92d39d", - "--wpds-color-stroke-surface-success-strong": "#008030", - "--wpds-color-stroke-surface-warning": "#e1bd7c", - "--wpds-color-stroke-surface-warning-strong": "#936300", + "--wpds-color-stroke-surface-success": "#92d19d", + "--wpds-color-stroke-surface-success-strong": "#007f2f", + "--wpds-color-stroke-surface-warning": "#e1be80", + "--wpds-color-stroke-surface-warning-strong": "#936400", "--wpds-cursor-control": "pointer", "--wpds-dimension-gap-2xl": "32px", "--wpds-dimension-gap-3xl": "40px", diff --git a/packages/theme/bin/generate-default-ramps/index.ts b/packages/theme/bin/generate-default-ramps/index.ts index 84ec6b0022914e..88a6d5366f97f6 100644 --- a/packages/theme/bin/generate-default-ramps/index.ts +++ b/packages/theme/bin/generate-default-ramps/index.ts @@ -27,8 +27,6 @@ const outputPath = join( ); const content = ` -// Generated by bin/generate-default-ramps/index.ts. Do not edit directly. -// Regenerate from the repository root: npm run build --workspace @wordpress/theme import type { RampResult } from './types.ts'; import type { DEFAULT_SEED_COLORS } from './constants.ts'; diff --git a/packages/theme/bin/generate-primitive-tokens/index.ts b/packages/theme/bin/generate-primitive-tokens/index.ts index 336dc150eae635..021d709c896f1c 100644 --- a/packages/theme/bin/generate-primitive-tokens/index.ts +++ b/packages/theme/bin/generate-primitive-tokens/index.ts @@ -12,10 +12,13 @@ import { getColorString } from '../../src/color-ramps/lib/color-utils.ts'; const __filename = fileURLToPath( import.meta.url ); const __dirname = path.dirname( __filename ); +// Path to the color.json file const colorJsonPath = path.join( __dirname, '../../tokens/color.json' ); -// Three decimals preserve 8-bit sRGB channels on round-trip to hex: -// the maximum rounding error is 0.0005 × 255, below half a channel step. +// 3 decimal places is the minimum precision for lossless hex serialization. +// With 3 decimal places rounding to the nearest 0.001, the maximum rounding +// error is 0.0005. With 256 possible hex values, 0.0005 × 256 = 0.128, +// guaranteeing the rounded value stays within 0.5 of the original value. const HEX_ROUNDING_PRECISION = 3; const transformColorStringToDTCGValue = ( color: string ) => { @@ -30,16 +33,18 @@ const transformColorStringToDTCGValue = ( color: string ) => { }; }; -// Replace primitive colors only; semantic aliases remain hand-authored. +// Main function function generatePrimitiveColorTokens() { const startTime = performance.now(); console.log( '🎨 Starting primitive color tokens generation...' ); try { + // Read the color.json file const colorJson = JSON.parse( fs.readFileSync( colorJsonPath, 'utf8' ) ); + // Build the ramps const bgRamp = buildBgRamp( DEFAULT_SEED_COLORS.background ); const accentRamps = [ ...Object.entries( DEFAULT_SEED_COLORS ) ] .filter( ( [ scaleName ] ) => scaleName !== 'background' ) @@ -48,8 +53,10 @@ function generatePrimitiveColorTokens() { ramp: buildAccentRamp( seed, bgRamp ), } ) ); - // The background seed uses the abbreviated `bg` primitive group. - // Public semantic tokens use the spelled-out `background` group. + // Convert the ramp values in a DTCG compatible format. + // Note: the background seed maps to the `bg` primitive ramp group, + // whose name is kept abbreviated even though the semantic tokens it + // feeds are exposed under the spelled-out `background` group. [ { scaleName: 'bg', @@ -68,6 +75,7 @@ function generatePrimitiveColorTokens() { } } ); + // Write the updated JSON back to the file with proper formatting fs.writeFileSync( colorJsonPath, JSON.stringify( colorJson, null, '\t' ) diff --git a/packages/theme/prebuilt/css/design-tokens.css b/packages/theme/prebuilt/css/design-tokens.css index 1dd31b9e7d5ddb..bbd9a9e3a0c4d2 100644 --- a/packages/theme/prebuilt/css/design-tokens.css +++ b/packages/theme/prebuilt/css/design-tokens.css @@ -38,7 +38,7 @@ /* Background color for interactive elements with error tone and normal emphasis. */ --wpds-color-background-interactive-error: #0000; /* Background color for interactive elements with error tone and normal emphasis that are hovered, focused, or active. */ - --wpds-color-background-interactive-error-active: #fff6f5; + --wpds-color-background-interactive-error-active: #f8f8f8; /* Background color for interactive elements with error tone and normal emphasis, in their disabled state. */ --wpds-color-background-interactive-error-disabled: #0000; /* Background color for interactive elements with error tone and strong emphasis. */ @@ -138,31 +138,31 @@ /* Foreground color for content like text with success tone and weak emphasis. */ --wpds-color-foreground-content-success-weak: #008030; /* Foreground color for content like text with warning tone and normal emphasis. */ - --wpds-color-foreground-content-warning: #654c19; + --wpds-color-foreground-content-warning: #654c1a; /* Foreground color for content like text with warning tone and weak emphasis. */ --wpds-color-foreground-content-warning-weak: #936300; /* Foreground color for interactive elements with brand tone and normal emphasis. */ - --wpds-color-foreground-interactive-brand: #222eda; + --wpds-color-foreground-interactive-brand: #3f61ea; /* Foreground color for interactive elements with brand tone and normal emphasis that are hovered, focused, or active. */ - --wpds-color-foreground-interactive-brand-active: #03063b; + --wpds-color-foreground-interactive-brand-active: #222eda; /* Foreground color for interactive elements with brand tone and normal emphasis, in their disabled state. */ --wpds-color-foreground-interactive-brand-disabled: #8d8d8d; /* Foreground color for interactive elements with brand tone and strong emphasis. */ - --wpds-color-foreground-interactive-brand-strong: #e7e8eb; + --wpds-color-foreground-interactive-brand-strong: #e7e8ea; /* Foreground color for interactive elements with brand tone and strong emphasis that are hovered, focused, or active. */ - --wpds-color-foreground-interactive-brand-strong-active: #e7e8eb; + --wpds-color-foreground-interactive-brand-strong-active: #e7e8ea; /* Foreground color for interactive elements with brand tone and strong emphasis, in their disabled state. */ --wpds-color-foreground-interactive-brand-strong-disabled: #8d8d8d; /* Foreground color for interactive elements with error tone and normal emphasis. */ - --wpds-color-foreground-interactive-error: #980f0f; + --wpds-color-foreground-interactive-error: #d71a1a; /* Foreground color for interactive elements with error tone and normal emphasis that are hovered, focused, or active. */ - --wpds-color-foreground-interactive-error-active: #260101; + --wpds-color-foreground-interactive-error-active: #980f0f; /* Foreground color for interactive elements with error tone and normal emphasis, in their disabled state. */ --wpds-color-foreground-interactive-error-disabled: #8d8d8d; /* Foreground color for interactive elements with error tone and strong emphasis. */ - --wpds-color-foreground-interactive-error-strong: #eae6e5; + --wpds-color-foreground-interactive-error-strong: #eae6e6; /* Foreground color for interactive elements with error tone and strong emphasis that are hovered, focused, or active. */ - --wpds-color-foreground-interactive-error-strong-active: #eae6e5; + --wpds-color-foreground-interactive-error-strong-active: #eae6e6; /* Foreground color for interactive elements with error tone and strong emphasis, in their disabled state. */ --wpds-color-foreground-interactive-error-strong-disabled: #8d8d8d; /* Foreground color for interactive elements with neutral tone and normal emphasis. */ diff --git a/packages/theme/prebuilt/js/design-token-fallbacks.mjs b/packages/theme/prebuilt/js/design-token-fallbacks.mjs index c15e96f380767e..cc3d4e98a29f48 100644 --- a/packages/theme/prebuilt/js/design-token-fallbacks.mjs +++ b/packages/theme/prebuilt/js/design-token-fallbacks.mjs @@ -23,7 +23,7 @@ export default { 'color-mix(in oklch, var(--wp-admin-theme-color, #3858e9) 12%, white)', '--wpds-color-background-interactive-brand-weak-disabled': '#0000', '--wpds-color-background-interactive-error': '#0000', - '--wpds-color-background-interactive-error-active': '#fff6f5', + '--wpds-color-background-interactive-error-active': '#f8f8f8', '--wpds-color-background-interactive-error-disabled': '#0000', '--wpds-color-background-interactive-error-strong': '#cc1818', '--wpds-color-background-interactive-error-strong-active': '#b90000', @@ -31,6 +31,9 @@ export default { '--wpds-color-background-interactive-error-weak': '#0000', '--wpds-color-background-interactive-error-weak-active': '#fae5e1', '--wpds-color-background-interactive-error-weak-disabled': '#0000', + '--wpds-color-background-interactive-neutral': '#fff', + '--wpds-color-background-interactive-neutral-active': '#fff', + '--wpds-color-background-interactive-neutral-disabled': '#fff', '--wpds-color-background-interactive-neutral-strong': '#2d2d2d', '--wpds-color-background-interactive-neutral-strong-active': '#1e1e1e', '--wpds-color-background-interactive-neutral-strong-disabled': '#e3e3e3', @@ -76,27 +79,27 @@ export default { '--wpds-color-foreground-content-warning': '#2e1900', '--wpds-color-foreground-content-warning-weak': '#936300', '--wpds-color-foreground-interactive-brand': - 'color-mix(in oklch, var(--wp-admin-theme-color, #3858e9) 88%, black)', + 'color-mix(in oklch, var(--wp-admin-theme-color, #3858e9) 96%, white)', '--wpds-color-foreground-interactive-brand-active': - 'color-mix(in oklch, var(--wp-admin-theme-color, #3858e9) 35%, black)', + 'color-mix(in oklch, var(--wp-admin-theme-color, #3858e9) 88%, black)', '--wpds-color-foreground-interactive-brand-disabled': '#8d8d8d', '--wpds-color-foreground-interactive-brand-strong': '#fff', '--wpds-color-foreground-interactive-brand-strong-active': '#fff', '--wpds-color-foreground-interactive-brand-strong-disabled': '#8d8d8d', - '--wpds-color-foreground-interactive-error': '#980f0f', - '--wpds-color-foreground-interactive-error-active': '#260101', + '--wpds-color-foreground-interactive-error': '#d71a1a', + '--wpds-color-foreground-interactive-error-active': '#980f0f', '--wpds-color-foreground-interactive-error-disabled': '#8d8d8d', - '--wpds-color-foreground-interactive-error-strong': '#eae6e5', - '--wpds-color-foreground-interactive-error-strong-active': '#eae6e5', + '--wpds-color-foreground-interactive-error-strong': '#eae6e6', + '--wpds-color-foreground-interactive-error-strong-active': '#eae6e6', '--wpds-color-foreground-interactive-error-strong-disabled': '#8d8d8d', '--wpds-color-foreground-interactive-neutral': '#505050', - '--wpds-color-foreground-interactive-neutral-active': '#141414', + '--wpds-color-foreground-interactive-neutral-active': '#505050', '--wpds-color-foreground-interactive-neutral-disabled': '#8d8d8d', '--wpds-color-foreground-interactive-neutral-strong': '#f0f0f0', '--wpds-color-foreground-interactive-neutral-strong-active': '#f0f0f0', '--wpds-color-foreground-interactive-neutral-strong-disabled': '#8d8d8d', '--wpds-color-foreground-interactive-neutral-weak': '#717171', - '--wpds-color-foreground-interactive-neutral-weak-active': '#141414', + '--wpds-color-foreground-interactive-neutral-weak-active': '#505050', '--wpds-color-foreground-interactive-neutral-weak-disabled': '#8d8d8d', '--wpds-color-stroke-focus': 'var(--wp-admin-theme-color, #3858e9)', '--wpds-color-stroke-interactive-brand': @@ -116,19 +119,19 @@ export default { 'color-mix(in oklch, var(--wp-admin-theme-color, #3858e9) 40%, white)', '--wpds-color-stroke-surface-brand-strong': 'var(--wp-admin-theme-color, #3858e9)', - '--wpds-color-stroke-surface-caution': '#d0c28b', - '--wpds-color-stroke-surface-caution-strong': '#836a00', - '--wpds-color-stroke-surface-error': '#e0afa7', + '--wpds-color-stroke-surface-caution': '#d0c38b', + '--wpds-color-stroke-surface-caution-strong': '#836b00', + '--wpds-color-stroke-surface-error': '#dfafa8', '--wpds-color-stroke-surface-error-strong': '#cc1818', - '--wpds-color-stroke-surface-info': '#adc6e2', + '--wpds-color-stroke-surface-info': '#aac6e5', '--wpds-color-stroke-surface-info-strong': '#006bd7', '--wpds-color-stroke-surface-neutral': '#dbdbdb', '--wpds-color-stroke-surface-neutral-strong': '#8d8d8d', '--wpds-color-stroke-surface-neutral-weak': '#f0f0f0', - '--wpds-color-stroke-surface-success': '#92d39d', - '--wpds-color-stroke-surface-success-strong': '#008030', - '--wpds-color-stroke-surface-warning': '#e1bd7c', - '--wpds-color-stroke-surface-warning-strong': '#936300', + '--wpds-color-stroke-surface-success': '#92d19d', + '--wpds-color-stroke-surface-success-strong': '#007f2f', + '--wpds-color-stroke-surface-warning': '#e1be80', + '--wpds-color-stroke-surface-warning-strong': '#936400', '--wpds-cursor-control': 'pointer', '--wpds-dimension-gap-2xl': '32px', '--wpds-dimension-gap-3xl': '40px', diff --git a/packages/theme/src/color-ramps/index.ts b/packages/theme/src/color-ramps/index.ts index 8c116b887a2f19..a024a963addd32 100644 --- a/packages/theme/src/color-ramps/index.ts +++ b/packages/theme/src/color-ramps/index.ts @@ -24,7 +24,7 @@ import { BLACK, CONTRAST_COMBINATIONS, WHITE } from './lib/constants.ts'; export { DEFAULT_SEED_COLORS } from './lib/constants.ts'; // Only solve both polarities near the black/white WCAG crossover. This keeps -// light and dark seeds keep their initial direction without a second solve. +// the extra work and possible direction change away from decisive seeds. const MAXIMUM_AMBIGUOUS_ENDPOINT_CONTRAST_DIFFERENCE = 1; // Allow the alternate direction only when its semantic anchor remains nearly // as faithful to the supplied seed as the initially preferred direction. diff --git a/packages/theme/src/color-ramps/lib/constants.ts b/packages/theme/src/color-ramps/lib/constants.ts index 689017dcb9f542..5020d94622250c 100644 --- a/packages/theme/src/color-ramps/lib/constants.ts +++ b/packages/theme/src/color-ramps/lib/constants.ts @@ -12,27 +12,34 @@ export const BLACK: PlainColorObject = { alpha: 1, }; -// Empirical WCAG ratio margin for search and rounding error. Final serialized -// colors still need contrast checks; this margin alone is not a guarantee. +// Margin added to target contrasts to counter for algorithm approximations and rounding errors. +// - the `CONTRAST_EPSILON` value is 0.004, so the real contrast can be lower by this amount. +// - the max contrast between adjacent RGB values is 1.016, so 0.016 is the maximum total rounding error between two values. +// - the sum is 0.02: the margin we add to ensure that the target contrast is met after all the rounding. export const UNIVERSAL_CONTRAST_TOPUP = 0.02; -// With preferLighter, choose black only when its minimum contrast exceeds -// white's by this amount. This changes the choice, not the required contrast. +// When enabling "lighter direction" bias, this is the amount by which +// black text contrast needs to be greater than white text contrast. +// The higher the value, the stronger the preference for white text. +// The current value has been determined empirically as the highest value +// that won't cause the algo not to be able to correctly solve all contrasts. export const WHITE_TEXT_CONTRAST_MARGIN = 3.1; -// Keep the accent SF2 anchor out of the middle-lightness range, where there -// is little contrast room for either foreground direction. +// These values are used as thresholds when trying to match the background +// ramp's lightness while calculating an accent ramp. They prevent the accent +// scale from being pinned to lightness values in the middle of the range, +// which would cause the algorithm to struggle to satisfy the accent scale +// constraints and therefore produce unexpected results. export const ACCENT_SCALE_BASE_LIGHTNESS_THRESHOLDS = { lighter: { min: 0.2, max: 0.4 }, darker: { min: 0.75, max: 0.98 }, } as const; -// Signed search-error tolerance. Its units depend on the caller's metric. +// Contrast precision we aim for. Approximately 1/256, resolution of an 8-bit number. export const CONTRAST_EPSILON = 4e-3; export const MAX_BISECTION_ITERATIONS = 10; -// Full-ramp diagnostic pairs, broader than the semantic token pair checks. export const CONTRAST_COMBINATIONS: { bgs: ( keyof Ramp )[]; fgs: ( keyof Ramp )[]; @@ -65,7 +72,7 @@ export const CONTRAST_COMBINATIONS: { }, ]; -// Shared defaults for token generation and runtime ThemeProvider calculations. +// Used when generating the DTCG tokens and the static color ramps. export const DEFAULT_SEED_COLORS = { background: '#fcfcfc', primary: '#3858e9', diff --git a/packages/theme/src/color-ramps/lib/default-ramps.ts b/packages/theme/src/color-ramps/lib/default-ramps.ts index 18002c86b56554..7bb6db485bcd5d 100644 --- a/packages/theme/src/color-ramps/lib/default-ramps.ts +++ b/packages/theme/src/color-ramps/lib/default-ramps.ts @@ -1,5 +1,3 @@ -// Generated by bin/generate-default-ramps/index.ts. Do not edit directly. -// Regenerate from the repository root: npm run build --workspace @wordpress/theme import type { RampResult } from './types.ts'; import type { DEFAULT_SEED_COLORS } from './constants.ts'; @@ -51,7 +49,7 @@ export const DEFAULT_RAMPS: Record< bgFillInverted2: '#1a1e27', bgFillInverted1: '#13009f', fgFillInverted: '#eff0f2', - surface1: '#edf1fb', + surface1: '#ecf1fc', fgSurface4: '#222eda', fgSurface5: '#03063b', }, @@ -162,7 +160,7 @@ export const DEFAULT_RAMPS: Record< surface5: '#f7dbd7', surface4: '#fae5e1', surface3: '#fff', - fgSurface3: '#d81a1a', + fgSurface3: '#d71a1a', fgSurface2: '#f74c40', stroke3: '#cc1818', stroke4: '#9d0000', @@ -170,8 +168,8 @@ export const DEFAULT_RAMPS: Record< stroke1: '#e0afa7', bgFillInverted2: '#231c1b', bgFillInverted1: '#640000', - fgFillInverted: '#f2efef', - surface1: '#fdedea', + fgFillInverted: '#f3efef', + surface1: '#fdece9', fgSurface4: '#980f0f', fgSurface5: '#260101', }, diff --git a/packages/theme/src/color-ramps/lib/find-color-with-constraints.ts b/packages/theme/src/color-ramps/lib/find-color-with-constraints.ts index 29b3af7705c994..ca41f637e6749b 100644 --- a/packages/theme/src/color-ramps/lib/find-color-with-constraints.ts +++ b/packages/theme/src/color-ramps/lib/find-color-with-constraints.ts @@ -5,29 +5,30 @@ import { clampToGamut, getContrast } from './color-utils.ts'; import { createChromaTaper, type TaperChromaOptions } from './taper-chroma.ts'; /** - * Signed log contrast ratio used as search error. Zero meets the target. - * - * @param c1 Achieved contrast. - * @param c2 Target contrast. + * Difference of contrast values that grows linearly with the Y luminance. + * We get more precise linear interpolations when we use this. + * @param c1 First contrast. + * @param c2 Second contrast. + * @return Difference of logarithms. */ function cdiff( c1: number, c2: number ) { return Math.log( c1 / c2 ); } /** - * Search OKLCH lightness toward black or white for a WCAG target, using the - * seed's hue and optional chroma taper. If the endpoint cannot reach the target, - * return it with a deficit. Results use search tolerance, not final hex checks. - * - * @param reference Color to contrast against. - * @param seed OKLCH seed supplying hue and chroma. - * @param target WCAG contrast ratio, already padded by the caller. - * @param direction Endpoint to search toward. - * @param options Optional lightness and chroma settings. - * @param options.lightnessConstraint Exact lightness to try before searching. - * @param options.lightnessConstraint.type Whether to force lightness even if contrast fails. - * @param options.lightnessConstraint.value OKLCH lightness to try. - * @param options.taperChromaOptions Chroma taper; omitted to retain seed chroma before gamut mapping. + * Solve for L such that: + * - the L applied to the seed meets the contrast target against the reference + * - the search is performed in one direction (ie lighter / darker) + * - more constraints can be applied around lightness + * @param reference + * @param seed + * @param target + * @param direction + * @param options + * @param options.lightnessConstraint + * @param options.lightnessConstraint.type + * @param options.lightnessConstraint.value + * @param options.taperChromaOptions */ export function findColorMeetingRequirements( reference: PlainColorObject, @@ -50,7 +51,8 @@ export function findColorMeetingRequirements( achieved: number; deficit?: number; } { - // A target of 1 means reuse, not a lightness search. + // A target of 1 means same color. + // A target lower than 1 doesn't make sense. if ( target <= 1 ) { return { color: reference, @@ -75,6 +77,7 @@ export function findColorMeetingRequirements( newL = tapered.l; newC = tapered.c; } else { + // It's already a ColorObject, return it directly return tapered; } } @@ -86,11 +89,15 @@ export function findColorMeetingRequirements( } ); } + // Set the boundary based on the direction. const mostContrastingL = direction === 'lighter' ? 1 : 0; const mostContrastingColor = direction === 'lighter' ? WHITE : BLACK; const highestContrast = getContrast( reference, mostContrastingColor ); if ( lightnessConstraint ) { + // Apply a specific L value. + // Useful when pinning a step to a specific lightness, of to specify + // min/max L values. const colorWithExactL = getColorForL( lightnessConstraint.value ); const exactLContrast = getContrast( reference, colorWithExactL ); const exactLContrastMeetsTarget = @@ -124,7 +131,10 @@ export function findColorMeetingRequirements( }; } - // Search from the reference lightness toward the selected endpoint. + // Bracket: low fails, high meets. + // Originally this was seed.oklch.l — although it's an assumption that works + // only when we know for sure the direction of the search. + // TODO: can we bring this back to seed.oklch.l ? const lowerL = get( reference, [ OKLCH, 'l' ] ); const lowerContrast = cdiff( 1, target ); const upperL = mostContrastingL; diff --git a/packages/theme/src/color-ramps/lib/index.ts b/packages/theme/src/color-ramps/lib/index.ts index 4cd0ce57416ecb..4fbf8bb2e8f605 100644 --- a/packages/theme/src/color-ramps/lib/index.ts +++ b/packages/theme/src/color-ramps/lib/index.ts @@ -30,18 +30,18 @@ import type { import { BLACK, WHITE, CONTRAST_EPSILON } from './constants.ts'; /** - * Solve the base steps and track the largest constraint deficit for seed - * adjustment. Perceptual reconstruction happens after this pass. + * Calculate the base color ramp before perceptual reconstruction. * - * @param params Base-pass inputs. - * @param params.seed OKLCH seed, possibly adjusted by a prior search. - * @param params.sortedSteps Steps to build, with dependencies first. - * @param params.config Base contrast and chroma settings. - * @param params.mainDir Ramp's foreground direction. - * @param params.oppDir Opposite of mainDir. - * @param params.pinLightness Lightness override, even if contrast fails. - * @param params.pinLightness.stepName Step to pin. - * @param params.pinLightness.value Pinned OKLCH lightness. + * @param params - The calculation parameters + * @param params.seed - The base color to build the ramp from + * @param params.sortedSteps - Steps sorted in dependency order + * @param params.config - Ramp configuration defining contrast requirements + * @param params.mainDir - Primary direction for the ramp (lighter/darker) + * @param params.oppDir - Opposite direction from mainDir + * @param params.pinLightness - Optional lightness override for a given step + * @param params.pinLightness.stepName + * @param params.pinLightness.value + * @return Object containing ramp results and satisfaction status */ function calculateRamp( { seed, @@ -67,7 +67,8 @@ function calculateRamp( { let maxDeficitDirection: RampDirection = 'lighter'; let maxDeficitStep; - // Dependencies use unrounded colors; only the output ramp is serialized. + // Keep track of the calculated colors, as they are going to be useful + // when other colors reference them. const calculatedColors = new Map< keyof Ramp | 'seed', PlainColorObject >(); calculatedColors.set( 'seed', seed ); @@ -93,6 +94,7 @@ function calculateRamp( { return referenceColor; } ); + // Check if we can reuse color from the `sameAsIfPossible` config option if ( sameAsIfPossible ) { const candidateColor = calculatedColors.get( sameAsIfPossible ); if ( ! candidateColor ) { @@ -107,11 +109,13 @@ function calculateRamp( { getContrast( referenceColor, candidateColor ) >= adjustedTarget ); + // If the candidate meets every contrast requirement, use it. if ( candidateMeetsTarget ) { + // Store the reused color calculatedColors.set( stepName, candidateColor ); rampResults[ stepName ] = getColorString( candidateColor ); - continue; + continue; // Skip to next step } } @@ -150,6 +154,7 @@ function calculateRamp( { const adjustedTarget = adjustContrastTarget( contrast.target ); + // Define the lightness constraint, if needed. let lightnessConstraint; if ( pinLightness?.stepName === stepName ) { lightnessConstraint = { @@ -163,6 +168,7 @@ function calculateRamp( { } as const; } + // Calculate the color meeting the requirements const searchResults = findColorMeetingRequirements( referenceColor, seed, @@ -174,8 +180,8 @@ function calculateRamp( { } ); - // The tightest constraint guides seed adjustment; negative deficits - // describe unused contrast room. + // When the target contrast is not met, take note of it and use + // that information to guide the ramp calculation bisection. if ( ! contrast.ignoreWhenAdjustingSeed && searchResults.deficit && @@ -186,8 +192,10 @@ function calculateRamp( { maxDeficitStep = stepName; } + // Store calculated color for future dependencies calculatedColors.set( stepName, searchResults.color ); + // Add to results rampResults[ stepName ] = getColorString( searchResults.color ); if ( ! searchResults.reached && ! contrast.ignoreWhenAdjustingSeed ) { @@ -278,8 +286,10 @@ export function buildRamp( oppDir = worse; } + // Get the correct calculation order based on dependencies const sortedSteps = sortByDependency( config.steps ); + // Calculate the ramp with the initial seed. const { rampResults, warnings, @@ -315,8 +325,10 @@ export function buildRamp( pinLightness, } ); - // Opposing failures mean the search has gone too far. Reverse the - // error sign instead of treating that unrelated deficit as progress. + // If the constraints start failing in the opposite direction to the original + // iteration's direction, that means we've moved too far away from the target. + // Don't use the `maxDeficit` value because it's not related to our search, + // and might even be positive, which would confuse the bisection algorithm. return iterationResults.maxDeficitDirection === maxDeficitDirection ? iterationResults.maxDeficit : -maxDeficit; @@ -351,8 +363,9 @@ export function buildRamp( bestWarnings = finalResult.warnings; } - // Elevation always runs from darker SF1 to lighter SF3, independent of the - // foreground direction used to solve the base constraints. + // Swap surface1 and surface3 for darker ramps to maintain visual elevation hierarchy. + // This ensures surface1 appears "behind" surface2, and surface3 appears "in front", + // regardless of the ramp's main direction. if ( mainDir === 'darker' ) { const tmpSurface1 = bestRamp.surface1; bestRamp.surface1 = bestRamp.surface3; diff --git a/packages/theme/src/color-ramps/lib/ramp-configs.ts b/packages/theme/src/color-ramps/lib/ramp-configs.ts index 1425608fd06497..9b6e4f97316cdf 100644 --- a/packages/theme/src/color-ramps/lib/ramp-configs.ts +++ b/packages/theme/src/color-ramps/lib/ramp-configs.ts @@ -39,7 +39,8 @@ const ACCENT_SURFACE_TAPER_CHROMA: TaperChromaOptions = { radiusLight: 0.01, }; -// Inverted fills retain their own high-contrast policy, separate from FGS5. +// Keep the existing inverted active-fill behavior independent from the new +// foreground endpoint policy. const highContrastInvertedFillConfig: RampStepConfig = { contrast: { reference: 'surface3', @@ -52,8 +53,10 @@ const highContrastInvertedFillConfig: RampStepConfig = { }; const FOREGROUND_PERCEPTUAL_TARGETS = { - // APCA design targets, not accessibility thresholds. Lower normal text when - // needed to leave a visible interaction state; keep the configured WCAG floors. + // These are design targets, not accessibility thresholds. WCAG ratios remain + // the hard gates for every surface on which a foreground can appear. + // Prefer strong normal text, but lower it when that is necessary to preserve + // a visible active state before the gamut endpoint. normalContrast: 86, // Let the interaction-state endpoint approach black or white when that makes // the state change more visible. WCAG remains a hard floor. @@ -269,8 +272,8 @@ export const BG_RAMP_CONFIG: RampConfig = { }, }; -// Unlike the background ramp's SF2 anchor, accents start from the strong fill. -// The subsequent perceptual passes are shared; see ../README.md for the flow. +// BG_RAMP: seed => surface2 => {bgFill, surface3 => all other tokens} +// ACCENT_RAMP: seed => bgFill1 => surface2 => surface3 => all other tokens export const ACCENT_RAMP_CONFIG: RampConfig = { steps: { ...BG_RAMP_CONFIG.steps, @@ -325,7 +328,7 @@ export const ACCENT_RAMP_CONFIG: RampConfig = { ...BG_RAMP_CONFIG.steps.stroke4, taperChromaOptions: undefined, }, - // Preserve chroma in the base foreground anchors. + // fgSurface: do not de-saturate fgSurface2: { ...BG_RAMP_CONFIG.steps.fgSurface2, taperChromaOptions: undefined, diff --git a/packages/theme/src/color-ramps/lib/types.ts b/packages/theme/src/color-ramps/lib/types.ts index c8c0667794dc1e..2118cc0385a093 100644 --- a/packages/theme/src/color-ramps/lib/types.ts +++ b/packages/theme/src/color-ramps/lib/types.ts @@ -1,7 +1,7 @@ import type { TaperChromaOptions } from './taper-chroma.ts'; export type Ramp = { - // Surface backgrounds: elevation (1–3) and emphasis (4–6). + // Backgrounds for surfaces (nuanced, slight variations compared to bg) surface1: string; surface2: string; surface3: string; @@ -18,7 +18,7 @@ export type Ramp = { bgFill2: string; bgFillInverted1: string; bgFillInverted2: string; - // Surface foregrounds: disabled (2), weak (3), normal (4), interaction (5). + // Foreground (text, icon) colors fgSurface2: string; fgSurface3: string; fgSurface4: string; @@ -37,7 +37,7 @@ export type BaseRampStep = keyof BaseRamp; export type AccentRampPurpose = 'full' | 'interactive' | 'status'; export type FollowDirection = 'main' | 'opposite' | 'best' | RampDirection; export type ContrastRequirement = { - /** Color against which to calculate the WCAG contrast ratio. */ + /** The reference color against which to calculate the contrast */ reference: BaseRampStep | 'seed'; /** * Other colors against which the generated color must meet the same target. @@ -46,32 +46,39 @@ export type ContrastRequirement = { */ additionalReferences?: readonly ( BaseRampStep | 'seed' )[]; /** - * Search direction: follow the ramp (`main`), reverse it (`opposite`), - * choose by endpoint contrast (`best`), or always use `lighter` / `darker`. + * Which direction should the algorithm search a matching color in: + * - main: follow the same direction as the ramp's main direction + * - opposite: follow the opposite direction of the ramp + * - best: pick the direction that has the most contrast headroom + * - hardcoded ramp direction (useful for generating colors that always + * light/dark regardless of the ramp direction) */ followDirection: FollowDirection; /** - * Bias `best` toward white text. This affects direction selection, not the - * required contrast ratio, and does not force the lighter direction. + * Prefer "lighter" direction when searching for a contrasting color. + * Especially useful for foreground color to counter the poor results that the + * WCAG algo gives when contrasting white text over mid-lightness backgrounds. */ preferLighter?: boolean; /** - * Required WCAG contrast ratio. A target of 1 copies the reference color. + * The contrast target to meet. */ target: number; /** - * Exclude this target from seed adjustment and base-pass warnings. + * When true, the algorithm won't count a failure in meeting the contrast + * target as a reason to recalculate the ramp. */ ignoreWhenAdjustingSeed?: boolean; }; export type RampStepConfig = { contrast: ContrastRequirement; - /** Preferred OKLCH lightness, used only when it meets the contrast target. */ lightness?: ( direction: RampDirection ) => number; taperChromaOptions?: TaperChromaOptions; /** - * Reuse this base step's color if it meets all contrast requirements. + * If specified, try to reuse the color from this step if it meets + * the contrast requirements. This reduces the number of unique colors + * in the ramp and improves consistency. */ sameAsIfPossible?: BaseRampStep; }; @@ -87,17 +94,16 @@ export type ForegroundRampStep = export type ForegroundScaleConfig = { /** Ramp step whose hue and chroma define the foreground scale. */ seed: BaseRampStep; - /** Step in the background ramp used to measure the APCA contrast range. */ + /** Background step used to measure the APCA contrast range. */ perceptualReference: BaseRampStep; - /** Preferred APCA magnitudes and gaps in Lc, not accessibility thresholds. */ perceptualTargets: { /** Preferred APCA contrast for normal content and resting controls. */ normalContrast: number; - /** Preferred gap below black/white; spent if the foreground gaps need it. */ + /** APCA contrast left unused unless the foreground intervals need it. */ endpointReserve: number; - /** Preferred gap from weak to normal emphasis; compresses if necessary. */ + /** Minimum APCA interval from weak to normal emphasis. */ weakToNormal: number; - /** Preferred gap from resting to interaction-state foreground. */ + /** Minimum APCA interval from a resting control to its active state. */ normalToActive: number; }; chroma: @@ -110,9 +116,7 @@ export type ForegroundScaleConfig = { }; steps: readonly ( { contrast: { - /** Check these steps in both the current and background ramps. */ references: readonly BaseRampStep[]; - /** Required WCAG contrast ratio, including after hex serialization. */ target: number; }; } & ( @@ -126,15 +130,12 @@ export type ForegroundScaleConfig = { }; export type RampConfig = { - /** Dependency-based WCAG solve, before perceptual reconstruction. */ steps: RampStepsConfig; - /** Foreground placement after surfaces and strokes have been rebuilt. */ foregroundScale: ForegroundScaleConfig; }; export type RampResult< Colors = Ramp > = { ramp: Colors; - /** Steps still failing their checked constraints; not all possible pairs. */ warnings?: ( keyof Ramp )[]; direction: RampDirection; }; diff --git a/packages/theme/src/color-ramps/lib/utils.ts b/packages/theme/src/color-ramps/lib/utils.ts index 52cec626447d67..342d4b80d3ac09 100644 --- a/packages/theme/src/color-ramps/lib/utils.ts +++ b/packages/theme/src/color-ramps/lib/utils.ts @@ -12,9 +12,8 @@ import type { BaseRampStep, RampStepsConfig, RampDirection } from './types.ts'; import { getContrast } from './color-utils.ts'; /** - * Track contrast and reuse dependencies between base steps. - * - * @param config Base step configuration. + * Build a dependency graph from the steps configuration + * @param config - The steps configuration object */ function buildDependencyGraph( config: RampStepsConfig ): { dependencies: Map< BaseRampStep, ( BaseRampStep | 'seed' )[] >; @@ -23,6 +22,7 @@ function buildDependencyGraph( config: RampStepsConfig ): { const dependencies = new Map< BaseRampStep, ( BaseRampStep | 'seed' )[] >(); const dependents = new Map< BaseRampStep | 'seed', BaseRampStep[] >(); + // Initialize maps Object.keys( config ).forEach( ( step ) => { dependencies.set( step as BaseRampStep, [] ); } ); @@ -31,6 +31,7 @@ function buildDependencyGraph( config: RampStepsConfig ): { dependents.set( step as BaseRampStep, [] ); } ); + // Build the graph Object.entries( config ).forEach( ( [ stepName, stepConfig ] ) => { const step = stepName as BaseRampStep; const references = [ @@ -43,6 +44,7 @@ function buildDependencyGraph( config: RampStepsConfig ): { dependents.get( reference )!.push( step ); } + // Add dependency for sameAsIfPossible if ( stepConfig.sameAsIfPossible ) { dependencies.get( step )!.push( stepConfig.sameAsIfPossible ); dependents.get( stepConfig.sameAsIfPossible )!.push( step ); @@ -53,9 +55,8 @@ function buildDependencyGraph( config: RampStepsConfig ): { } /** - * Order base steps so their references are built first. Reject cycles. - * - * @param config Base step configuration. + * Topologically sort steps based on their dependencies + * @param config - The steps configuration object */ export function sortByDependency( config: RampStepsConfig ): BaseRampStep[] { const { dependents } = buildDependencyGraph( config ); @@ -98,10 +99,10 @@ export function sortByDependency( config: RampStepsConfig ): BaseRampStep[] { return result; } /** - * Return the requested step and its dependencies, in build order. - * + * Return minimal set of steps that are needed to calculate `stepName` from the seed. * @param stepName Name of the step. * @param config Configuration of the ramp. + * @return Array of steps that `stepName` depends on. */ export function stepsForStep( stepName: BaseRampStep, @@ -131,11 +132,12 @@ export function stepsForStep( } /** - * Choose black or white by its weakest WCAG contrast across the references. - * This selects a direction; it does not prove a contrast target is reachable. - * - * @param references Colors the foreground must contrast against. - * @param preferLighter Bias the comparison toward white. + * Finds out whether a lighter or a darker foreground color achieves a better + * contrast against every reference color + * @param references + * @param preferLighter Whether the check should favor white foreground color + * @return An object with "better" and "worse" properties, each holding a + * ramp direction value. */ export function computeBetterFgColorDirection( references: string | PlainColorObject | readonly PlainColorObject[], @@ -165,24 +167,22 @@ export function computeBetterFgColorDirection( : { better: 'lighter', worse: 'darker' }; } -/** - * Pad a WCAG target for rounding, except 1, which means reuse the reference. - * - * @param target Unpadded WCAG ratio. - */ export function adjustContrastTarget( target: number ) { if ( target === 1 ) { return 1; } + // Add a little top up to take into account any rounding error and algo imprecisions. return target + UNIVERSAL_CONTRAST_TOPUP; } /** - * Bound the accent surface anchor to leave room for contrasting foregrounds. - * - * @param rawLightness Background SF2 lightness in OKLCH. - * @param direction Ramp's foreground direction. + * Prevent the accent scale from referencing a lightness value that + * would prevent the algorithm from complying with the requirements + * and cause it to generate unexpected results. + * @param rawLightness + * @param direction + * @return The clamped lightness value */ export function clampAccentScaleReferenceLightness( rawLightness: number, @@ -193,17 +193,15 @@ export function clampAccentScaleReferenceLightness( } /** - * Search for zero signed error between two bounds. "Lower" and "upper" refer - * to error signs, not numeric lightness order. Stops at the tolerance or - * iteration limit; callers must check any strict output requirements. - * - * @param calculateC Build a candidate from the search parameter (usually lightness). - * @param calculateValue Signed error for a candidate. - * @param initLowerL Parameter at the negative-error bound. - * @param initLowerValue Initial negative error. - * @param initUpperL Parameter at the positive-error bound. - * @param initUpperValue Initial positive error. - * @return Last sampled candidate. + * Find the value of `L` (luminance) that produces a `C` (color) that has a + * `value` (contrast delta) equal to zero. + * @param calculateC Calculate `C` from a given `L`. + * @param calculateValue Calculate value (delta) for a given `C`. + * @param initLowerL Initial lower value of `L`. + * @param initLowerValue Initial lower delta (negative). + * @param initUpperL Initial upper value of `L`. + * @param initUpperValue Initial upper delta (positive). + * @return Resulting value of type `C`. */ export function solveWithBisect< C >( calculateC: ( l: number ) => C, diff --git a/packages/theme/src/color-ramps/test/__snapshots__/index.test.ts.snap b/packages/theme/src/color-ramps/test/__snapshots__/index.test.ts.snap index bdf3aea7666c88..d3e6f88d65ef4b 100644 --- a/packages/theme/src/color-ramps/test/__snapshots__/index.test.ts.snap +++ b/packages/theme/src/color-ramps/test/__snapshots__/index.test.ts.snap @@ -2151,7 +2151,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFill2": "#2e49d9", "bgFillInverted1": "#13009f", "bgFillInverted2": "#1a1e27", - "fgFill": "#e7e8eb", + "fgFill": "#e7e8ea", "fgFillInverted": "#eff0f2", "fgSurface2": "#3351e2", "fgSurface3": "#2031c3", @@ -2190,7 +2190,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFill2": "#2e49d9", "bgFillInverted1": "#13009f", "bgFillInverted2": "#1a1e27", - "fgFill": "#e7e8eb", + "fgFill": "#e7e8ea", "fgFillInverted": "#eff0f2", "fgSurface2": "#466bfd", "fgSurface3": "#2f4cdc", @@ -2229,7 +2229,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFill2": "#2e49d9", "bgFillInverted1": "#13009f", "bgFillInverted2": "#1a1e27", - "fgFill": "#e7e8eb", + "fgFill": "#e7e8ea", "fgFillInverted": "#eff0f2", "fgSurface2": "#6088ff", "fgSurface3": "#3858e9", @@ -2269,8 +2269,8 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFill1": "#0090ff", "bgFill2": "#37a3ff", "bgFillInverted1": "#b8e0ff", - "bgFillInverted2": "#eef0f2", - "fgFill": "#22282e", + "bgFillInverted2": "#eef0f3", + "fgFill": "#23282d", "fgFillInverted": "#1a1f24", "fgSurface2": "#005bbf", "fgSurface3": "#0090ff", @@ -2308,8 +2308,8 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFill1": "#0090ff", "bgFill2": "#37a3ff", "bgFillInverted1": "#b8e0ff", - "bgFillInverted2": "#eef0f2", - "fgFill": "#22282e", + "bgFillInverted2": "#eef0f3", + "fgFill": "#23282d", "fgFillInverted": "#1a1f24", "fgSurface2": "#005dc1", "fgSurface3": "#0090ff", @@ -2347,8 +2347,8 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFill1": "#0090ff", "bgFill2": "#37a3ff", "bgFillInverted1": "#b8e0ff", - "bgFillInverted2": "#eef0f2", - "fgFill": "#22282e", + "bgFillInverted2": "#eef0f3", + "fgFill": "#23282d", "fgFillInverted": "#1a1f24", "fgSurface1": "#004ba3", "fgSurface2": "#0065d0", @@ -2387,8 +2387,8 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFill1": "#0090ff", "bgFill2": "#37a3ff", "bgFillInverted1": "#b8e0ff", - "bgFillInverted2": "#eef0f2", - "fgFill": "#22282e", + "bgFillInverted2": "#eef0f3", + "fgFill": "#23282d", "fgFillInverted": "#1a1f24", "fgSurface2": "#007be8", "fgSurface3": "#6bb9ff", @@ -2426,8 +2426,8 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFill1": "#0090ff", "bgFill2": "#37a3ff", "bgFillInverted1": "#b8e0ff", - "bgFillInverted2": "#eef0f2", - "fgFill": "#22282e", + "bgFillInverted2": "#eef0f3", + "fgFill": "#23282d", "fgFillInverted": "#1a1f24", "fgSurface2": "#289eff", "fgSurface3": "#7fc3ff", @@ -2465,9 +2465,9 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFill1": "#0090ff", "bgFill2": "#007fed", "bgFillInverted1": "#001a4f", - "bgFillInverted2": "#040609", + "bgFillInverted2": "#050609", "fgFill": "#12161a", - "fgFillInverted": "#eef0f2", + "fgFillInverted": "#eef0f3", "fgSurface2": "#00489e", "fgSurface3": "#002e70", "fgSurface4": "#000327", @@ -2506,7 +2506,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFillInverted1": "#002a69", "bgFillInverted2": "#1a1f24", "fgFill": "#12161a", - "fgFillInverted": "#eef0f2", + "fgFillInverted": "#eef0f3", "fgSurface2": "#005fc6", "fgSurface3": "#004599", "fgSurface4": "#001b4f", @@ -2545,7 +2545,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFillInverted1": "#002a69", "bgFillInverted2": "#1a1f24", "fgFill": "#12161a", - "fgFillInverted": "#eef0f2", + "fgFillInverted": "#eef0f3", "fgSurface2": "#0077e4", "fgSurface3": "#005abc", "fgSurface4": "#001b4f", @@ -2553,7 +2553,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "stroke2": "#77a0cf", "stroke3": "#005abc", "stroke4": "#003f8e", - "surface1": "#c1d8f3", + "surface1": "#bdd9f9", "surface2": "#cde0f6", "surface3": "#dae8f9", "surface4": "#bad3f1", @@ -2584,7 +2584,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFillInverted1": "#002a69", "bgFillInverted2": "#1a1f24", "fgFill": "#12161a", - "fgFillInverted": "#eef0f2", + "fgFillInverted": "#eef0f3", "fgSurface2": "#0190ff", "fgSurface3": "#006fdb", "fgSurface4": "#001b4f", @@ -2796,7 +2796,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "stroke2": "#57825e", "stroke3": "#69d682", "stroke4": "#a0ffb4", - "surface1": "#264d2e", + "surface1": "#254d2e", "surface2": "#2a5232", "surface3": "#2d5836", "surface4": "#2f5b38", @@ -2916,7 +2916,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "stroke2": "#6daf79", "stroke3": "#007020", "stroke4": "#00510c", - "surface1": "#8fefa2", + "surface1": "#9beba9", "surface2": "#a4f5b2", "surface3": "#c1f8ca", "surface4": "#89e99c", @@ -2987,7 +2987,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFill1": "#f0d148", "bgFill2": "#ffe762", "bgFillInverted1": "#f9da53", - "bgFillInverted2": "#faf1ca", + "bgFillInverted2": "#fef1bc", "fgFill": "#5b5a53", "fgFillInverted": "#1f1e1b", "fgSurface2": "#725c00", @@ -3026,7 +3026,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFill1": "#f0d148", "bgFill2": "#ffe762", "bgFillInverted1": "#f9da53", - "bgFillInverted2": "#faf1ca", + "bgFillInverted2": "#fef1bc", "fgFill": "#5b5a53", "fgFillInverted": "#1f1e1b", "fgSurface2": "#745e00", @@ -3065,7 +3065,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFill1": "#f0d148", "bgFill2": "#ffe762", "bgFillInverted1": "#f9da53", - "bgFillInverted2": "#faf1ca", + "bgFillInverted2": "#fef1bc", "fgFill": "#5b5a53", "fgFillInverted": "#1f1e1b", "fgSurface1": "#604c00", @@ -3105,7 +3105,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFill1": "#f0d148", "bgFill2": "#ffe762", "bgFillInverted1": "#f9da53", - "bgFillInverted2": "#faf1ca", + "bgFillInverted2": "#fef1bc", "fgFill": "#5b5a53", "fgFillInverted": "#1f1e1b", "fgSurface2": "#957a00", @@ -3115,7 +3115,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "stroke2": "#8e8661", "stroke3": "#f0d148", "stroke4": "#fff", - "surface1": "#2d2814", + "surface1": "#2e2812", "surface2": "#332e18", "surface3": "#39331b", "surface4": "#3d371d", @@ -3144,7 +3144,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFill1": "#f0d148", "bgFill2": "#ffe762", "bgFillInverted1": "#f9da53", - "bgFillInverted2": "#faf1ca", + "bgFillInverted2": "#fef1bc", "fgFill": "#5b5a53", "fgFillInverted": "#1f1e1b", "fgSurface1": "#947901", @@ -3155,7 +3155,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "stroke2": "#8e8661", "stroke3": "#f0d148", "stroke4": "#fff", - "surface1": "#4a4325", + "surface1": "#4a4323", "surface2": "#4f4828", "surface3": "#554d2b", "surface4": "#58502e", @@ -3186,7 +3186,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFillInverted1": "#281d00", "bgFillInverted2": "#060605", "fgFill": "#4f4e48", - "fgFillInverted": "#faf1c9", + "fgFillInverted": "#fef1bb", "fgSurface2": "#5c4900", "fgSurface3": "#3e3000", "fgSurface4": "#0b0500", @@ -3225,7 +3225,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFillInverted1": "#392c00", "bgFillInverted2": "#1f1e1b", "fgFill": "#4f4e48", - "fgFillInverted": "#faf1c9", + "fgFillInverted": "#fef1bb", "fgSurface2": "#776000", "fgSurface3": "#544814", "fgSurface4": "#493e10", @@ -3265,7 +3265,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFillInverted1": "#392c00", "bgFillInverted2": "#1f1e1b", "fgFill": "#4f4e48", - "fgFillInverted": "#faf1c9", + "fgFillInverted": "#fef1bb", "fgSurface2": "#907600", "fgSurface3": "#715b00", "fgSurface4": "#281d00", @@ -3343,9 +3343,9 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFill1": "#f0b849", "bgFill2": "#ffcd61", "bgFillInverted1": "#ffd670", - "bgFillInverted2": "#f5efe5", - "fgFill": "#524e49", - "fgFillInverted": "#1f1e1b", + "bgFillInverted2": "#f6efe5", + "fgFill": "#524e48", + "fgFillInverted": "#201e1b", "fgSurface2": "#815600", "fgSurface3": "#f0b849", "fgSurface4": "#ffebb0", @@ -3382,9 +3382,9 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFill1": "#f0b849", "bgFill2": "#ffcd61", "bgFillInverted1": "#ffd670", - "bgFillInverted2": "#f5efe5", - "fgFill": "#524e49", - "fgFillInverted": "#1f1e1b", + "bgFillInverted2": "#f6efe5", + "fgFill": "#524e48", + "fgFillInverted": "#201e1b", "fgSurface2": "#825700", "fgSurface3": "#f0b849", "fgSurface4": "#ffebb0", @@ -3581,12 +3581,12 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFill1": "#f0b849", "bgFill2": "#dda633", "bgFillInverted1": "#422800", - "bgFillInverted2": "#1f1e1b", + "bgFillInverted2": "#201e1b", "fgFill": "#45423d", - "fgFillInverted": "#f5efe5", + "fgFillInverted": "#f6efe5", "fgSurface2": "#855900", "fgSurface3": "#5b4416", - "fgSurface4": "#4f3b12", + "fgSurface4": "#503b12", "fgSurface5": "#000", "stroke1": "#a58e67", "stroke2": "#96805a", @@ -3621,9 +3621,9 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFill1": "#f0b849", "bgFill2": "#dda633", "bgFillInverted1": "#422800", - "bgFillInverted2": "#1f1e1b", + "bgFillInverted2": "#201e1b", "fgFill": "#45423d", - "fgFillInverted": "#f5efe5", + "fgFillInverted": "#f6efe5", "fgSurface2": "#a26e00", "fgSurface3": "#7f5400", "fgSurface4": "#2e1900", @@ -3660,7 +3660,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFill1": "#f0b849", "bgFill2": "#dda633", "bgFillInverted1": "#422800", - "bgFillInverted2": "#1f1e1b", + "bgFillInverted2": "#201e1b", "fgFill": "#45423d", "fgFillInverted": "#f5efe5", "fgSurface2": "#ba8500", @@ -3834,7 +3834,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "stroke2": "#9e5249", "stroke3": "#ff8879", "stroke4": "#ffc3b9", - "surface1": "#451712", + "surface1": "#421914", "surface2": "#4e1b16", "surface3": "#571f19", "surface4": "#5d211c", @@ -3893,16 +3893,16 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "value": 0.7, }, }, - "seedComputed": "#fff5f1", + "seedComputed": "#fffaf7", "seedOriginal": "#cc1818", "seedUnchanged": false, }, "output": { "direction": "darker", "ramp": { - "bgFill1": "#fff5f1", - "bgFill2": "#e9dfdb", - "bgFillInverted1": "#211917", + "bgFill1": "#fffaf7", + "bgFill2": "#e9e4e1", + "bgFillInverted1": "#1e1a18", "bgFillInverted2": "#000", "fgFill": "#221d1b", "fgFillDark": "#f9ede9", @@ -3982,9 +3982,9 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFill1": "#cc1818", "bgFill2": "#b90000", "bgFillInverted1": "#640000", - "bgFillInverted2": "#231c1b", - "fgFill": "#eae6e5", - "fgFillInverted": "#f2efef", + "bgFillInverted2": "#241c1b", + "fgFill": "#eae6e6", + "fgFillInverted": "#f3efef", "fgSurface2": "#e1342d", "fgSurface3": "#bd0002", "fgSurface4": "#470000", diff --git a/packages/theme/src/prebuilt/ts/color-tokens.ts b/packages/theme/src/prebuilt/ts/color-tokens.ts index e3dcc6c41af9fb..6c009421705f04 100644 --- a/packages/theme/src/prebuilt/ts/color-tokens.ts +++ b/packages/theme/src/prebuilt/ts/color-tokens.ts @@ -21,6 +21,7 @@ export default { 'foreground-interactive-brand-strong-active', ], 'primary-surface4': [ 'background-interactive-brand-weak-active' ], + 'primary-fgSurface3': [ 'foreground-interactive-brand' ], 'primary-stroke3': [ 'background-thumb-brand', 'background-thumb-brand-active', @@ -31,8 +32,7 @@ export default { 'primary-stroke4': [ 'stroke-interactive-brand-active' ], 'primary-stroke1': [ 'stroke-surface-brand' ], 'primary-surface1': [ 'background-surface-brand' ], - 'primary-fgSurface4': [ 'foreground-interactive-brand' ], - 'primary-fgSurface5': [ 'foreground-interactive-brand-active' ], + 'primary-fgSurface4': [ 'foreground-interactive-brand-active' ], 'info-surface2': [ 'background-surface-info-weak' ], 'info-surface4': [ 'background-surface-info' ], 'info-fgSurface3': [ 'foreground-content-info-weak' ], @@ -65,7 +65,10 @@ export default { 'background-interactive-error-weak-active', 'background-surface-error', ], - 'error-fgSurface3': [ 'foreground-content-error-weak' ], + 'error-fgSurface3': [ + 'foreground-content-error-weak', + 'foreground-interactive-error', + ], 'error-stroke3': [ 'stroke-interactive-error', 'stroke-interactive-error-strong', @@ -75,9 +78,8 @@ export default { 'error-stroke1': [ 'stroke-surface-error' ], 'error-fgSurface4': [ 'foreground-content-error', - 'foreground-interactive-error', + 'foreground-interactive-error-active', ], - 'error-fgSurface5': [ 'foreground-interactive-error-active' ], 'bg-surface2': [ 'background-surface-neutral' ], 'bg-surface5': [ 'background-interactive-brand-strong-disabled', @@ -85,7 +87,12 @@ export default { 'background-interactive-neutral-strong-disabled', ], 'bg-surface4': [ 'background-interactive-neutral-weak-active' ], - 'bg-surface3': [ 'background-surface-neutral-strong' ], + 'bg-surface3': [ + 'background-interactive-neutral', + 'background-interactive-neutral-active', + 'background-interactive-neutral-disabled', + 'background-surface-neutral-strong', + ], 'bg-fgSurface3': [ 'foreground-content-neutral-weak', 'foreground-interactive-neutral-weak', @@ -133,8 +140,6 @@ export default { 'background-thumb-neutral', 'foreground-content-neutral', 'foreground-interactive-neutral', - ], - 'bg-fgSurface5': [ 'foreground-interactive-neutral-active', 'foreground-interactive-neutral-weak-active', ], diff --git a/packages/theme/src/semantic-color-contrast-pairs.ts b/packages/theme/src/semantic-color-contrast-pairs.ts index 4a064a213459ad..3dcc6f818c5ee7 100644 --- a/packages/theme/src/semantic-color-contrast-pairs.ts +++ b/packages/theme/src/semantic-color-contrast-pairs.ts @@ -1,11 +1,11 @@ -export const MINIMUM_TEXT_CONTRAST = 4.5; - /** * Semantic color token paths used together for normal text. * * Paths omit the shared `wpds-color` prefix so build configuration and runtime * tests can adapt the same pairs to DTCG token IDs and CSS custom properties. */ +export const MINIMUM_TEXT_CONTRAST = 4.5; + export const SEMANTIC_COLOR_CONTRAST_PAIRS = [ { background: 'background.interactive.neutral', diff --git a/packages/theme/tokens/README.md b/packages/theme/tokens/README.md index d2d861f311ba7a..2edd814c99eddb 100644 --- a/packages/theme/tokens/README.md +++ b/packages/theme/tokens/README.md @@ -25,9 +25,11 @@ Semantic tokens follow a consistent naming pattern that encodes the token's purp ## Primitive and Semantic Tokens -**Primitive tokens** are internal raw values, such as a color or spacing size. They are not part of the public API. +**Primitive tokens** are internal, reusable, raw values emitted by the design system's theming. They are not part of the public API, but instead are referenced by semantic tokens, which use the underlying values and provide purpose and meaning to how those values are used. -**Semantic tokens** are the public names that reference those values and describe their intended use. Choose a token by its purpose, not by the value it happens to have in the default theme. +**Semantic tokens** are the public API of the design system's tokens. They map to primitive token values, but the values are incidental, and a consumer is expected to choose a semantic token based on their specific use-case. The design system provides semantic tokens to cover a breadth of use-cases that are standardized at a design systems level. + +This structure is meant to **shift the emphasis away from the values themselves and toward the meaning and purpose that the tokens represent**. Ultimately, the tokens still map to raw values that affect how a component is styled and those values should be internally consistent, but the primitive layer is an incidental concern of the theming internals and not a consideration of the end-user of the design system. Example: @@ -62,7 +64,7 @@ In the example above, the CSS properties generated from these tokens would inclu --wpds-dimension-size-lg: 40px; ``` -Consumers use `--wpds-dimension-size-lg` for elements that follow the large button and input size, without depending on the internal `primitive.space.80` name or its value. +Someone using the design system should never see or concern themselves with either the `primitive.space.80` token or the underlying 4 pixel base unit, and instead focus on the semantics of how element size tokens apply to their component. In this example, a large token being used for a component that follows the size of buttons and inputs in the system. ## Custom Extensions diff --git a/packages/theme/tokens/color.json b/packages/theme/tokens/color.json index bedb21bf64a1d1..6e679f35193371 100644 --- a/packages/theme/tokens/color.json +++ b/packages/theme/tokens/color.json @@ -34,22 +34,22 @@ "surface2": { "$value": { "colorSpace": "srgb", - "components": [ 0.965, 0.973, 0.996 ], - "hex": "#f6f8fe" + "components": [ 0.973, 0.973, 0.973 ], + "hex": "#f8f8f8" } }, "surface6": { "$value": { "colorSpace": "srgb", - "components": [ 0.78, 0.824, 0.933 ], - "hex": "#c7d2ee" + "components": [ 0.776, 0.824, 0.945 ], + "hex": "#c6d2f1" } }, "surface5": { "$value": { "colorSpace": "srgb", - "components": [ 0.847, 0.875, 0.941 ], - "hex": "#d8dff0" + "components": [ 0.839, 0.875, 0.957 ], + "hex": "#d6dff4" } }, "surface4": { @@ -104,8 +104,8 @@ "stroke1": { "$value": { "colorSpace": "srgb", - "components": [ 0.682, 0.733, 0.855 ], - "hex": "#aebbda" + "components": [ 0.686, 0.733, 0.855 ], + "hex": "#afbbda" } }, "bgFillInverted2": { @@ -132,8 +132,8 @@ "surface1": { "$value": { "colorSpace": "srgb", - "components": [ 0.929, 0.945, 0.984 ], - "hex": "#edf1fb" + "components": [ 0.925, 0.945, 0.988 ], + "hex": "#ecf1fc" } }, "fgSurface4": { @@ -176,15 +176,15 @@ "surface2": { "$value": { "colorSpace": "srgb", - "components": [ 0.953, 0.976, 1 ], - "hex": "#f3f9ff" + "components": [ 0.973, 0.973, 0.973 ], + "hex": "#f8f8f8" } }, "surface6": { "$value": { "colorSpace": "srgb", - "components": [ 0.737, 0.835, 0.949 ], - "hex": "#bcd5f2" + "components": [ 0.729, 0.839, 0.965 ], + "hex": "#bad6f6" } }, "surface5": { @@ -197,8 +197,8 @@ "surface4": { "$value": { "colorSpace": "srgb", - "components": [ 0.878, 0.929, 0.988 ], - "hex": "#e0edfc" + "components": [ 0.886, 0.929, 0.98 ], + "hex": "#e2edfa" } }, "surface3": { @@ -239,15 +239,15 @@ "stroke2": { "$value": { "colorSpace": "srgb", - "components": [ 0.6, 0.702, 0.816 ], - "hex": "#99b3d0" + "components": [ 0.588, 0.702, 0.827 ], + "hex": "#96b3d3" } }, "stroke1": { "$value": { "colorSpace": "srgb", - "components": [ 0.678, 0.776, 0.886 ], - "hex": "#adc6e2" + "components": [ 0.667, 0.776, 0.898 ], + "hex": "#aac6e5" } }, "bgFillInverted2": { @@ -267,8 +267,8 @@ "fgFillInverted": { "$value": { "colorSpace": "srgb", - "components": [ 0.933, 0.941, 0.949 ], - "hex": "#eef0f2" + "components": [ 0.933, 0.941, 0.953 ], + "hex": "#eef0f3" } }, "surface1": { @@ -318,15 +318,15 @@ "surface2": { "$value": { "colorSpace": "srgb", - "components": [ 0.922, 1, 0.929 ], - "hex": "#ebffed" + "components": [ 0.973, 0.973, 0.973 ], + "hex": "#f8f8f8" } }, "surface6": { "$value": { "colorSpace": "srgb", - "components": [ 0.537, 0.91, 0.612 ], - "hex": "#89e89c" + "components": [ 0.537, 0.906, 0.608 ], + "hex": "#89e79b" } }, "surface5": { @@ -360,36 +360,36 @@ "fgSurface2": { "$value": { "colorSpace": "srgb", - "components": [ 0.169, 0.624, 0.306 ], - "hex": "#2b9f4e" + "components": [ 0.165, 0.62, 0.302 ], + "hex": "#2a9e4d" } }, "stroke3": { "$value": { "colorSpace": "srgb", - "components": [ 0, 0.502, 0.188 ], - "hex": "#008030" + "components": [ 0, 0.498, 0.184 ], + "hex": "#007f2f" } }, "stroke4": { "$value": { "colorSpace": "srgb", - "components": [ 0, 0.38, 0.075 ], - "hex": "#006113" + "components": [ 0, 0.376, 0.075 ], + "hex": "#006013" } }, "stroke2": { "$value": { "colorSpace": "srgb", - "components": [ 0.506, 0.757, 0.553 ], - "hex": "#81c18d" + "components": [ 0.51, 0.753, 0.553 ], + "hex": "#82c08d" } }, "stroke1": { "$value": { "colorSpace": "srgb", - "components": [ 0.573, 0.827, 0.616 ], - "hex": "#92d39d" + "components": [ 0.573, 0.82, 0.616 ], + "hex": "#92d19d" } }, "bgFillInverted2": { @@ -409,8 +409,8 @@ "fgFillInverted": { "$value": { "colorSpace": "srgb", - "components": [ 0.925, 0.949, 0.925 ], - "hex": "#ecf2ec" + "components": [ 0.922, 0.949, 0.925 ], + "hex": "#ebf2ec" } }, "surface1": { @@ -460,29 +460,29 @@ "surface2": { "$value": { "colorSpace": "srgb", - "components": [ 1, 0.969, 0.882 ], - "hex": "#fff7e1" + "components": [ 0.973, 0.973, 0.973 ], + "hex": "#f8f8f8" } }, "surface6": { "$value": { "colorSpace": "srgb", - "components": [ 0.953, 0.8, 0.525 ], - "hex": "#f3cc86" + "components": [ 0.961, 0.804, 0.525 ], + "hex": "#f5cd86" } }, "surface5": { "$value": { "colorSpace": "srgb", - "components": [ 0.965, 0.855, 0.667 ], - "hex": "#f6daaa" + "components": [ 0.973, 0.859, 0.659 ], + "hex": "#f8dba8" } }, "surface4": { "$value": { "colorSpace": "srgb", - "components": [ 1, 0.91, 0.753 ], - "hex": "#ffe8c0" + "components": [ 0.988, 0.918, 0.788 ], + "hex": "#fceac9" } }, "surface3": { @@ -502,22 +502,22 @@ "fgSurface2": { "$value": { "colorSpace": "srgb", - "components": [ 0.71, 0.498, 0 ], - "hex": "#b57f00" + "components": [ 0.714, 0.502, 0 ], + "hex": "#b68000" } }, "stroke3": { "$value": { "colorSpace": "srgb", - "components": [ 0.576, 0.388, 0 ], - "hex": "#936300" + "components": [ 0.576, 0.392, 0 ], + "hex": "#936400" } }, "stroke4": { "$value": { "colorSpace": "srgb", - "components": [ 0.439, 0.286, 0 ], - "hex": "#704900" + "components": [ 0.439, 0.29, 0 ], + "hex": "#704a00" } }, "stroke2": { @@ -530,15 +530,15 @@ "stroke1": { "$value": { "colorSpace": "srgb", - "components": [ 0.882, 0.741, 0.486 ], - "hex": "#e1bd7c" + "components": [ 0.882, 0.745, 0.502 ], + "hex": "#e1be80" } }, "bgFillInverted2": { "$value": { "colorSpace": "srgb", - "components": [ 0.122, 0.118, 0.106 ], - "hex": "#1f1e1b" + "components": [ 0.125, 0.118, 0.106 ], + "hex": "#201e1b" } }, "bgFillInverted1": { @@ -551,8 +551,8 @@ "fgFillInverted": { "$value": { "colorSpace": "srgb", - "components": [ 0.961, 0.937, 0.898 ], - "hex": "#f5efe5" + "components": [ 0.965, 0.937, 0.898 ], + "hex": "#f6efe5" } }, "surface1": { @@ -565,8 +565,8 @@ "fgSurface4": { "$value": { "colorSpace": "srgb", - "components": [ 0.396, 0.298, 0.098 ], - "hex": "#654c19" + "components": [ 0.396, 0.298, 0.102 ], + "hex": "#654c1a" } }, "fgSurface5": { @@ -602,22 +602,22 @@ "surface2": { "$value": { "colorSpace": "srgb", - "components": [ 1, 0.965, 0.961 ], - "hex": "#fff6f5" + "components": [ 0.973, 0.973, 0.973 ], + "hex": "#f8f8f8" } }, "surface6": { "$value": { "colorSpace": "srgb", - "components": [ 0.949, 0.788, 0.765 ], - "hex": "#f2c9c3" + "components": [ 0.969, 0.784, 0.757 ], + "hex": "#f7c8c1" } }, "surface5": { "$value": { "colorSpace": "srgb", - "components": [ 0.961, 0.851, 0.835 ], - "hex": "#f5d9d5" + "components": [ 0.973, 0.847, 0.827 ], + "hex": "#f8d8d3" } }, "surface4": { @@ -637,8 +637,8 @@ "fgSurface3": { "$value": { "colorSpace": "srgb", - "components": [ 0.847, 0.102, 0.102 ], - "hex": "#d81a1a" + "components": [ 0.843, 0.102, 0.102 ], + "hex": "#d71a1a" } }, "fgSurface2": { @@ -672,15 +672,15 @@ "stroke1": { "$value": { "colorSpace": "srgb", - "components": [ 0.878, 0.686, 0.655 ], - "hex": "#e0afa7" + "components": [ 0.875, 0.686, 0.659 ], + "hex": "#dfafa8" } }, "bgFillInverted2": { "$value": { "colorSpace": "srgb", - "components": [ 0.137, 0.11, 0.106 ], - "hex": "#231c1b" + "components": [ 0.141, 0.11, 0.106 ], + "hex": "#241c1b" } }, "bgFillInverted1": { @@ -693,15 +693,15 @@ "fgFillInverted": { "$value": { "colorSpace": "srgb", - "components": [ 0.949, 0.937, 0.937 ], - "hex": "#f2efef" + "components": [ 0.953, 0.937, 0.937 ], + "hex": "#f3efef" } }, "surface1": { "$value": { "colorSpace": "srgb", - "components": [ 0.992, 0.929, 0.918 ], - "hex": "#fdedea" + "components": [ 0.992, 0.925, 0.914 ], + "hex": "#fdece9" } }, "fgSurface4": { @@ -886,29 +886,29 @@ "surface2": { "$value": { "colorSpace": "srgb", - "components": [ 1, 0.976, 0.792 ], - "hex": "#fff9ca" + "components": [ 0.973, 0.973, 0.973 ], + "hex": "#f8f8f8" } }, "surface6": { "$value": { "colorSpace": "srgb", - "components": [ 0.91, 0.82, 0.439 ], - "hex": "#e8d170" + "components": [ 0.918, 0.824, 0.439 ], + "hex": "#ead270" } }, "surface5": { "$value": { "colorSpace": "srgb", - "components": [ 0.953, 0.871, 0.525 ], - "hex": "#f3de86" + "components": [ 0.961, 0.875, 0.529 ], + "hex": "#f5df87" } }, "surface4": { "$value": { "colorSpace": "srgb", - "components": [ 1, 0.918, 0.588 ], - "hex": "#ffea96" + "components": [ 1, 0.925, 0.569 ], + "hex": "#ffec91" } }, "surface3": { @@ -935,29 +935,29 @@ "stroke3": { "$value": { "colorSpace": "srgb", - "components": [ 0.514, 0.416, 0 ], - "hex": "#836a00" + "components": [ 0.514, 0.42, 0 ], + "hex": "#836b00" } }, "stroke4": { "$value": { "colorSpace": "srgb", - "components": [ 0.388, 0.31, 0 ], - "hex": "#634f00" + "components": [ 0.388, 0.314, 0 ], + "hex": "#635000" } }, "stroke2": { "$value": { "colorSpace": "srgb", - "components": [ 0.745, 0.694, 0.486 ], - "hex": "#beb17c" + "components": [ 0.749, 0.694, 0.482 ], + "hex": "#bfb17b" } }, "stroke1": { "$value": { "colorSpace": "srgb", - "components": [ 0.816, 0.761, 0.545 ], - "hex": "#d0c28b" + "components": [ 0.816, 0.765, 0.545 ], + "hex": "#d0c38b" } }, "bgFillInverted2": { @@ -977,22 +977,22 @@ "fgFillInverted": { "$value": { "colorSpace": "srgb", - "components": [ 0.98, 0.945, 0.788 ], - "hex": "#faf1c9" + "components": [ 0.996, 0.945, 0.733 ], + "hex": "#fef1bb" } }, "surface1": { "$value": { "colorSpace": "srgb", - "components": [ 1, 0.937, 0.663 ], - "hex": "#ffefa9" + "components": [ 1, 0.949, 0.592 ], + "hex": "#fff297" } }, "fgSurface4": { "$value": { "colorSpace": "srgb", - "components": [ 0.361, 0.31, 0.086 ], - "hex": "#5c4f16" + "components": [ 0.365, 0.314, 0.086 ], + "hex": "#5d5016" } }, "fgSurface5": { @@ -1064,6 +1064,18 @@ } }, "interactive": { + "neutral": { + "$value": "{wpds-color.primitive.bg.surface3}", + "$description": "Solid background color for neutral interactive controls, such as input fields and unchecked checkboxes." + }, + "neutral-active": { + "$value": "{wpds-color.primitive.bg.surface3}", + "$description": "Solid background color for neutral interactive controls that are hovered, focused, or active." + }, + "neutral-disabled": { + "$value": "{wpds-color.primitive.bg.surface3}", + "$description": "Solid background color for neutral interactive controls in their disabled state." + }, "neutral-strong": { "$value": "{wpds-color.primitive.bg.bgFillInverted1}", "$description": "Background color for interactive elements with neutral tone and strong emphasis." @@ -1250,7 +1262,7 @@ "$description": "Foreground color for interactive elements with neutral tone and normal emphasis." }, "neutral-active": { - "$value": "{wpds-color.primitive.bg.fgSurface5}", + "$value": "{wpds-color.primitive.bg.fgSurface4}", "$description": "Foreground color for interactive elements with neutral tone and normal emphasis that are hovered, focused, or active." }, "neutral-disabled": { @@ -1274,7 +1286,7 @@ "$description": "Foreground color for interactive elements with neutral tone and weak emphasis." }, "neutral-weak-active": { - "$value": "{wpds-color.primitive.bg.fgSurface5}", + "$value": "{wpds-color.primitive.bg.fgSurface4}", "$description": "Foreground color for interactive elements with neutral tone and weak emphasis that are hovered, focused, or active." }, "neutral-weak-disabled": { @@ -1282,11 +1294,11 @@ "$description": "Foreground color for interactive elements with neutral tone and weak emphasis, in their disabled state." }, "brand": { - "$value": "{wpds-color.primitive.primary.fgSurface4}", + "$value": "{wpds-color.primitive.primary.fgSurface3}", "$description": "Foreground color for interactive elements with brand tone and normal emphasis." }, "brand-active": { - "$value": "{wpds-color.primitive.primary.fgSurface5}", + "$value": "{wpds-color.primitive.primary.fgSurface4}", "$description": "Foreground color for interactive elements with brand tone and normal emphasis that are hovered, focused, or active." }, "brand-disabled": { @@ -1306,11 +1318,11 @@ "$description": "Foreground color for interactive elements with brand tone and strong emphasis, in their disabled state." }, "error": { - "$value": "{wpds-color.primitive.error.fgSurface4}", + "$value": "{wpds-color.primitive.error.fgSurface3}", "$description": "Foreground color for interactive elements with error tone and normal emphasis." }, "error-active": { - "$value": "{wpds-color.primitive.error.fgSurface5}", + "$value": "{wpds-color.primitive.error.fgSurface4}", "$description": "Foreground color for interactive elements with error tone and normal emphasis that are hovered, focused, or active." }, "error-disabled": { From be0e96ead2f57bce6abea1aee8b81c129237b2a6 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Mon, 7 Sep 2026 11:54:58 +0200 Subject: [PATCH 22/44] Theme: Align ramp tests with current tokens --- packages/theme/src/test/theme-provider.browser.test.tsx | 1 - .../src/test/use-theme-provider-styles.jsdom.test.tsx | 9 +++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/theme/src/test/theme-provider.browser.test.tsx b/packages/theme/src/test/theme-provider.browser.test.tsx index f5949109b26fe6..64525185233e6e 100644 --- a/packages/theme/src/test/theme-provider.browser.test.tsx +++ b/packages/theme/src/test/theme-provider.browser.test.tsx @@ -116,7 +116,6 @@ describe( 'ThemeProvider', () => { expect( activeForeground ).toBe( readProp( provider, FOREGROUND_NEUTRAL_WEAK_ACTIVE ) ); - expect( restingForeground ).not.toBe( activeForeground ); } ); it( 'does not define color tokens if neither customized nor inherited', async () => { diff --git a/packages/theme/src/test/use-theme-provider-styles.jsdom.test.tsx b/packages/theme/src/test/use-theme-provider-styles.jsdom.test.tsx index 966563f1c095ed..3c9b71c944cb20 100644 --- a/packages/theme/src/test/use-theme-provider-styles.jsdom.test.tsx +++ b/packages/theme/src/test/use-theme-provider-styles.jsdom.test.tsx @@ -129,20 +129,21 @@ describe( 'useThemeProviderStyles', () => { DEFAULT_SEED_COLORS.info, ] ) { rerender( { primary } ); - const full = buildAccentRamp( + const interactive = buildAccentRamp( primary, - buildBgRamp( background ) + buildBgRamp( background ), + 'interactive' ); expect( result.current.themeProviderStyles[ '--wpds-color-foreground-interactive-brand-active' ] - ).toBe( full.ramp.fgSurface5 ); + ).toBe( interactive.ramp.fgSurface4 ); expect( result.current.themeProviderStyles[ '--wpds-color-stroke-interactive-brand-active' ] - ).toBe( full.ramp.stroke4 ); + ).toBe( interactive.ramp.stroke4 ); expect( result.current.colorWarnings ).toEqual( [] ); } } ); From d23dcbca6ce8da605f70f44598dbe36cc1769a01 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Mon, 7 Sep 2026 12:56:57 +0200 Subject: [PATCH 23/44] Theme: Move runtime ramp pruning to follow-up --- packages/theme/CHANGELOG.md | 1 - packages/theme/src/color-ramps/README.md | 2 - packages/theme/src/color-ramps/index.ts | 25 +-- .../color-ramps/lib/build-foreground-scale.ts | 148 +++++++----------- .../color-ramps/lib/build-perceptual-steps.ts | 100 +++++------- packages/theme/src/color-ramps/lib/index.ts | 53 ++----- packages/theme/src/color-ramps/lib/types.ts | 10 -- .../test/build-foreground-scale.test.ts | 135 ---------------- .../test/color-space-registration.test.ts | 19 +-- .../src/color-ramps/test/ramp-purpose.test.ts | 103 +----------- .../use-theme-provider-styles.jsdom.test.tsx | 41 +---- .../src/theme-provider-color-warnings.ts | 11 +- .../theme/src/use-theme-provider-styles.ts | 16 +- 13 files changed, 124 insertions(+), 540 deletions(-) delete mode 100644 packages/theme/src/color-ramps/test/build-foreground-scale.test.ts diff --git a/packages/theme/CHANGELOG.md b/packages/theme/CHANGELOG.md index 16068a0a5fd369..c6ff344ff6bc58 100644 --- a/packages/theme/CHANGELOG.md +++ b/packages/theme/CHANGELOG.md @@ -76,7 +76,6 @@ - Generate the default ramps before derived token artifacts so one build uses the current ramp algorithm throughout. ([#82525](https://github.com/WordPress/gutenberg/pull/82525)) - Cache relative luminance calculations used by color-ramp contrast checks. ([#82445](https://github.com/WordPress/gutenberg/pull/82445)) - Register color spaces only when calculating colors, avoiding module-import side effects. ([#82294](https://github.com/WordPress/gutenberg/pull/82294)) -- Skip unused ramp outputs and redundant base foreground solves, and calculate status interaction colors only when contrast correction needs them. Preserve semantic colors and contrast checks. ([#82294](https://github.com/WordPress/gutenberg/pull/82294)) - Generate the default ramps before derived token artifacts so one build uses the current ramp algorithm throughout. ([#82294](https://github.com/WordPress/gutenberg/pull/82294)) - Enforce NodeNext module resolution in the build project so future declaration imports are checked against the package's published ESM resolution rules. ([#82088](https://github.com/WordPress/gutenberg/pull/82088)) - Update the `@types/node` development dependency to v24, matching the Node.js version the repository builds and tests against ([#82616](https://github.com/WordPress/gutenberg/pull/82616)). diff --git a/packages/theme/src/color-ramps/README.md b/packages/theme/src/color-ramps/README.md index 12f17e4de9edb4..9c454e4efb9095 100644 --- a/packages/theme/src/color-ramps/README.md +++ b/packages/theme/src/color-ramps/README.md @@ -31,8 +31,6 @@ Perceptual spacing is a preference; configured WCAG contrast floors take priorit Surface foregrounds use FGS2 for disabled controls, FGS3 for weak emphasis, FGS4 for normal content and resting controls, and FGS5 for interaction states. FGS5 can approach black or white at the expense of chroma. FGS1 is no longer generated. Strong fills use separate foregrounds. See [`tokens/color.json`](../../tokens/color.json) for exact mappings. -Runtime accent profiles skip output work that semantic tokens do not use: `interactive` for primary/error and `status` for the other accents. They retain dependencies and spacing budgets that affect visible colors. Generation and diagnostics use `full` ramps. - ## Checks and generated files Ramp warnings report unmet step constraints. [`theme-provider-color-warnings.ts`](../theme-provider-color-warnings.ts) also checks listed semantic text/background pairs. The separate `checkAccessibleCombinations` diagnostic checks a broader matrix, including cross-ramp pairs that semantic tokens may not use. Neither warning collector repairs colors or proves every possible pairing accessible. diff --git a/packages/theme/src/color-ramps/index.ts b/packages/theme/src/color-ramps/index.ts index a024a963addd32..ba6b162a8fe5fc 100644 --- a/packages/theme/src/color-ramps/index.ts +++ b/packages/theme/src/color-ramps/index.ts @@ -13,8 +13,6 @@ import { } from './lib/utils.ts'; import { BG_RAMP_CONFIG, ACCENT_RAMP_CONFIG } from './lib/ramp-configs.ts'; import type { - AccentRampPurpose, - AccentRampResult, RampResult as InternalRampResult, RampDirection, Ramp, @@ -155,25 +153,13 @@ function getBgRampInfo( ramp: InternalRampResult ): { * Build an accent ramp. When a background is supplied, inherit its direction * and pin accent SF2 to a bounded version of the background's SF2 lightness. * - * @param seed Opaque sRGB seed for the accent. - * @param bgRamp Completed background ramp on which the accent appears. - * @param purpose Internal profile selected by the caller's semantic usage. + * @param seed Opaque sRGB seed for the accent. + * @param bgRamp Completed background ramp on which the accent appears. */ export function buildAccentRamp( seed: string, - bgRamp?: InternalRampResult, - purpose?: 'full' -): InternalRampResult; -export function buildAccentRamp( - seed: string, - bgRamp: InternalRampResult | undefined, - purpose: AccentRampPurpose -): AccentRampResult; -export function buildAccentRamp( - seed: string, - bgRamp?: InternalRampResult, - purpose: AccentRampPurpose = 'full' -): AccentRampResult { + bgRamp?: InternalRampResult +): InternalRampResult { if ( typeof seed !== 'string' || seed.trim() === '' ) { throw new Error( 'Seed color must be a non-empty string' ); } @@ -181,7 +167,6 @@ export function buildAccentRamp( const bgRampInfo = bgRamp ? getBgRampInfo( bgRamp ) : undefined; return buildRamp( seed, ACCENT_RAMP_CONFIG, { ...bgRampInfo, - purpose, } ); } @@ -192,7 +177,7 @@ export function buildAccentRamp( * * @param params Full ramps to inspect. * @param params.bgRamp Background ramp. - * @param params.accentRamps Accent ramps; purpose-pruned outputs are not supported. + * @param params.accentRamps Accent ramps. */ export function checkAccessibleCombinations( { bgRamp, diff --git a/packages/theme/src/color-ramps/lib/build-foreground-scale.ts b/packages/theme/src/color-ramps/lib/build-foreground-scale.ts index a425251cce7fc5..f71bbe3d4484ea 100644 --- a/packages/theme/src/color-ramps/lib/build-foreground-scale.ts +++ b/packages/theme/src/color-ramps/lib/build-foreground-scale.ts @@ -12,8 +12,6 @@ import { clampToGamut, getColorString, getContrast } from './color-utils.ts'; import { UNIVERSAL_CONTRAST_TOPUP } from './constants.ts'; import { taperChroma } from './taper-chroma.ts'; import type { - AccentRampResult, - BaseRamp, BaseRampResult, Ramp, ForegroundScaleConfig, @@ -273,20 +271,20 @@ function findColorAtPerceptualContrast( { * @param options Serialization inputs. * @param options.color Foreground before serialization. * @param options.getColorAtLightness Color path indexed by OKLCH lightness. - * @param options.getStrongColor Resolve the strongest allowed fallback. + * @param options.strongColor Strongest allowed fallback. * @param options.references Background colors to check together. * @param options.target Unpadded WCAG contrast floor. */ function serializeColorMeetingContrast( { color, getColorAtLightness, - getStrongColor, + strongColor, references, target, }: { color: PlainColorObject; getColorAtLightness: GetColorForLightness; - getStrongColor: () => PlainColorObject; + strongColor: PlainColorObject; references: readonly string[]; target: number; } ) { @@ -295,7 +293,6 @@ function serializeColorMeetingContrast( { return serializedColor; } - const strongColor = getStrongColor(); const colorLightness = get( color, [ OKLCH, 'l' ] ); const strongLightness = get( strongColor, [ OKLCH, 'l' ] ); const direction = Math.sign( strongLightness - colorLightness ); @@ -398,29 +395,15 @@ function serializeColorMeetingPerceptualInterval( { * the intermediate steps within the available contrast range. WCAG floors * take priority across the configured surfaces; unresolved failures warn. * - * @param ramp Ramp whose foreground steps will be rebuilt. - * @param backgroundRamp Background ramp on which the colors will appear. - * @param config Foreground scale anchors, targets, and constraints. - * @param includeInteractionState Whether FGS5 is needed as an output color. + * @param ramp Ramp whose foreground steps will be rebuilt. + * @param backgroundRamp Background ramp on which the colors will appear. + * @param config Foreground scale anchors, targets, and constraints. */ export function buildForegroundScale( ramp: BaseRampResult, backgroundRamp: BaseRampResult, - config: ForegroundScaleConfig, - includeInteractionState?: true -): RampResult; -export function buildForegroundScale( - ramp: BaseRampResult, - backgroundRamp: BaseRampResult, - config: ForegroundScaleConfig, - includeInteractionState: boolean -): AccentRampResult; -export function buildForegroundScale( - ramp: BaseRampResult, - backgroundRamp: BaseRampResult, - config: ForegroundScaleConfig, - includeInteractionState = true -): AccentRampResult { + config: ForegroundScaleConfig +): RampResult { // APCA resolves sRGB by name, even for color objects. ColorSpace.register( sRGB ); const seed = clampToGamut( ramp.ramp[ config.seed ] ); @@ -563,62 +546,48 @@ export function buildForegroundScale( target: steps.fgSurface4.contrast.target, } ); } - // Status ramps need the spacing budget above, but only need this color as - // a bound when serialization requires correction. Reuse it across corrections. - let strongColor: PlainColorObject | undefined; - function getStrongColor(): PlainColorObject { - if ( strongColor ) { - return strongColor; - } - - const normalContrast = getPerceptualContrastMagnitude( - displayBackground, - normalColor - ); - const requestedStrongContrast = Math.min( - maximumStrongContrast, - Math.max( - normalContrast + normalToActiveTarget, - preferredActiveContrast - ) - ); - strongColor = findColorAtPerceptualContrast( { - background: displayBackground, + const normalContrast = getPerceptualContrastMagnitude( + displayBackground, + normalColor + ); + const requestedStrongContrast = Math.min( + maximumStrongContrast, + Math.max( + normalContrast + normalToActiveTarget, + preferredActiveContrast + ) + ); + let strongColor = findColorAtPerceptualContrast( { + background: displayBackground, + getColorAtLightness, + weakColor: normalColor, + strongColor: strongEndpoint, + target: requestedStrongContrast, + } ); + if ( + ! meetsContrastTarget( + strongColor, + references.fgSurface5, + strongStep.contrast.target + ) + ) { + strongColor = findColorAtContrastTarget( { getColorAtLightness, - weakColor: normalColor, + weakColor: strongColor, strongColor: strongEndpoint, - target: requestedStrongContrast, + references: references.fgSurface5, + target: strongStep.contrast.target, } ); - if ( - ! meetsContrastTarget( - strongColor, - references.fgSurface5, - strongStep.contrast.target - ) - ) { - strongColor = findColorAtContrastTarget( { - getColorAtLightness, - weakColor: strongColor, - strongColor: strongEndpoint, - references: references.fgSurface5, - target: strongStep.contrast.target, - } ); - } - return strongColor; } colors.set( 'fgSurface3', fgSurface3 ); colors.set( 'fgSurface4', normalColor ); - if ( includeInteractionState ) { - colors.set( 'fgSurface5', getStrongColor() ); - } + colors.set( 'fgSurface5', strongColor ); - const outputSteps = includeInteractionState - ? config.steps - : config.steps.filter( ( step ) => step.name !== 'fgSurface5' ); - function serializeStep( step: ForegroundScaleConfig[ 'steps' ][ number ] ) { + const nextRamp = { ...ramp.ramp } as Ramp; + for ( const step of config.steps ) { const color = colors.get( step.name )!; - return serializeColorMeetingContrast( { + nextRamp[ step.name ] = serializeColorMeetingContrast( { color, getColorAtLightness: step.preserveAnchor && step !== strongStep @@ -628,32 +597,21 @@ export function buildForegroundScale( lightness ) : getColorAtLightness, - getStrongColor, + strongColor, references: references[ step.name ], target: step.contrast.target, } ); } - const nextRamp: BaseRamp & { fgSurface4: string; fgSurface5?: string } = { - ...ramp.ramp, - fgSurface4: serializeStep( steps.fgSurface4 ), - }; - for ( const step of outputSteps ) { - if ( step.name !== 'fgSurface4' ) { - nextRamp[ step.name ] = serializeStep( step ); - } - } - if ( includeInteractionState ) { - nextRamp.fgSurface5 = serializeColorMeetingPerceptualInterval( { - background: displayBackground, - normalColor: nextRamp.fgSurface4, - strongColor: nextRamp.fgSurface5!, - strongEndpoint, - getColorAtLightness, - references: references.fgSurface5, - wcagTarget: strongStep.contrast.target, - perceptualTarget: normalToActive, - } ); - } + nextRamp.fgSurface5 = serializeColorMeetingPerceptualInterval( { + background: displayBackground, + normalColor: nextRamp.fgSurface4, + strongColor: nextRamp.fgSurface5, + strongEndpoint, + getColorAtLightness, + references: references.fgSurface5, + wcagTarget: strongStep.contrast.target, + perceptualTarget: normalToActive, + } ); const foregroundStepNames = new Set< keyof Ramp >( config.steps.map( ( { name } ) => name ) @@ -661,7 +619,7 @@ export function buildForegroundScale( const warnings = ( ramp.warnings ?? [] ).filter( ( step ) => ! foregroundStepNames.has( step ) ); - for ( const step of outputSteps ) { + for ( const step of config.steps ) { if ( ! meetsContrastFloor( nextRamp[ step.name ]!, diff --git a/packages/theme/src/color-ramps/lib/build-perceptual-steps.ts b/packages/theme/src/color-ramps/lib/build-perceptual-steps.ts index 868c30b44aff92..1f51a2d9286949 100644 --- a/packages/theme/src/color-ramps/lib/build-perceptual-steps.ts +++ b/packages/theme/src/color-ramps/lib/build-perceptual-steps.ts @@ -12,7 +12,7 @@ import { } from 'colorjs.io/fn'; import { clampToGamut, getColorString, getContrast } from './color-utils.ts'; import { UNIVERSAL_CONTRAST_TOPUP } from './constants.ts'; -import type { AccentRampPurpose, BaseRamp, BaseRampResult } from './types.ts'; +import type { BaseRamp, BaseRampResult } from './types.ts'; import { solveWithBisect } from './utils.ts'; type GetColorAtLightness = ( lightness: number ) => PlainColorObject; @@ -252,12 +252,11 @@ function findColorMeetingWcag( { /** * Balance SF1/SF3 around SF2 in OKLr lightness, clipping each endpoint * independently. Space SF4-SF6 beyond the elevation steps in the ramp direction - * where room remains. SF6 still sets that range when its output is not needed. + * where room remains. * - * @param ramp Base ramp with SF1-SF3 already ordered by elevation. - * @param purpose Which surface outputs need reconstruction. + * @param ramp Base ramp with SF1-SF3 already ordered by elevation. */ -function rebuildSurfaces( ramp: BaseRampResult, purpose: AccentRampPurpose ) { +function rebuildSurfaces( ramp: BaseRampResult ) { const nextRamp = { ...ramp.ramp }; const surface1Lightness = getPerceptualLightness( ramp.ramp.surface1 ); const surface2Lightness = getPerceptualLightness( ramp.ramp.surface2 ); @@ -331,10 +330,6 @@ function rebuildSurfaces( ramp: BaseRampResult, purpose: AccentRampPurpose ) { } as const; for ( const [ step, lightness ] of Object.entries( targetLightnesses ) ) { - // Keep SF6's spacing anchor above, but avoid its unused output conversion. - if ( purpose !== 'full' && step === 'surface6' ) { - continue; - } nextRamp[ step as keyof typeof targetLightnesses ] = getColorForPerceptualLightness( ramp.ramp[ step as keyof typeof targetLightnesses ], @@ -362,18 +357,16 @@ function getStrokeReferences( } /** - * Repair ST3 against the rebuilt surfaces, then reposition ST2 and ST4 only - * when their purpose needs them. Preserve authored stroke differences where - * possible; WCAG correction takes precedence over perceptual spacing. + * Repair ST3 against the rebuilt surfaces, then reposition ST2 and ST4. + * Preserve authored stroke differences where possible; WCAG correction takes + * precedence over perceptual spacing. * * @param ramp Ramp with its surfaces already rebuilt. * @param backgroundRamp Additional surfaces behind accent strokes. - * @param purpose Which stroke outputs need reconstruction. */ function rebuildStrokes( ramp: BaseRampResult, - backgroundRamp: BaseRampResult, - purpose: AccentRampPurpose + backgroundRamp: BaseRampResult ) { const nextRamp = { ...ramp.ramp }; const references = getStrokeReferences( nextRamp, backgroundRamp ); @@ -384,41 +377,36 @@ function rebuildStrokes( } ); const stroke3Lightness = getPerceptualLightness( nextRamp.stroke3 ); - if ( purpose === 'full' ) { - const stroke1Reference = to( clampToGamut( nextRamp.stroke1 ), OKLab ); - const stroke1To3Difference = deltaEOK2( - stroke1Reference, - nextRamp.stroke3 - ); - const authoredStroke2Difference = deltaEOK2( - stroke1Reference, - ramp.ramp.stroke2 - ); - const stroke1Lightness = getPerceptualLightness( nextRamp.stroke1 ); - const getStroke2AtLightness = - createAnchoredColorForStepAtPerceptualLightness( { - ramp: nextRamp, - step: 'stroke2', - anchor: nextRamp.stroke1, - startLightness: stroke1Lightness, - endLightness: stroke3Lightness, - } ); - nextRamp.stroke2 = getColorString( - findColorAtDeltaE( { - reference: stroke1Reference, - getColorAtLightness: getStroke2AtLightness, - startLightness: stroke1Lightness, - endLightness: stroke3Lightness, - target: Math.min( - authoredStroke2Difference, - stroke1To3Difference * 0.95 - ), - } ) - ); - } - if ( purpose === 'status' ) { - return nextRamp; - } + const stroke1Reference = to( clampToGamut( nextRamp.stroke1 ), OKLab ); + const stroke1To3Difference = deltaEOK2( + stroke1Reference, + nextRamp.stroke3 + ); + const authoredStroke2Difference = deltaEOK2( + stroke1Reference, + ramp.ramp.stroke2 + ); + const stroke1Lightness = getPerceptualLightness( nextRamp.stroke1 ); + const getStroke2AtLightness = + createAnchoredColorForStepAtPerceptualLightness( { + ramp: nextRamp, + step: 'stroke2', + anchor: nextRamp.stroke1, + startLightness: stroke1Lightness, + endLightness: stroke3Lightness, + } ); + nextRamp.stroke2 = getColorString( + findColorAtDeltaE( { + reference: stroke1Reference, + getColorAtLightness: getStroke2AtLightness, + startLightness: stroke1Lightness, + endLightness: stroke3Lightness, + target: Math.min( + authoredStroke2Difference, + stroke1To3Difference * 0.95 + ), + } ) + ); const stroke3Contrast = Math.abs( contrastAPCA( nextRamp.surface3, nextRamp.stroke3 ) @@ -475,25 +463,19 @@ function rebuildStrokes( * * @param ramp Base ramp to adjust. * @param backgroundRamp Background surfaces used by accent strokes. - * @param purpose Which accent steps need their final reconstruction. */ export function buildPerceptualSteps( ramp: BaseRampResult, - backgroundRamp?: BaseRampResult, - purpose: AccentRampPurpose = 'full' + backgroundRamp?: BaseRampResult ): BaseRampResult { ColorSpace.register( sRGB ); const surfaceResult = { ...ramp, - ramp: rebuildSurfaces( ramp, purpose ), + ramp: rebuildSurfaces( ramp ), }; return { ...surfaceResult, - ramp: rebuildStrokes( - surfaceResult, - backgroundRamp ?? surfaceResult, - purpose - ), + ramp: rebuildStrokes( surfaceResult, backgroundRamp ?? surfaceResult ), }; } diff --git a/packages/theme/src/color-ramps/lib/index.ts b/packages/theme/src/color-ramps/lib/index.ts index 4fbf8bb2e8f605..c86f7ad23f543d 100644 --- a/packages/theme/src/color-ramps/lib/index.ts +++ b/packages/theme/src/color-ramps/lib/index.ts @@ -16,8 +16,6 @@ import { solveWithBisect, } from './utils.ts'; import type { - AccentRampPurpose, - AccentRampResult, BaseRamp, BaseRampStep, FollowDirection, @@ -218,30 +216,21 @@ type BuildRampOptions = { pinLightness?: { stepName: BaseRampStep; value: number }; backgroundRamp?: RampResult; rescaleToFitContrastTargets?: boolean; - purpose?: AccentRampPurpose; }; /** * Solve base constraints, rebuild surfaces and strokes, then position the * foreground scale. Seed lightness may shift to make the base constraints fit. - * Purpose-specific outputs omit unused steps only after preserving their - * constraint dependencies and spacing contributions. * - * @param seedArg Original opaque sRGB seed string. - * @param config Base constraints and foreground policy. - * @param options Direction, seed adjustment, background, and output profile. + * @param seedArg Original opaque sRGB seed string. + * @param config Base constraints and foreground policy. + * @param options Direction, seed adjustment, and background. + * @param options.mainDirection Main ramp direction override. + * @param options.pinLightness Optional pinned step lightness. + * @param options.backgroundRamp Background behind an accent ramp. + * @param options.rescaleToFitContrastTargets Whether seed rescaling is allowed. * @return Generated colors, direction, and any remaining ramp warnings. */ -export function buildRamp( - seedArg: string, - config: RampConfig, - options?: BuildRampOptions & { purpose?: 'full' } -): RampResult; -export function buildRamp( - seedArg: string, - config: RampConfig, - options: BuildRampOptions -): AccentRampResult; export function buildRamp( seedArg: string, config: RampConfig, @@ -378,34 +367,12 @@ export function buildRamp( warnings: bestWarnings, direction: mainDir, }, - backgroundRamp, - purpose + backgroundRamp ); - if ( purpose === 'full' ) { - return buildForegroundScale( - rampResult, - backgroundRamp ?? rampResult, - config.foregroundScale - ); - } - const foregroundResult = buildForegroundScale( + return buildForegroundScale( rampResult, backgroundRamp ?? rampResult, - config.foregroundScale, - purpose !== 'status' + config.foregroundScale ); - // Do not expose base colors whose final reconstruction was intentionally - // skipped. Their base constraints and spacing contributions remain intact. - const pruned: AccentRampResult = { - ...foregroundResult, - ramp: { ...foregroundResult.ramp }, - }; - delete pruned.ramp.surface6; - delete pruned.ramp.stroke2; - if ( purpose === 'status' ) { - delete pruned.ramp.stroke4; - delete pruned.ramp.fgSurface5; - } - return pruned; } diff --git a/packages/theme/src/color-ramps/lib/types.ts b/packages/theme/src/color-ramps/lib/types.ts index 2118cc0385a093..91952c6eaf344b 100644 --- a/packages/theme/src/color-ramps/lib/types.ts +++ b/packages/theme/src/color-ramps/lib/types.ts @@ -32,9 +32,6 @@ export type RampDirection = 'lighter' | 'darker'; // FGS4/FGS5 are produced by the foreground pass, not the base constraint solve. export type BaseRamp = Omit< Ramp, 'fgSurface4' | 'fgSurface5' >; export type BaseRampStep = keyof BaseRamp; -// Internal calculation profiles. ThemeProvider chooses from semantic usage; -// full ramps remain available for generation and diagnostics. -export type AccentRampPurpose = 'full' | 'interactive' | 'status'; export type FollowDirection = 'main' | 'opposite' | 'best' | RampDirection; export type ContrastRequirement = { /** The reference color against which to calculate the contrast */ @@ -141,10 +138,3 @@ export type RampResult< Colors = Ramp > = { }; export type BaseRampResult = RampResult< BaseRamp >; - -export type AccentRampResult = RampResult< - Omit< Ramp, 'surface6' | 'stroke2' | 'stroke4' | 'fgSurface5' > & - Partial< - Pick< Ramp, 'surface6' | 'stroke2' | 'stroke4' | 'fgSurface5' > - > ->; diff --git a/packages/theme/src/color-ramps/test/build-foreground-scale.test.ts b/packages/theme/src/color-ramps/test/build-foreground-scale.test.ts deleted file mode 100644 index 33a8171231caa2..00000000000000 --- a/packages/theme/src/color-ramps/test/build-foreground-scale.test.ts +++ /dev/null @@ -1,135 +0,0 @@ -import { afterEach, describe, expect, it, vi } from 'vitest'; -import { buildAccentRamp, buildBgRamp } from '..'; -import { buildForegroundScale } from '../lib/build-foreground-scale'; -import * as colorUtils from '../lib/color-utils'; -import { ACCENT_RAMP_CONFIG } from '../lib/ramp-configs'; - -function createFixture( backgroundSeed: string, accentSeed = '#0090ff' ) { - const background = buildBgRamp( backgroundSeed ); - const full = buildAccentRamp( accentSeed, background ); - const { fgSurface4, fgSurface5, ...baseColors } = full.ramp; - const scale = ACCENT_RAMP_CONFIG.foregroundScale; - const strongStep = scale.steps.find( - ( step ) => step.name === 'fgSurface5' - )!; - // Reading this target marks the strong-color calculation, without replacing - // any of the color math or exposing the private solver to the test. - const readStrongTarget = vi.fn( () => strongStep.contrast.target ); - const config = { - ...scale, - steps: scale.steps.map( ( step ) => - step === strongStep - ? { - ...step, - contrast: { - ...step.contrast, - get target() { - return readStrongTarget(); - }, - }, - } - : step - ), - }; - return { - background, - base: { ...full, ramp: baseColors }, - config, - readStrongTarget, - expected: { fgSurface4, fgSurface5 }, - }; -} - -describe( 'status foreground calculation', () => { - afterEach( () => { - vi.restoreAllMocks(); - } ); - - it( 'skips the interaction color when retained colors serialize above their contrast floors', () => { - const { background, base, config, readStrongTarget, expected } = - createFixture( '#fcfcfc' ); - - const result = buildForegroundScale( base, background, config, false ); - - expect( readStrongTarget ).not.toHaveBeenCalled(); - expect( result.ramp ).not.toHaveProperty( 'fgSurface5' ); - expect( result.ramp.fgSurface4 ).toBe( expected.fgSurface4 ); - expect( result.warnings ).toBeUndefined(); - } ); - - it( 'defers the interaction color until a rounded foreground needs the existing correction', () => { - const { background, base, config, readStrongTarget } = - createFixture( '#a4a4a4' ); - const serialize = colorUtils.getColorString; - const solvesBeforeCorrection: number[] = []; - vi.spyOn( colorUtils, 'getColorString' ).mockImplementation( - ( color ) => { - const serialized = serialize( color ); - if ( serialized === '#003767' ) { - solvesBeforeCorrection.push( - readStrongTarget.mock.calls.length - ); - } - return serialized; - } - ); - - const result = buildForegroundScale( base, background, config, false ); - - expect( solvesBeforeCorrection[ 0 ] ).toBe( 0 ); - expect( readStrongTarget ).toHaveBeenCalledTimes( 1 ); - // This is the exact pre-optimization correction for this rounding edge. - expect( result.ramp.fgSurface3 ).toBe( '#003667' ); - expect( result.ramp.fgSurface4 ).toBe( '#002b53' ); - expect( result.warnings ).toBeUndefined(); - for ( const ramp of [ background.ramp, result.ramp ] ) { - for ( const surface of [ - 'surface1', - 'surface2', - 'surface3', - ] as const ) { - expect( - colorUtils.getContrast( - ramp[ surface ], - result.ramp.fgSurface3 - ) - ).toBeGreaterThanOrEqual( 4.5 ); - } - } - } ); - - it( 'reuses one interaction-color calculation for multiple serialization corrections', () => { - const { background, base, config, readStrongTarget } = createFixture( - '#dfdfdf', - '#4ab866' - ); - const serialize = colorUtils.getColorString; - const solvesBeforeCorrection = new Map< string, number >(); - vi.spyOn( colorUtils, 'getColorString' ).mockImplementation( - ( color ) => { - const serialized = serialize( color ); - if ( - [ '#236032', '#286b39' ].includes( serialized ) && - ! solvesBeforeCorrection.has( serialized ) - ) { - solvesBeforeCorrection.set( - serialized, - readStrongTarget.mock.calls.length - ); - } - return serialized; - } - ); - - const result = buildForegroundScale( base, background, config, false ); - - expect( [ ...solvesBeforeCorrection ] ).toEqual( [ - [ '#236032', 0 ], - [ '#286b39', 1 ], - ] ); - expect( readStrongTarget ).toHaveBeenCalledTimes( 1 ); - expect( result.ramp.fgSurface3 ).toBe( '#286a39' ); - expect( result.ramp.fgSurface4 ).toBe( '#235f32' ); - expect( result.warnings ).toBeUndefined(); - } ); -} ); diff --git a/packages/theme/src/color-ramps/test/color-space-registration.test.ts b/packages/theme/src/color-ramps/test/color-space-registration.test.ts index c2bf6aa9b0d179..ae1fe43ed51275 100644 --- a/packages/theme/src/color-ramps/test/color-space-registration.test.ts +++ b/packages/theme/src/color-ramps/test/color-space-registration.test.ts @@ -74,17 +74,14 @@ describe( 'color-space registration', () => { expect( result.warnings ).toBeUndefined(); } ); - it.each( [ 'full', 'interactive', 'status' ] as const )( - 'builds %s accent ramps from a supplied background without prior registration', - async ( purpose ) => { - const { buildBgRamp, buildAccentRamp } = await import( '..' ); - const background = buildBgRamp( '#fcfcfc' ); - ColorSpace.registry = {}; + it( 'builds an accent ramp from a supplied background without prior registration', async () => { + const { buildBgRamp, buildAccentRamp } = await import( '..' ); + const background = buildBgRamp( '#fcfcfc' ); + ColorSpace.registry = {}; - const result = buildAccentRamp( '#3858e9', background, purpose ); + const result = buildAccentRamp( '#3858e9', background ); - expect( result.ramp.fgSurface4 ).toMatch( /^#[\da-f]{6}$/ ); - expect( result.warnings ).toBeUndefined(); - } - ); + expect( result.ramp.fgSurface4 ).toMatch( /^#[\da-f]{6}$/ ); + expect( result.warnings ).toBeUndefined(); + } ); } ); diff --git a/packages/theme/src/color-ramps/test/ramp-purpose.test.ts b/packages/theme/src/color-ramps/test/ramp-purpose.test.ts index 23221f326d1064..aa425bf3f7874d 100644 --- a/packages/theme/src/color-ramps/test/ramp-purpose.test.ts +++ b/packages/theme/src/color-ramps/test/ramp-purpose.test.ts @@ -1,17 +1,5 @@ import { describe, expect, it } from 'vitest'; -import { buildAccentRamp, buildBgRamp, DEFAULT_SEED_COLORS } from '..'; -import { getContrast } from '../lib/color-utils'; -import type { Ramp } from '../lib/types'; -import colorTokens from '../../prebuilt/ts/color-tokens'; - -const combinations = [ - { background: '#fcfcfc', primary: '#3858e9' }, - { background: '#1e1e1e', primary: '#3858e9' }, - { background: '#413256', primary: '#a3b745' }, - { background: '#777777', primary: '#d63638' }, - { background: '#fcfcfc', primary: '#ffd700' }, - { background: '#1e1e1e', primary: '#00ffff' }, -] as const; +import { buildAccentRamp, buildBgRamp } from '..'; describe( 'ramp purpose', () => { it( 'omits steps with no semantic consumer or downstream dependency', () => { @@ -24,93 +12,4 @@ describe( 'ramp purpose', () => { } } } ); - - it.each( combinations )( - 'preserves semantic colors for every accent purpose over $background', - ( { background, primary } ) => { - const backgroundRamp = buildBgRamp( background ); - const seeds = { - ...DEFAULT_SEED_COLORS, - primary, - }; - - for ( const [ role, seed ] of Object.entries( seeds ) ) { - if ( role === 'background' ) { - continue; - } - const purpose = - role === 'primary' || role === 'error' - ? 'interactive' - : 'status'; - const full = buildAccentRamp( seed, backgroundRamp ); - const result = buildAccentRamp( seed, backgroundRamp, purpose ); - const mappedSteps = Object.entries( colorTokens ) - .filter( ( [ key ] ) => key.startsWith( `${ role }-` ) ) - .map( - ( [ key ] ) => - key.slice( role.length + 1 ) as keyof Ramp - ); - - for ( const step of mappedSteps ) { - expect( result.ramp[ step ] ).toBe( full.ramp[ step ] ); - } - expect( result.warnings ).toEqual( full.warnings ); - expect( result.direction ).toBe( full.direction ); - expect( result.ramp ).not.toHaveProperty( 'stroke2' ); - expect( result.ramp ).not.toHaveProperty( 'surface6' ); - if ( purpose === 'status' ) { - expect( result.ramp ).not.toHaveProperty( 'stroke4' ); - expect( result.ramp ).not.toHaveProperty( 'fgSurface5' ); - } - } - } - ); - - it.each( combinations )( - 'preserves foreground and stroke contrast for each purpose over $background', - ( { background, primary } ) => { - const backgroundRamp = buildBgRamp( background ); - for ( const purpose of [ 'interactive', 'status' ] as const ) { - const result = buildAccentRamp( - primary, - backgroundRamp, - purpose - ); - for ( const referenceRamp of [ backgroundRamp, result ] ) { - for ( const surface of [ - 'surface1', - 'surface2', - 'surface3', - ] as const ) { - expect( - getContrast( - referenceRamp.ramp[ surface ], - result.ramp.stroke3 - ) - ).toBeGreaterThanOrEqual( 3 ); - expect( - getContrast( - referenceRamp.ramp[ surface ], - result.ramp.fgSurface3 - ) - ).toBeGreaterThanOrEqual( 4.5 ); - } - for ( const surface of [ - 'surface1', - 'surface2', - 'surface3', - 'surface4', - 'surface5', - ] as const ) { - expect( - getContrast( - referenceRamp.ramp[ surface ], - result.ramp.fgSurface4 - ) - ).toBeGreaterThanOrEqual( 4.5 ); - } - } - } - } - ); } ); diff --git a/packages/theme/src/test/use-theme-provider-styles.jsdom.test.tsx b/packages/theme/src/test/use-theme-provider-styles.jsdom.test.tsx index 3c9b71c944cb20..602239b62d5992 100644 --- a/packages/theme/src/test/use-theme-provider-styles.jsdom.test.tsx +++ b/packages/theme/src/test/use-theme-provider-styles.jsdom.test.tsx @@ -18,11 +18,7 @@ import { renderHook } from '@testing-library/react'; import { type ReactNode } from 'react'; import { ThemeProvider } from '../theme-provider'; import { useThemeProviderStyles } from '../use-theme-provider-styles'; -import { - buildAccentRamp, - buildBgRamp, - DEFAULT_SEED_COLORS, -} from '../color-ramps'; +import { DEFAULT_SEED_COLORS } from '../color-ramps'; describe( 'useThemeProviderStyles', () => { describe( 'resolvedSettings', () => { @@ -113,41 +109,6 @@ describe( 'useThemeProviderStyles', () => { } ); describe( 'color styles', () => { - it( 'keeps interactive colors when a brand seed was previously cached as a status color', () => { - const background = '#fafafa'; - const { result, rerender } = renderHook( - ( { primary } ) => - useThemeProviderStyles( { - color: { primary, background }, - } ), - { initialProps: { primary: '#abcdef' } } - ); - - for ( const primary of [ - DEFAULT_SEED_COLORS.info, - DEFAULT_SEED_COLORS.success, - DEFAULT_SEED_COLORS.info, - ] ) { - rerender( { primary } ); - const interactive = buildAccentRamp( - primary, - buildBgRamp( background ), - 'interactive' - ); - expect( - result.current.themeProviderStyles[ - '--wpds-color-foreground-interactive-brand-active' - ] - ).toBe( interactive.ramp.fgSurface4 ); - expect( - result.current.themeProviderStyles[ - '--wpds-color-stroke-interactive-brand-active' - ] - ).toBe( interactive.ramp.stroke4 ); - expect( result.current.colorWarnings ).toEqual( [] ); - } - } ); - it( 'omits color custom properties by default', () => { const { result } = renderHook( () => useThemeProviderStyles() ); diff --git a/packages/theme/src/theme-provider-color-warnings.ts b/packages/theme/src/theme-provider-color-warnings.ts index 2b69c9195fa045..5044a6ef4587bd 100644 --- a/packages/theme/src/theme-provider-color-warnings.ts +++ b/packages/theme/src/theme-provider-color-warnings.ts @@ -1,10 +1,6 @@ import type { DEFAULT_SEED_COLORS } from './color-ramps/lib/constants.ts'; import { getContrast } from './color-ramps/lib/color-utils.ts'; -import type { - AccentRampResult, - Ramp, - RampResult, -} from './color-ramps/lib/types.ts'; +import type { Ramp, RampResult } from './color-ramps/lib/types.ts'; import { getSemanticColorCustomProperty, MINIMUM_TEXT_CONTRAST, @@ -42,10 +38,7 @@ export type ThemeProviderColorWarning = * @param colorValues Semantic CSS custom properties and serialized colors. */ export function collectThemeProviderColorWarnings( - ramps: ReadonlyMap< - ThemeProviderColorRampName, - RampResult | AccentRampResult - >, + ramps: ReadonlyMap< ThemeProviderColorRampName, RampResult >, colorValues: ReadonlyMap< string, string > ): ThemeProviderColorWarning[] { const warnings: ThemeProviderColorWarning[] = []; diff --git a/packages/theme/src/use-theme-provider-styles.ts b/packages/theme/src/use-theme-provider-styles.ts index db7326a848a5c2..825a686b2c21f2 100644 --- a/packages/theme/src/use-theme-provider-styles.ts +++ b/packages/theme/src/use-theme-provider-styles.ts @@ -19,7 +19,6 @@ import { type RampResult, } from './color-ramps/index.ts'; import { getColorString } from './color-ramps/lib/color-utils.ts'; -import type { AccentRampResult } from './color-ramps/lib/types.ts'; import type { ThemeProviderProps } from './types.ts'; import { collectThemeProviderColorWarnings, @@ -131,10 +130,7 @@ function legacyWpAdminThemeOverridesCSS( accent: string ): Entry[] { } function colorTokensCSS( - computedColorRamps: Map< - ThemeProviderColorRampName, - RampResult | AccentRampResult - > + computedColorRamps: Map< ThemeProviderColorRampName, RampResult > ): Entry[] { const entries: Entry[] = []; @@ -185,7 +181,7 @@ function generateThemeProviderColors( }; const computedColorRamps = new Map< ThemeProviderColorRampName, - RampResult | AccentRampResult + RampResult >(); const bgRamp = getCachedBgRamp( seeds.background ); @@ -195,13 +191,7 @@ function generateThemeProviderColors( rampName, rampName === 'background' ? bgRamp - : getCachedAccentRamp( - seed, - bgRamp, - rampName === 'primary' || rampName === 'error' - ? 'interactive' - : 'status' - ) + : getCachedAccentRamp( seed, bgRamp ) ); } const colorEntries = colorTokensCSS( computedColorRamps ); From 226ea14e5b77baf06871166a1ab3971570848676 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Mon, 7 Sep 2026 14:45:18 +0200 Subject: [PATCH 24/44] Theme: Restore WordPress admin theme seeds --- packages/theme/src/color-ramps/stories/index.story.tsx | 6 +++--- .../design-system/theme-example-application.story.tsx | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/theme/src/color-ramps/stories/index.story.tsx b/packages/theme/src/color-ramps/stories/index.story.tsx index 3ce6fd87de8cf7..b3e326d93dc6c4 100644 --- a/packages/theme/src/color-ramps/stories/index.story.tsx +++ b/packages/theme/src/color-ramps/stories/index.story.tsx @@ -241,9 +241,9 @@ const SAMPLE_COMBINATIONS = [ }, { label: 'Ectoplasm', - source: 'Theme exploration', - background: '#413256', - primary: '#a3b745', + source: 'WordPress admin scheme', + background: '#4f386e', + primary: '#646c3e', }, { label: 'Ocean', diff --git a/storybook/stories/design-system/theme-example-application.story.tsx b/storybook/stories/design-system/theme-example-application.story.tsx index b71460cd15a9c9..86a4c83ff81f00 100644 --- a/storybook/stories/design-system/theme-example-application.story.tsx +++ b/storybook/stories/design-system/theme-example-application.story.tsx @@ -35,7 +35,7 @@ const SIDEBAR_THEME_PRESETS = [ { id: 'ectoplasm', title: 'Ectoplasm', - colors: { primary: '#a3b745', background: '#413256' }, + colors: { primary: '#646c3e', background: '#4f386e' }, }, ] as const; From 62c0e72c636f7477b10ccd5dd2c6437ae572c2bf Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Thu, 10 Sep 2026 13:13:02 +0200 Subject: [PATCH 25/44] Theme: Reconcile color ramps with trunk --- .../internal/_wpds-token-fallbacks.scss | 56 +- packages/theme/CHANGELOG.md | 1 - packages/theme/prebuilt/css/design-tokens.css | 60 +- .../prebuilt/js/design-token-fallbacks.mjs | 56 +- .../color-ramps/lib/build-foreground-scale.ts | 20 +- .../color-ramps/lib/build-perceptual-steps.ts | 15 +- .../src/color-ramps/lib/default-ramps.ts | 67 +- packages/theme/src/color-ramps/lib/index.ts | 10 +- .../theme/src/color-ramps/lib/ramp-configs.ts | 1 + .../test/__snapshots__/index.test.ts.snap | 6911 ++++++++--------- .../test/color-space-registration.test.ts | 53 +- .../theme/src/color-ramps/test/index.test.ts | 419 +- packages/theme/tokens/color.json | 252 +- 13 files changed, 3795 insertions(+), 4126 deletions(-) diff --git a/packages/base-styles/internal/_wpds-token-fallbacks.scss b/packages/base-styles/internal/_wpds-token-fallbacks.scss index 679f68b4bc08c7..7022e3ac5c4fa9 100644 --- a/packages/base-styles/internal/_wpds-token-fallbacks.scss +++ b/packages/base-styles/internal/_wpds-token-fallbacks.scss @@ -25,13 +25,13 @@ $fallbacks: ( "color-mix(in oklch, var(--wp-admin-theme-color, #3858e9) 12%, white)", "--wpds-color-background-interactive-brand-weak-disabled": "#0000", "--wpds-color-background-interactive-error": "#0000", - "--wpds-color-background-interactive-error-active": "#f8f8f8", + "--wpds-color-background-interactive-error-active": "#fff6f5", "--wpds-color-background-interactive-error-disabled": "#0000", "--wpds-color-background-interactive-error-strong": "#cc1818", "--wpds-color-background-interactive-error-strong-active": "#b90000", "--wpds-color-background-interactive-error-strong-disabled": "#e3e3e3", "--wpds-color-background-interactive-error-weak": "#0000", - "--wpds-color-background-interactive-error-weak-active": "#fae5e1", + "--wpds-color-background-interactive-error-weak-active": "#fce7e3", "--wpds-color-background-interactive-error-weak-disabled": "#0000", "--wpds-color-background-interactive-neutral": "#fff", "--wpds-color-background-interactive-neutral-active": "#fff", @@ -46,14 +46,14 @@ $fallbacks: ( "color-mix(in oklch, var(--wp-admin-theme-color, #3858e9) 8%, white)", "--wpds-color-background-surface-caution": "#ffea96", "--wpds-color-background-surface-caution-weak": "#fff9ca", - "--wpds-color-background-surface-error": "#fae5e1", + "--wpds-color-background-surface-error": "#fce7e3", "--wpds-color-background-surface-error-weak": "#fff6f5", "--wpds-color-background-surface-info": "#e0edfc", "--wpds-color-background-surface-info-weak": "#f3f9ff", "--wpds-color-background-surface-neutral": "#fcfcfc", "--wpds-color-background-surface-neutral-strong": "#fff", - "--wpds-color-background-surface-neutral-weak": "#f4f4f4", - "--wpds-color-background-surface-success": "#c2f8ca", + "--wpds-color-background-surface-neutral-weak": "#f7f7f7", + "--wpds-color-background-surface-success": "#c5fbcd", "--wpds-color-background-surface-success-weak": "#ebffed", "--wpds-color-background-surface-warning": "#ffe8c0", "--wpds-color-background-surface-warning-weak": "#fff7e1", @@ -68,18 +68,18 @@ $fallbacks: ( "--wpds-color-background-thumb-neutral-weak-disabled": "#dbdbdb", "--wpds-color-background-track-neutral": "#dbdbdb", "--wpds-color-background-track-neutral-weak": "#f0f0f0", - "--wpds-color-foreground-content-caution": "#281d00", - "--wpds-color-foreground-content-caution-weak": "#836a00", - "--wpds-color-foreground-content-error": "#470000", - "--wpds-color-foreground-content-error-weak": "#d71a1a", - "--wpds-color-foreground-content-info": "#001b4f", - "--wpds-color-foreground-content-info-weak": "#006ec6", - "--wpds-color-foreground-content-neutral": "#1e1e1e", - "--wpds-color-foreground-content-neutral-weak": "#707070", - "--wpds-color-foreground-content-success": "#002900", - "--wpds-color-foreground-content-success-weak": "#008030", - "--wpds-color-foreground-content-warning": "#2e1900", - "--wpds-color-foreground-content-warning-weak": "#936300", + "--wpds-color-foreground-content-caution": "#5c4f16", + "--wpds-color-foreground-content-caution-weak": "#7c6c21", + "--wpds-color-foreground-content-error": "#980f0f", + "--wpds-color-foreground-content-error-weak": "#d81a1a", + "--wpds-color-foreground-content-info": "#005092", + "--wpds-color-foreground-content-info-weak": "#006fc7", + "--wpds-color-foreground-content-neutral": "#505050", + "--wpds-color-foreground-content-neutral-weak": "#717171", + "--wpds-color-foreground-content-success": "#215b2f", + "--wpds-color-foreground-content-success-weak": "#307e44", + "--wpds-color-foreground-content-warning": "#654c19", + "--wpds-color-foreground-content-warning-weak": "#886726", "--wpds-color-foreground-interactive-brand": "color-mix(in oklch, var(--wp-admin-theme-color, #3858e9) 96%, white)", "--wpds-color-foreground-interactive-brand-active": @@ -88,11 +88,11 @@ $fallbacks: ( "--wpds-color-foreground-interactive-brand-strong": "#fff", "--wpds-color-foreground-interactive-brand-strong-active": "#fff", "--wpds-color-foreground-interactive-brand-strong-disabled": "#8d8d8d", - "--wpds-color-foreground-interactive-error": "#d71a1a", + "--wpds-color-foreground-interactive-error": "#d81a1a", "--wpds-color-foreground-interactive-error-active": "#980f0f", "--wpds-color-foreground-interactive-error-disabled": "#8d8d8d", - "--wpds-color-foreground-interactive-error-strong": "#eae6e6", - "--wpds-color-foreground-interactive-error-strong-active": "#eae6e6", + "--wpds-color-foreground-interactive-error-strong": "#f2efef", + "--wpds-color-foreground-interactive-error-strong-active": "#f2efef", "--wpds-color-foreground-interactive-error-strong-disabled": "#8d8d8d", "--wpds-color-foreground-interactive-neutral": "#505050", "--wpds-color-foreground-interactive-neutral-active": "#505050", @@ -121,19 +121,19 @@ $fallbacks: ( "color-mix(in oklch, var(--wp-admin-theme-color, #3858e9) 40%, white)", "--wpds-color-stroke-surface-brand-strong": "var(--wp-admin-theme-color, #3858e9)", - "--wpds-color-stroke-surface-caution": "#d0c38b", - "--wpds-color-stroke-surface-caution-strong": "#836b00", - "--wpds-color-stroke-surface-error": "#dfafa8", + "--wpds-color-stroke-surface-caution": "#d0c28b", + "--wpds-color-stroke-surface-caution-strong": "#836a00", + "--wpds-color-stroke-surface-error": "#e0afa7", "--wpds-color-stroke-surface-error-strong": "#cc1818", - "--wpds-color-stroke-surface-info": "#aac6e5", + "--wpds-color-stroke-surface-info": "#adc6e2", "--wpds-color-stroke-surface-info-strong": "#006bd7", "--wpds-color-stroke-surface-neutral": "#dbdbdb", "--wpds-color-stroke-surface-neutral-strong": "#8d8d8d", "--wpds-color-stroke-surface-neutral-weak": "#f0f0f0", - "--wpds-color-stroke-surface-success": "#92d19d", - "--wpds-color-stroke-surface-success-strong": "#007f2f", - "--wpds-color-stroke-surface-warning": "#e1be80", - "--wpds-color-stroke-surface-warning-strong": "#936400", + "--wpds-color-stroke-surface-success": "#92d39d", + "--wpds-color-stroke-surface-success-strong": "#008030", + "--wpds-color-stroke-surface-warning": "#e1bd7c", + "--wpds-color-stroke-surface-warning-strong": "#936300", "--wpds-cursor-control": "pointer", "--wpds-dimension-gap-2xl": "32px", "--wpds-dimension-gap-3xl": "40px", diff --git a/packages/theme/CHANGELOG.md b/packages/theme/CHANGELOG.md index c6ff344ff6bc58..5552dd9a527a61 100644 --- a/packages/theme/CHANGELOG.md +++ b/packages/theme/CHANGELOG.md @@ -76,7 +76,6 @@ - Generate the default ramps before derived token artifacts so one build uses the current ramp algorithm throughout. ([#82525](https://github.com/WordPress/gutenberg/pull/82525)) - Cache relative luminance calculations used by color-ramp contrast checks. ([#82445](https://github.com/WordPress/gutenberg/pull/82445)) - Register color spaces only when calculating colors, avoiding module-import side effects. ([#82294](https://github.com/WordPress/gutenberg/pull/82294)) -- Generate the default ramps before derived token artifacts so one build uses the current ramp algorithm throughout. ([#82294](https://github.com/WordPress/gutenberg/pull/82294)) - Enforce NodeNext module resolution in the build project so future declaration imports are checked against the package's published ESM resolution rules. ([#82088](https://github.com/WordPress/gutenberg/pull/82088)) - Update the `@types/node` development dependency to v24, matching the Node.js version the repository builds and tests against ([#82616](https://github.com/WordPress/gutenberg/pull/82616)). - Declare the `@storybook/addon-docs` development dependency used by the package's story documentation ([#82676](https://github.com/WordPress/gutenberg/pull/82676)). diff --git a/packages/theme/prebuilt/css/design-tokens.css b/packages/theme/prebuilt/css/design-tokens.css index bbd9a9e3a0c4d2..c77760ad5a3de6 100644 --- a/packages/theme/prebuilt/css/design-tokens.css +++ b/packages/theme/prebuilt/css/design-tokens.css @@ -32,13 +32,13 @@ /* Background color for interactive elements with brand tone and weak emphasis. */ --wpds-color-background-interactive-brand-weak: #0000; /* Background color for interactive elements with brand tone and weak emphasis that are hovered, focused, or active. */ - --wpds-color-background-interactive-brand-weak-active: #e5eaf7; + --wpds-color-background-interactive-brand-weak-active: #e6ebf8; /* Background color for interactive elements with brand tone and weak emphasis, in their disabled state. */ --wpds-color-background-interactive-brand-weak-disabled: #0000; /* Background color for interactive elements with error tone and normal emphasis. */ --wpds-color-background-interactive-error: #0000; /* Background color for interactive elements with error tone and normal emphasis that are hovered, focused, or active. */ - --wpds-color-background-interactive-error-active: #f8f8f8; + --wpds-color-background-interactive-error-active: #fff6f5; /* Background color for interactive elements with error tone and normal emphasis, in their disabled state. */ --wpds-color-background-interactive-error-disabled: #0000; /* Background color for interactive elements with error tone and strong emphasis. */ @@ -50,7 +50,7 @@ /* Background color for interactive elements with error tone and weak emphasis. */ --wpds-color-background-interactive-error-weak: #0000; /* Background color for interactive elements with error tone and weak emphasis that are hovered, focused, or active. */ - --wpds-color-background-interactive-error-weak-active: #fae5e1; + --wpds-color-background-interactive-error-weak-active: #fce7e3; /* Background color for interactive elements with error tone and weak emphasis, in their disabled state. */ --wpds-color-background-interactive-error-weak-disabled: #0000; /* Solid background color for neutral interactive controls, such as input fields and unchecked checkboxes. */ @@ -64,25 +64,25 @@ /* Background color for interactive elements with neutral tone and strong emphasis that are hovered, focused, or active. */ --wpds-color-background-interactive-neutral-strong-active: #1e1e1e; /* Background color for interactive elements with neutral tone and strong emphasis, in their disabled state. */ - --wpds-color-background-interactive-neutral-strong-disabled: #e6e6e6; + --wpds-color-background-interactive-neutral-strong-disabled: #e3e3e3; /* Background color for interactive elements with neutral tone and weak emphasis. */ --wpds-color-background-interactive-neutral-weak: #0000; /* Background color for interactive elements with neutral tone and weak emphasis that are hovered, focused, or active. */ - --wpds-color-background-interactive-neutral-weak-active: #ededed; + --wpds-color-background-interactive-neutral-weak-active: #efefef; /* Background color for interactive elements with neutral tone and weak emphasis, in their disabled state. */ --wpds-color-background-interactive-neutral-weak-disabled: #0000; /* Background color for surfaces with brand tone and normal emphasis. */ - --wpds-color-background-surface-brand: #ecf0fa; + --wpds-color-background-surface-brand: #edf1fb; /* Background color for surfaces with caution tone and normal emphasis. */ - --wpds-color-background-surface-caution: #fee995; + --wpds-color-background-surface-caution: #ffea96; /* Background color for surfaces with caution tone and weak emphasis. */ --wpds-color-background-surface-caution-weak: #fff9ca; /* Background color for surfaces with error tone and normal emphasis. */ - --wpds-color-background-surface-error: #fae5e1; + --wpds-color-background-surface-error: #fce7e3; /* Background color for surfaces with error tone and weak emphasis. */ --wpds-color-background-surface-error-weak: #fff6f5; /* Background color for surfaces with info tone and normal emphasis. */ - --wpds-color-background-surface-info: #deebfa; + --wpds-color-background-surface-info: #e0edfc; /* Background color for surfaces with info tone and weak emphasis. */ --wpds-color-background-surface-info-weak: #f3f9ff; /* Background color for surfaces with normal emphasis. */ @@ -90,13 +90,13 @@ /* Background color for surfaces with strong emphasis. */ --wpds-color-background-surface-neutral-strong: #fff; /* Background color for surfaces with weak emphasis. */ - --wpds-color-background-surface-neutral-weak: #f4f4f4; + --wpds-color-background-surface-neutral-weak: #f7f7f7; /* Background color for surfaces with success tone and normal emphasis. */ - --wpds-color-background-surface-success: #c2f8ca; + --wpds-color-background-surface-success: #c5fbcd; /* Background color for surfaces with success tone and weak emphasis. */ --wpds-color-background-surface-success-weak: #ebffed; /* Background color for surfaces with warning tone and normal emphasis. */ - --wpds-color-background-surface-warning: #fde6be; + --wpds-color-background-surface-warning: #ffe8c0; /* Background color for surfaces with warning tone and weak emphasis. */ --wpds-color-background-surface-warning-weak: #fff7e1; /* Background color for thumbs with a brand tone and normal emphasis (eg. slider thumb and filled track). */ @@ -118,17 +118,17 @@ /* Background color for tracks with a neutral tone and weak emphasis (eg. scrollbar track). */ --wpds-color-background-track-neutral-weak: #f0f0f0; /* Foreground color for content like text with caution tone and normal emphasis. */ - --wpds-color-foreground-content-caution: #281d00; + --wpds-color-foreground-content-caution: #5c4f16; /* Foreground color for content like text with caution tone and weak emphasis. */ - --wpds-color-foreground-content-caution-weak: #836a00; + --wpds-color-foreground-content-caution-weak: #7c6c21; /* Foreground color for content like text with error tone and normal emphasis. */ - --wpds-color-foreground-content-error: #470000; + --wpds-color-foreground-content-error: #980f0f; /* Foreground color for content like text with error tone and weak emphasis. */ - --wpds-color-foreground-content-error-weak: #cc1818; + --wpds-color-foreground-content-error-weak: #d81a1a; /* Foreground color for content like text with info tone and normal emphasis. */ - --wpds-color-foreground-content-info: #001b4f; + --wpds-color-foreground-content-info: #005092; /* Foreground color for content like text with info tone and weak emphasis. */ - --wpds-color-foreground-content-info-weak: #006bd7; + --wpds-color-foreground-content-info-weak: #006fc7; /* Foreground color for content like text with normal emphasis. */ --wpds-color-foreground-content-neutral: #505050; /* Foreground color for content like text with weak emphasis. */ @@ -136,11 +136,11 @@ /* Foreground color for content like text with success tone and normal emphasis. */ --wpds-color-foreground-content-success: #215b2f; /* Foreground color for content like text with success tone and weak emphasis. */ - --wpds-color-foreground-content-success-weak: #008030; + --wpds-color-foreground-content-success-weak: #307e44; /* Foreground color for content like text with warning tone and normal emphasis. */ - --wpds-color-foreground-content-warning: #654c1a; + --wpds-color-foreground-content-warning: #654c19; /* Foreground color for content like text with warning tone and weak emphasis. */ - --wpds-color-foreground-content-warning-weak: #936300; + --wpds-color-foreground-content-warning-weak: #886726; /* Foreground color for interactive elements with brand tone and normal emphasis. */ --wpds-color-foreground-interactive-brand: #3f61ea; /* Foreground color for interactive elements with brand tone and normal emphasis that are hovered, focused, or active. */ @@ -148,27 +148,27 @@ /* Foreground color for interactive elements with brand tone and normal emphasis, in their disabled state. */ --wpds-color-foreground-interactive-brand-disabled: #8d8d8d; /* Foreground color for interactive elements with brand tone and strong emphasis. */ - --wpds-color-foreground-interactive-brand-strong: #e7e8ea; + --wpds-color-foreground-interactive-brand-strong: #eff0f2; /* Foreground color for interactive elements with brand tone and strong emphasis that are hovered, focused, or active. */ - --wpds-color-foreground-interactive-brand-strong-active: #e7e8ea; + --wpds-color-foreground-interactive-brand-strong-active: #eff0f2; /* Foreground color for interactive elements with brand tone and strong emphasis, in their disabled state. */ --wpds-color-foreground-interactive-brand-strong-disabled: #8d8d8d; /* Foreground color for interactive elements with error tone and normal emphasis. */ - --wpds-color-foreground-interactive-error: #d71a1a; + --wpds-color-foreground-interactive-error: #d81a1a; /* Foreground color for interactive elements with error tone and normal emphasis that are hovered, focused, or active. */ --wpds-color-foreground-interactive-error-active: #980f0f; /* Foreground color for interactive elements with error tone and normal emphasis, in their disabled state. */ --wpds-color-foreground-interactive-error-disabled: #8d8d8d; /* Foreground color for interactive elements with error tone and strong emphasis. */ - --wpds-color-foreground-interactive-error-strong: #eae6e6; + --wpds-color-foreground-interactive-error-strong: #f2efef; /* Foreground color for interactive elements with error tone and strong emphasis that are hovered, focused, or active. */ - --wpds-color-foreground-interactive-error-strong-active: #eae6e6; + --wpds-color-foreground-interactive-error-strong-active: #f2efef; /* Foreground color for interactive elements with error tone and strong emphasis, in their disabled state. */ --wpds-color-foreground-interactive-error-strong-disabled: #8d8d8d; /* Foreground color for interactive elements with neutral tone and normal emphasis. */ - --wpds-color-foreground-interactive-neutral: #1e1e1e; + --wpds-color-foreground-interactive-neutral: #505050; /* Foreground color for interactive elements with neutral tone and normal emphasis that are hovered, focused, or active. */ - --wpds-color-foreground-interactive-neutral-active: #1e1e1e; + --wpds-color-foreground-interactive-neutral-active: #505050; /* Foreground color for interactive elements with neutral tone and normal emphasis, in their disabled state. */ --wpds-color-foreground-interactive-neutral-disabled: #8d8d8d; /* Foreground color for interactive elements with neutral tone and strong emphasis. */ @@ -178,9 +178,9 @@ /* Foreground color for interactive elements with neutral tone and strong emphasis, in their disabled state. */ --wpds-color-foreground-interactive-neutral-strong-disabled: #8d8d8d; /* Foreground color for interactive elements with neutral tone and weak emphasis. */ - --wpds-color-foreground-interactive-neutral-weak: #707070; + --wpds-color-foreground-interactive-neutral-weak: #717171; /* Foreground color for interactive elements with neutral tone and weak emphasis that are hovered, focused, or active. */ - --wpds-color-foreground-interactive-neutral-weak-active: #1e1e1e; + --wpds-color-foreground-interactive-neutral-weak-active: #505050; /* Foreground color for interactive elements with neutral tone and weak emphasis, in their disabled state. */ --wpds-color-foreground-interactive-neutral-weak-disabled: #8d8d8d; /* Accessible stroke color applied to focus rings. */ diff --git a/packages/theme/prebuilt/js/design-token-fallbacks.mjs b/packages/theme/prebuilt/js/design-token-fallbacks.mjs index cc3d4e98a29f48..69b39f7678db82 100644 --- a/packages/theme/prebuilt/js/design-token-fallbacks.mjs +++ b/packages/theme/prebuilt/js/design-token-fallbacks.mjs @@ -23,13 +23,13 @@ export default { 'color-mix(in oklch, var(--wp-admin-theme-color, #3858e9) 12%, white)', '--wpds-color-background-interactive-brand-weak-disabled': '#0000', '--wpds-color-background-interactive-error': '#0000', - '--wpds-color-background-interactive-error-active': '#f8f8f8', + '--wpds-color-background-interactive-error-active': '#fff6f5', '--wpds-color-background-interactive-error-disabled': '#0000', '--wpds-color-background-interactive-error-strong': '#cc1818', '--wpds-color-background-interactive-error-strong-active': '#b90000', '--wpds-color-background-interactive-error-strong-disabled': '#e3e3e3', '--wpds-color-background-interactive-error-weak': '#0000', - '--wpds-color-background-interactive-error-weak-active': '#fae5e1', + '--wpds-color-background-interactive-error-weak-active': '#fce7e3', '--wpds-color-background-interactive-error-weak-disabled': '#0000', '--wpds-color-background-interactive-neutral': '#fff', '--wpds-color-background-interactive-neutral-active': '#fff', @@ -44,14 +44,14 @@ export default { 'color-mix(in oklch, var(--wp-admin-theme-color, #3858e9) 8%, white)', '--wpds-color-background-surface-caution': '#ffea96', '--wpds-color-background-surface-caution-weak': '#fff9ca', - '--wpds-color-background-surface-error': '#fae5e1', + '--wpds-color-background-surface-error': '#fce7e3', '--wpds-color-background-surface-error-weak': '#fff6f5', '--wpds-color-background-surface-info': '#e0edfc', '--wpds-color-background-surface-info-weak': '#f3f9ff', '--wpds-color-background-surface-neutral': '#fcfcfc', '--wpds-color-background-surface-neutral-strong': '#fff', - '--wpds-color-background-surface-neutral-weak': '#f4f4f4', - '--wpds-color-background-surface-success': '#c2f8ca', + '--wpds-color-background-surface-neutral-weak': '#f7f7f7', + '--wpds-color-background-surface-success': '#c5fbcd', '--wpds-color-background-surface-success-weak': '#ebffed', '--wpds-color-background-surface-warning': '#ffe8c0', '--wpds-color-background-surface-warning-weak': '#fff7e1', @@ -66,18 +66,18 @@ export default { '--wpds-color-background-thumb-neutral-weak-disabled': '#dbdbdb', '--wpds-color-background-track-neutral': '#dbdbdb', '--wpds-color-background-track-neutral-weak': '#f0f0f0', - '--wpds-color-foreground-content-caution': '#281d00', - '--wpds-color-foreground-content-caution-weak': '#836a00', - '--wpds-color-foreground-content-error': '#470000', - '--wpds-color-foreground-content-error-weak': '#d71a1a', - '--wpds-color-foreground-content-info': '#001b4f', - '--wpds-color-foreground-content-info-weak': '#006ec6', - '--wpds-color-foreground-content-neutral': '#1e1e1e', - '--wpds-color-foreground-content-neutral-weak': '#707070', - '--wpds-color-foreground-content-success': '#002900', - '--wpds-color-foreground-content-success-weak': '#008030', - '--wpds-color-foreground-content-warning': '#2e1900', - '--wpds-color-foreground-content-warning-weak': '#936300', + '--wpds-color-foreground-content-caution': '#5c4f16', + '--wpds-color-foreground-content-caution-weak': '#7c6c21', + '--wpds-color-foreground-content-error': '#980f0f', + '--wpds-color-foreground-content-error-weak': '#d81a1a', + '--wpds-color-foreground-content-info': '#005092', + '--wpds-color-foreground-content-info-weak': '#006fc7', + '--wpds-color-foreground-content-neutral': '#505050', + '--wpds-color-foreground-content-neutral-weak': '#717171', + '--wpds-color-foreground-content-success': '#215b2f', + '--wpds-color-foreground-content-success-weak': '#307e44', + '--wpds-color-foreground-content-warning': '#654c19', + '--wpds-color-foreground-content-warning-weak': '#886726', '--wpds-color-foreground-interactive-brand': 'color-mix(in oklch, var(--wp-admin-theme-color, #3858e9) 96%, white)', '--wpds-color-foreground-interactive-brand-active': @@ -86,11 +86,11 @@ export default { '--wpds-color-foreground-interactive-brand-strong': '#fff', '--wpds-color-foreground-interactive-brand-strong-active': '#fff', '--wpds-color-foreground-interactive-brand-strong-disabled': '#8d8d8d', - '--wpds-color-foreground-interactive-error': '#d71a1a', + '--wpds-color-foreground-interactive-error': '#d81a1a', '--wpds-color-foreground-interactive-error-active': '#980f0f', '--wpds-color-foreground-interactive-error-disabled': '#8d8d8d', - '--wpds-color-foreground-interactive-error-strong': '#eae6e6', - '--wpds-color-foreground-interactive-error-strong-active': '#eae6e6', + '--wpds-color-foreground-interactive-error-strong': '#f2efef', + '--wpds-color-foreground-interactive-error-strong-active': '#f2efef', '--wpds-color-foreground-interactive-error-strong-disabled': '#8d8d8d', '--wpds-color-foreground-interactive-neutral': '#505050', '--wpds-color-foreground-interactive-neutral-active': '#505050', @@ -119,19 +119,19 @@ export default { 'color-mix(in oklch, var(--wp-admin-theme-color, #3858e9) 40%, white)', '--wpds-color-stroke-surface-brand-strong': 'var(--wp-admin-theme-color, #3858e9)', - '--wpds-color-stroke-surface-caution': '#d0c38b', - '--wpds-color-stroke-surface-caution-strong': '#836b00', - '--wpds-color-stroke-surface-error': '#dfafa8', + '--wpds-color-stroke-surface-caution': '#d0c28b', + '--wpds-color-stroke-surface-caution-strong': '#836a00', + '--wpds-color-stroke-surface-error': '#e0afa7', '--wpds-color-stroke-surface-error-strong': '#cc1818', - '--wpds-color-stroke-surface-info': '#aac6e5', + '--wpds-color-stroke-surface-info': '#adc6e2', '--wpds-color-stroke-surface-info-strong': '#006bd7', '--wpds-color-stroke-surface-neutral': '#dbdbdb', '--wpds-color-stroke-surface-neutral-strong': '#8d8d8d', '--wpds-color-stroke-surface-neutral-weak': '#f0f0f0', - '--wpds-color-stroke-surface-success': '#92d19d', - '--wpds-color-stroke-surface-success-strong': '#007f2f', - '--wpds-color-stroke-surface-warning': '#e1be80', - '--wpds-color-stroke-surface-warning-strong': '#936400', + '--wpds-color-stroke-surface-success': '#92d39d', + '--wpds-color-stroke-surface-success-strong': '#008030', + '--wpds-color-stroke-surface-warning': '#e1bd7c', + '--wpds-color-stroke-surface-warning-strong': '#936300', '--wpds-cursor-control': 'pointer', '--wpds-dimension-gap-2xl': '32px', '--wpds-dimension-gap-3xl': '40px', diff --git a/packages/theme/src/color-ramps/lib/build-foreground-scale.ts b/packages/theme/src/color-ramps/lib/build-foreground-scale.ts index f71bbe3d4484ea..05743bccdbc242 100644 --- a/packages/theme/src/color-ramps/lib/build-foreground-scale.ts +++ b/packages/theme/src/color-ramps/lib/build-foreground-scale.ts @@ -4,6 +4,7 @@ import { get, OKLCH, OKLCH_sRGB as OklchSrgb, + parse, sRGB, to, type PlainColorObject, @@ -24,6 +25,10 @@ type GetColorForLightness = ( lightness: number ) => PlainColorObject; const PERCEPTUAL_INTERVAL_SERIALIZATION_TOPUP = 0.25; +function clampSerializedColorToGamut( color: string ) { + return clampToGamut( parse( color ) ); +} + function getChromaPreservingColorForLightness( seed: PlainColorObject, lightness: number @@ -366,7 +371,10 @@ function serializeColorMeetingPerceptualInterval( { return strongColor; } - const strongLightness = get( clampToGamut( strongColor ), [ OKLCH, 'l' ] ); + const strongLightness = get( clampSerializedColorToGamut( strongColor ), [ + OKLCH, + 'l', + ] ); const endpointLightness = get( strongEndpoint, [ OKLCH, 'l' ] ); const direction = Math.sign( endpointLightness - strongLightness ); if ( direction === 0 ) { @@ -406,12 +414,14 @@ export function buildForegroundScale( ): RampResult { // APCA resolves sRGB by name, even for color objects. ColorSpace.register( sRGB ); - const seed = clampToGamut( ramp.ramp[ config.seed ] ); + const seed = clampSerializedColorToGamut( ramp.ramp[ config.seed ] ); const getColorAtLightness = createColorForLightness( seed, config ); // Parse and convert this once. APCA evaluates it for every intermediate // foreground candidate. const displayBackground = to( - clampToGamut( backgroundRamp.ramp[ config.perceptualReference ] ), + clampSerializedColorToGamut( + backgroundRamp.ramp[ config.perceptualReference ] + ), sRGB ); const steps = Object.fromEntries( @@ -435,7 +445,9 @@ export function buildForegroundScale( for ( const step of config.steps.filter( ( stepConfig ) => stepConfig.preserveAnchor === true ) ) { - const currentColor = clampToGamut( ramp.ramp[ step.name ] ); + const currentColor = clampSerializedColorToGamut( + ramp.ramp[ step.name ] + ); colors.set( step.name, meetsContrastTarget( diff --git a/packages/theme/src/color-ramps/lib/build-perceptual-steps.ts b/packages/theme/src/color-ramps/lib/build-perceptual-steps.ts index 1f51a2d9286949..b00463d2a25ec2 100644 --- a/packages/theme/src/color-ramps/lib/build-perceptual-steps.ts +++ b/packages/theme/src/color-ramps/lib/build-perceptual-steps.ts @@ -6,6 +6,7 @@ import { OKLab, OKLCH, OKLrab, + parse, sRGB, to, type PlainColorObject, @@ -30,6 +31,10 @@ function getPerceptualLightness( color: string | PlainColorObject ) { return get( color, [ OKLrab, 'l' ] ); } +function clampColorToGamut( color: string | PlainColorObject ) { + return clampToGamut( typeof color === 'string' ? parse( color ) : color ); +} + /** * Create a path indexed by OKLr lightness, not OKLCH lightness. Preserve the * seed's authored chroma and hue before gamut mapping, which may reduce chroma. @@ -39,7 +44,7 @@ function getPerceptualLightness( color: string | PlainColorObject ) { function createColorAtPerceptualLightness( seed: string | PlainColorObject ): GetColorAtLightness { - const parsedSeed = clampToGamut( seed ); + const parsedSeed = clampColorToGamut( seed ); const chroma = get( parsedSeed, [ OKLCH, 'c' ] ); const hue = get( parsedSeed, [ OKLCH, 'h' ] ); @@ -97,9 +102,9 @@ function createAnchoredColorForStepAtPerceptualLightness( { startLightness: number; endLightness: number; } ) { - const stepColor = to( clampToGamut( ramp[ step ] ), OKLCH ); + const stepColor = to( clampColorToGamut( ramp[ step ] ), OKLCH ); const getTargetColor = createColorAtPerceptualLightness( stepColor ); - const anchorColor = to( clampToGamut( anchor ), OKLCH ); + const anchorColor = to( clampColorToGamut( anchor ), OKLCH ); const anchorChroma = get( anchorColor, [ OKLCH, 'c' ] ); const stepChroma = get( stepColor, [ OKLCH, 'c' ] ); const distance = endLightness - startLightness; @@ -377,7 +382,7 @@ function rebuildStrokes( } ); const stroke3Lightness = getPerceptualLightness( nextRamp.stroke3 ); - const stroke1Reference = to( clampToGamut( nextRamp.stroke1 ), OKLab ); + const stroke1Reference = to( clampColorToGamut( nextRamp.stroke1 ), OKLab ); const stroke1To3Difference = deltaEOK2( stroke1Reference, nextRamp.stroke3 @@ -425,7 +430,7 @@ function rebuildStrokes( return nextRamp; } - const stroke3Reference = to( clampToGamut( nextRamp.stroke3 ), OKLab ); + const stroke3Reference = to( clampColorToGamut( nextRamp.stroke3 ), OKLab ); const minimumActiveDifference = deltaEOK2( stroke3Reference, ramp.ramp.stroke4 diff --git a/packages/theme/src/color-ramps/lib/default-ramps.ts b/packages/theme/src/color-ramps/lib/default-ramps.ts index 7bb6db485bcd5d..79d004a079e57d 100644 --- a/packages/theme/src/color-ramps/lib/default-ramps.ts +++ b/packages/theme/src/color-ramps/lib/default-ramps.ts @@ -37,19 +37,19 @@ export const DEFAULT_RAMPS: Record< fgFill: '#eff0f2', surface2: '#f6f8fe', surface6: '#c7d2ee', - surface5: '#dce3f4', - surface4: '#e5eaf7', + surface5: '#d8dff0', + surface4: '#e6ebf8', surface3: '#fff', fgSurface3: '#3f61ea', fgSurface2: '#5a82ff', stroke3: '#3858e9', stroke4: '#2337c8', - stroke2: '#9aaad1', + stroke2: '#9ba9c9', stroke1: '#aebbda', bgFillInverted2: '#1a1e27', bgFillInverted1: '#13009f', fgFillInverted: '#eff0f2', - surface1: '#ecf1fc', + surface1: '#edf1fb', fgSurface4: '#222eda', fgSurface5: '#03063b', }, @@ -62,19 +62,21 @@ export const DEFAULT_RAMPS: Record< fgFill: '#12161a', surface2: '#f3f9ff', surface6: '#bcd5f2', - surface5: '#d3e4f7', - surface4: '#deebfa', + surface5: '#d0e1f4', + surface4: '#e0edfc', surface3: '#fff', fgSurface3: '#006fc7', fgSurface2: '#008bfa', stroke3: '#006bd7', stroke4: '#004fa9', - stroke2: '#95b5d9', + stroke2: '#99b3d0', stroke1: '#adc6e2', bgFillInverted2: '#1a1f24', bgFillInverted1: '#002a69', fgFillInverted: '#eef0f2', - surface1: '#e7f1fd', + surface1: '#e8f2fe', + fgSurface4: '#005092', + fgSurface5: '#00132a', }, direction: 'darker', }, @@ -85,20 +87,21 @@ export const DEFAULT_RAMPS: Record< fgFill: '#1b1f1c', surface2: '#ebffed', surface6: '#89e89c', - surface5: '#a7f5b5', - surface4: '#c2f8ca', + surface5: '#a5f2b3', + surface4: '#c5fbcd', surface3: '#fff', - fgSurface4: '#002900', - fgSurface3: '#008030', + fgSurface3: '#307e44', fgSurface2: '#2b9f4e', stroke3: '#008030', stroke4: '#006113', - stroke2: '#84c08f', + stroke2: '#81c18d', stroke1: '#92d39d', bgFillInverted2: '#1b1f1c', bgFillInverted1: '#003701', fgFillInverted: '#ecf2ec', - surface1: '#d4fbd9', + surface1: '#d6fddb', + fgSurface4: '#215b2f', + fgSurface5: '#041708', }, direction: 'darker', }, @@ -109,20 +112,21 @@ export const DEFAULT_RAMPS: Record< fgFill: '#1f1e1b', surface2: '#fff9ca', surface6: '#e8d170', - surface5: '#f7e28a', - surface4: '#fee995', + surface5: '#f3de86', + surface4: '#ffea96', surface3: '#fff', - fgSurface4: '#281d00', - fgSurface3: '#836a00', + fgSurface3: '#7c6c21', fgSurface2: '#a48700', stroke3: '#836a00', stroke4: '#634f00', - stroke2: '#bdb17e', + stroke2: '#beb17c', stroke1: '#d0c28b', bgFillInverted2: '#1f1e1b', bgFillInverted1: '#392c00', fgFillInverted: '#faf1c9', surface1: '#ffefa9', + fgSurface4: '#5c4f16', + fgSurface5: '#181402', }, direction: 'darker', }, @@ -133,20 +137,21 @@ export const DEFAULT_RAMPS: Record< fgFill: '#1f1e1b', surface2: '#fff7e1', surface6: '#f3cc86', - surface5: '#fadead', - surface4: '#fde6be', + surface5: '#f6daaa', + surface4: '#ffe8c0', surface3: '#fff', - fgSurface4: '#2e1900', - fgSurface3: '#936300', + fgSurface3: '#886726', fgSurface2: '#b57f00', stroke3: '#936300', stroke4: '#704900', - stroke2: '#c8ad7f', + stroke2: '#c9a76c', stroke1: '#e1bd7c', bgFillInverted2: '#1f1e1b', bgFillInverted1: '#422800', fgFillInverted: '#f5efe5', - surface1: '#ffeccb', + surface1: '#ffedcc', + fgSurface4: '#654c19', + fgSurface5: '#1b1203', }, direction: 'darker', }, @@ -157,19 +162,19 @@ export const DEFAULT_RAMPS: Record< fgFill: '#f2efef', surface2: '#fff6f5', surface6: '#f2c9c3', - surface5: '#f7dbd7', - surface4: '#fae5e1', + surface5: '#f5d9d5', + surface4: '#fce7e3', surface3: '#fff', - fgSurface3: '#d71a1a', + fgSurface3: '#d81a1a', fgSurface2: '#f74c40', stroke3: '#cc1818', stroke4: '#9d0000', - stroke2: '#d99a91', + stroke2: '#ce9b93', stroke1: '#e0afa7', bgFillInverted2: '#231c1b', bgFillInverted1: '#640000', - fgFillInverted: '#f3efef', - surface1: '#fdece9', + fgFillInverted: '#f2efef', + surface1: '#fdedea', fgSurface4: '#980f0f', fgSurface5: '#260101', }, diff --git a/packages/theme/src/color-ramps/lib/index.ts b/packages/theme/src/color-ramps/lib/index.ts index c86f7ad23f543d..d0c71bfc2290ed 100644 --- a/packages/theme/src/color-ramps/lib/index.ts +++ b/packages/theme/src/color-ramps/lib/index.ts @@ -239,15 +239,7 @@ export function buildRamp( pinLightness, backgroundRamp, rescaleToFitContrastTargets = true, - }: { - mainDirection?: RampDirection; - pinLightness?: { - stepName: keyof Ramp; - value: number; - }; - backgroundRamp?: RampResult; - rescaleToFitContrastTargets?: boolean; - } = {} + }: BuildRampOptions = {} ): RampResult { // Parse and validate here: the single point where user-supplied color strings enter. const parsedSeed = parseSeedColor( seedArg ); diff --git a/packages/theme/src/color-ramps/lib/ramp-configs.ts b/packages/theme/src/color-ramps/lib/ramp-configs.ts index 9b6e4f97316cdf..d11b0c3ea1c8d6 100644 --- a/packages/theme/src/color-ramps/lib/ramp-configs.ts +++ b/packages/theme/src/color-ramps/lib/ramp-configs.ts @@ -244,6 +244,7 @@ const BG_RAMP_STEPS: RampConfig[ 'steps' ] = { // Preserve the 4.5:1 WCAG floor after 8-bit sRGB serialization. target: 4.55, }, + lightness: lightnessConstraintForegroundHighContrast, taperChromaOptions: FG_TAPER_CHROMA, }, fgFillInverted: { diff --git a/packages/theme/src/color-ramps/test/__snapshots__/index.test.ts.snap b/packages/theme/src/color-ramps/test/__snapshots__/index.test.ts.snap index d3e6f88d65ef4b..1a1d6b82c46f89 100644 --- a/packages/theme/src/color-ramps/test/__snapshots__/index.test.ts.snap +++ b/packages/theme/src/color-ramps/test/__snapshots__/index.test.ts.snap @@ -21,24 +21,22 @@ exports[`buildRamps > accent ramp snapshots for admin themes 1`] = ` "ramp": { "bgFill1": "#437aa8", "bgFill2": "#5189b8", - "bgFillDark": "#000", "bgFillInverted1": "#b0e1ff", "bgFillInverted2": "#eef0f2", "fgFill": "#000", - "fgFillDark": "#eef0f2", "fgFillInverted": "#1b1e21", - "fgSurface1": "#0a4872", "fgSurface2": "#2b628f", - "fgSurface3": "#82bbec", - "fgSurface4": "#d8f4ff", + "fgSurface3": "#437ba9", + "fgSurface4": "#cbe0f3", + "fgSurface5": "#fdfefe", "stroke1": "#496680", - "stroke2": "#53738f", + "stroke2": "#55738d", "stroke3": "#82bbec", "stroke4": "#b6e4ff", "surface1": "#000", "surface2": "#000", - "surface3": "#030d19", - "surface4": "#061522", + "surface3": "#00040d", + "surface4": "#010c18", "surface5": "#0a1c2b", "surface6": "#142a3c", }, @@ -63,25 +61,23 @@ exports[`buildRamps > accent ramp snapshots for admin themes 1`] = ` "ramp": { "bgFill1": "#437aa8", "bgFill2": "#5189b8", - "bgFillDark": "#000", "bgFillInverted1": "#b0e1ff", "bgFillInverted2": "#eef0f2", "fgFill": "#000", - "fgFillDark": "#eef0f2", "fgFillInverted": "#1b1e21", - "fgSurface1": "#0d4974", "fgSurface2": "#2d6491", - "fgSurface3": "#82bbec", - "fgSurface4": "#d8f4ff", + "fgSurface3": "#467faf", + "fgSurface4": "#cbe0f3", + "fgSurface5": "#fdfefe", "stroke1": "#496680", - "stroke2": "#53738f", + "stroke2": "#55738d", "stroke3": "#82bbec", "stroke4": "#b6e4ff", "surface1": "#000", "surface2": "#00040a", - "surface3": "#04111d", - "surface4": "#071825", - "surface5": "#0c1e2e", + "surface3": "#030f1b", + "surface4": "#03121f", + "surface5": "#0d1f2f", "surface6": "#152b3e", }, "warnings": undefined, @@ -105,25 +101,23 @@ exports[`buildRamps > accent ramp snapshots for admin themes 1`] = ` "ramp": { "bgFill1": "#437aa8", "bgFill2": "#5189b8", - "bgFillDark": "#000", "bgFillInverted1": "#b0e1ff", "bgFillInverted2": "#eef0f2", "fgFill": "#000", - "fgFillDark": "#eef0f2", "fgFillInverted": "#1b1e21", - "fgSurface1": "#19527d", "fgSurface2": "#366d9a", - "fgSurface3": "#82bbec", - "fgSurface4": "#d8f4ff", + "fgSurface3": "#4d8bbe", + "fgSurface4": "#cde1f3", + "fgSurface5": "#fdfefe", "stroke1": "#496680", - "stroke2": "#53738f", + "stroke2": "#55738d", "stroke3": "#82bbec", "stroke4": "#b6e4ff", "surface1": "#020d18", "surface2": "#071725", "surface3": "#0d202f", - "surface4": "#102435", - "surface5": "#14293c", + "surface4": "#0d2132", + "surface5": "#162b3e", "surface6": "#1b344a", }, "warnings": undefined, @@ -147,25 +141,23 @@ exports[`buildRamps > accent ramp snapshots for admin themes 1`] = ` "ramp": { "bgFill1": "#437aa8", "bgFill2": "#5189b8", - "bgFillDark": "#000", "bgFillInverted1": "#b0e1ff", "bgFillInverted2": "#eef0f2", "fgFill": "#000", - "fgFillDark": "#eef0f2", "fgFillInverted": "#1b1e21", - "fgSurface1": "#2d6491", "fgSurface2": "#4a81af", - "fgSurface3": "#82bbec", - "fgSurface4": "#d8f4ff", + "fgSurface3": "#5aa0db", + "fgSurface4": "#d7e7f6", + "fgSurface5": "#fdfefe", "stroke1": "#496680", - "stroke2": "#53738f", + "stroke2": "#55738d", "stroke3": "#82bbec", "stroke4": "#b6e4ff", "surface1": "#142b3d", "surface2": "#183044", "surface3": "#1c354b", - "surface4": "#1f3950", - "surface5": "#223e56", + "surface4": "#1e384f", + "surface5": "#244058", "surface6": "#294862", }, "warnings": undefined, @@ -189,25 +181,23 @@ exports[`buildRamps > accent ramp snapshots for admin themes 1`] = ` "ramp": { "bgFill1": "#437aa8", "bgFill2": "#5189b8", - "bgFillDark": "#000", "bgFillInverted1": "#b0e1ff", "bgFillInverted2": "#eef0f2", "fgFill": "#000", - "fgFillDark": "#eef0f2", "fgFillInverted": "#1b1e21", - "fgSurface1": "#4980ae", - "fgSurface2": "#689fd0", - "fgSurface3": "#8ac3f5", - "fgSurface4": "#d8f4ff", + "fgSurface2": "#69a0d1", + "fgSurface3": "#98c2e8", + "fgSurface4": "#dbe9f7", + "fgSurface5": "#fdfefe", "stroke1": "#4e6c87", - "stroke2": "#517a9e", + "stroke2": "#5c7d9a", "stroke3": "#8ac3f5", "stroke4": "#c9edff", "surface1": "#28465f", "surface2": "#2b4b66", "surface3": "#2f506c", "surface4": "#315370", - "surface5": "#345876", + "surface5": "#365a78", "surface6": "#3b6283", }, "warnings": undefined, @@ -231,25 +221,23 @@ exports[`buildRamps > accent ramp snapshots for admin themes 1`] = ` "ramp": { "bgFill1": "#437aa8", "bgFill2": "#356c99", - "bgFillDark": "#060709", "bgFillInverted1": "#001e45", "bgFillInverted2": "#060709", "fgFill": "#fff", - "fgFillDark": "#eef0f2", "fgFillInverted": "#eef0f2", - "fgSurface1": "#336a97", "fgSurface2": "#154f7a", - "fgSurface3": "#00335c", - "fgSurface4": "#00061e", + "fgSurface3": "#19344a", + "fgSurface4": "#12283a", + "fgSurface5": "#000", "stroke1": "#597b9a", - "stroke2": "#4f6e89", + "stroke2": "#4d6f8c", "stroke3": "#00335c", "stroke4": "#000924", "surface1": "#6b9eca", "surface2": "#73a4cf", "surface3": "#7baad3", - "surface4": "#6599c7", - "surface5": "#5f94c2", + "surface4": "#679bc9", + "surface5": "#5d91bf", "surface6": "#5788b3", }, "warnings": undefined, @@ -273,25 +261,23 @@ exports[`buildRamps > accent ramp snapshots for admin themes 1`] = ` "ramp": { "bgFill1": "#437aa8", "bgFill2": "#356c99", - "bgFillDark": "#1b1e21", "bgFillInverted1": "#002e57", "bgFillInverted2": "#1b1e21", "fgFill": "#fff", - "fgFillDark": "#eef0f2", "fgFillInverted": "#eef0f2", - "fgSurface1": "#4c83b1", "fgSurface2": "#2f6793", - "fgSurface3": "#114c77", - "fgSurface4": "#001f46", + "fgSurface3": "#274b6a", + "fgSurface4": "#22425d", + "fgSurface5": "#000", "stroke1": "#7096b9", - "stroke2": "#6388a8", + "stroke2": "#6289aa", "stroke3": "#114c77", "stroke4": "#002e56", "surface1": "#96bbdc", "surface2": "#a1c2e0", "surface3": "#abc9e4", - "surface4": "#8fb7da", - "surface5": "#86b1d7", + "surface4": "#91b9dc", + "surface5": "#84afd4", "surface6": "#74a5cf", }, "warnings": undefined, @@ -315,25 +301,23 @@ exports[`buildRamps > accent ramp snapshots for admin themes 1`] = ` "ramp": { "bgFill1": "#437aa8", "bgFill2": "#356c99", - "bgFillDark": "#1b1e21", "bgFillInverted1": "#002e57", "bgFillInverted2": "#1b1e21", "fgFill": "#fff", - "fgFillDark": "#eef0f2", "fgFillInverted": "#eef0f2", - "fgSurface1": "#649ccc", "fgSurface2": "#467dab", - "fgSurface3": "#2a618d", - "fgSurface4": "#001f46", + "fgSurface3": "#346187", + "fgSurface4": "#2e5779", + "fgSurface5": "#071520", "stroke1": "#91b0cd", - "stroke2": "#7ca1c2", + "stroke2": "#80a0bd", "stroke3": "#2a618d", "stroke4": "#074570", "surface1": "#c3d9ec", "surface2": "#cfe1f1", "surface3": "#dbe9f5", - "surface4": "#bcd4ea", - "surface5": "#b2cde6", + "surface4": "#bed6ec", + "surface5": "#b0cbe4", "surface6": "#9dc0df", }, "warnings": undefined, @@ -357,25 +341,23 @@ exports[`buildRamps > accent ramp snapshots for admin themes 1`] = ` "ramp": { "bgFill1": "#437aa8", "bgFill2": "#356c99", - "bgFillDark": "#1b1e21", "bgFillInverted1": "#002e57", "bgFillInverted2": "#1b1e21", "fgFill": "#fff", - "fgFillDark": "#eef0f2", "fgFillInverted": "#eef0f2", - "fgSurface1": "#7db5e6", "fgSurface2": "#5c93c3", - "fgSurface3": "#3f75a3", - "fgSurface4": "#001f46", + "fgSurface3": "#4278a6", + "fgSurface4": "#2f587a", + "fgSurface5": "#071520", "stroke1": "#b6cbde", - "stroke2": "#9ebad3", + "stroke2": "#a2b8cc", "stroke3": "#3f75a3", "stroke4": "#215985", - "surface1": "#f0f7fe", + "surface1": "#f5fcff", "surface2": "#fff", "surface3": "#fff", - "surface4": "#e8f1fa", - "surface5": "#ddeaf6", + "surface4": "#eaf3fc", + "surface5": "#dae7f3", "surface6": "#c7dbee", }, "warnings": undefined, @@ -401,25 +383,23 @@ exports[`buildRamps > accent ramp snapshots for admin themes 1`] = ` "ramp": { "bgFill1": "#976c4a", "bgFill2": "#a67b58", - "bgFillDark": "#000", "bgFillInverted1": "#ffd2ad", "bgFillInverted2": "#f2f0ee", "fgFill": "#000", - "fgFillDark": "#f2f0ee", "fgFillInverted": "#201d1b", - "fgSurface1": "#623c19", "fgSurface2": "#7e5534", - "fgSurface3": "#d7aa86", - "fgSurface4": "#ffe8cc", + "fgSurface3": "#986d4b", + "fgSurface4": "#f0d9c7", + "fgSurface5": "#fefdfd", "stroke1": "#755d4a", - "stroke2": "#846954", + "stroke2": "#836a56", "stroke3": "#d7aa86", "stroke4": "#ffd3ae", "surface1": "#000", "surface2": "#000", - "surface3": "#140b04", - "surface4": "#1d1108", - "surface5": "#26180d", + "surface3": "#080300", + "surface4": "#130802", + "surface5": "#25170c", "surface6": "#352417", }, "warnings": undefined, @@ -443,24 +423,22 @@ exports[`buildRamps > accent ramp snapshots for admin themes 1`] = ` "ramp": { "bgFill1": "#976c4a", "bgFill2": "#a67b58", - "bgFillDark": "#000", "bgFillInverted1": "#ffd2ad", "bgFillInverted2": "#f2f0ee", "fgFill": "#000", - "fgFillDark": "#f2f0ee", "fgFillInverted": "#201d1b", - "fgSurface1": "#643d1b", "fgSurface2": "#805735", - "fgSurface3": "#d7aa86", - "fgSurface4": "#ffe8cc", + "fgSurface3": "#9e714d", + "fgSurface4": "#f0d9c8", + "fgSurface5": "#fefdfd", "stroke1": "#755d4a", - "stroke2": "#846954", + "stroke2": "#836a56", "stroke3": "#d7aa86", "stroke4": "#ffd3ae", "surface1": "#000", "surface2": "#060301", - "surface3": "#180d06", - "surface4": "#20130a", + "surface3": "#160b05", + "surface4": "#1a0e06", "surface5": "#281a0f", "surface6": "#372518", }, @@ -485,25 +463,23 @@ exports[`buildRamps > accent ramp snapshots for admin themes 1`] = ` "ramp": { "bgFill1": "#976c4a", "bgFill2": "#a67b58", - "bgFillDark": "#000", "bgFillInverted1": "#ffd2ad", "bgFillInverted2": "#f2f0ee", "fgFill": "#000", - "fgFillDark": "#f2f0ee", "fgFillInverted": "#201d1b", - "fgSurface1": "#6d4524", - "fgSurface2": "#895f3e", - "fgSurface3": "#d7aa86", - "fgSurface4": "#ffe8cc", + "fgSurface2": "#8a603f", + "fgSurface3": "#ab7b55", + "fgSurface4": "#f1dbca", + "fgSurface5": "#fefdfd", "stroke1": "#755d4a", - "stroke2": "#846954", + "stroke2": "#836a56", "stroke3": "#d7aa86", "stroke4": "#ffd3ae", - "surface1": "#130904", + "surface1": "#140a04", "surface2": "#1f1309", "surface3": "#291b0f", - "surface4": "#2e1f12", - "surface5": "#352416", + "surface4": "#2b1c10", + "surface5": "#362517", "surface6": "#422e1e", }, "warnings": undefined, @@ -527,25 +503,23 @@ exports[`buildRamps > accent ramp snapshots for admin themes 1`] = ` "ramp": { "bgFill1": "#976c4a", "bgFill2": "#a67b58", - "bgFillDark": "#000", "bgFillInverted1": "#ffd2ad", "bgFillInverted2": "#f2f0ee", "fgFill": "#000", - "fgFillDark": "#f2f0ee", "fgFillInverted": "#201d1b", - "fgSurface1": "#7f5735", "fgSurface2": "#9d7250", - "fgSurface3": "#d7aa86", - "fgSurface4": "#ffe8cc", + "fgSurface3": "#c58e63", + "fgSurface4": "#f3e1d4", + "fgSurface5": "#fefdfd", "stroke1": "#755d4a", - "stroke2": "#846954", + "stroke2": "#836a56", "stroke3": "#d7aa86", "stroke4": "#ffd3ae", "surface1": "#352417", "surface2": "#3c291b", "surface3": "#422e1e", - "surface4": "#463221", - "surface5": "#4c3624", + "surface4": "#453120", + "surface5": "#4e3826", "surface6": "#583f2b", }, "warnings": undefined, @@ -569,25 +543,23 @@ exports[`buildRamps > accent ramp snapshots for admin themes 1`] = ` "ramp": { "bgFill1": "#976c4a", "bgFill2": "#a67b58", - "bgFillDark": "#000", "bgFillInverted1": "#ffd2ad", "bgFillInverted2": "#f2f0ee", "fgFill": "#000", - "fgFillDark": "#f2f0ee", "fgFillInverted": "#201d1b", - "fgSurface1": "#9c724f", "fgSurface2": "#bc906d", - "fgSurface3": "#e1b28e", - "fgSurface4": "#ffe8cc", + "fgSurface3": "#e3b28c", + "fgSurface4": "#f5e5d9", + "fgSurface5": "#fefefd", "stroke1": "#7d634f", - "stroke2": "#906e53", + "stroke2": "#8d725c", "stroke3": "#e1b28e", "stroke4": "#ffe0bd", "surface1": "#553d2a", "surface2": "#5b422d", "surface3": "#614631", "surface4": "#654933", - "surface5": "#6a4d36", + "surface5": "#6d5039", "surface6": "#76573e", }, "warnings": undefined, @@ -611,25 +583,23 @@ exports[`buildRamps > accent ramp snapshots for admin themes 1`] = ` "ramp": { "bgFill1": "#916745", "bgFill2": "#835a38", - "bgFillDark": "#030302", "bgFillInverted1": "#351200", "bgFillInverted2": "#030302", "fgFill": "#f7f5f4", - "fgFillDark": "#f2efee", "fgFillInverted": "#f2efee", - "fgSurface1": "#845b39", "fgSurface2": "#68411f", - "fgSurface3": "#4b2601", - "fgSurface4": "#0b0000", + "fgSurface3": "#402b1b", + "fgSurface4": "#312013", + "fgSurface5": "#000", "stroke1": "#8e705a", - "stroke2": "#7f644f", + "stroke2": "#81644e", "stroke3": "#4b2601", "stroke4": "#140200", "surface1": "#bd8d67", "surface2": "#c4936b", "surface3": "#cc9970", - "surface4": "#b78964", - "surface5": "#b18460", + "surface4": "#b88a65", + "surface5": "#ae815d", "surface6": "#a37957", }, "warnings": undefined, @@ -653,25 +623,23 @@ exports[`buildRamps > accent ramp snapshots for admin themes 1`] = ` "ramp": { "bgFill1": "#916745", "bgFill2": "#835a38", - "bgFillDark": "#201d1b", "bgFillInverted1": "#492400", "bgFillInverted2": "#201d1b", "fgFill": "#f7f5f4", - "fgFillDark": "#f2efee", "fgFillInverted": "#f2efee", - "fgSurface1": "#9f7451", "fgSurface2": "#815836", - "fgSurface3": "#653e1c", - "fgSurface4": "#361300", + "fgSurface3": "#5d412a", + "fgSurface4": "#513824", + "fgSurface5": "#000", "stroke1": "#ad8a6f", - "stroke2": "#9c7c63", + "stroke2": "#9d7c61", "stroke3": "#653e1c", "stroke4": "#462200", - "surface1": "#deab83", + "surface1": "#ddaa83", "surface2": "#e1b38f", - "surface3": "#e5bc9c", - "surface4": "#daa67d", - "surface5": "#d4a076", + "surface3": "#e4bb9b", + "surface4": "#dba77e", + "surface5": "#d19d73", "surface6": "#c5946c", }, "warnings": undefined, @@ -695,25 +663,23 @@ exports[`buildRamps > accent ramp snapshots for admin themes 1`] = ` "ramp": { "bgFill1": "#916745", "bgFill2": "#835a38", - "bgFillDark": "#201d1b", "bgFillInverted1": "#492400", "bgFillInverted2": "#201d1b", "fgFill": "#f7f5f4", - "fgFillDark": "#f2efee", "fgFillInverted": "#f2efee", - "fgSurface1": "#b98d6a", "fgSurface2": "#9a6f4d", - "fgSurface3": "#7c5432", - "fgSurface4": "#361300", + "fgSurface3": "#795539", + "fgSurface4": "#6c4c32", + "fgSurface5": "#1d1108", "stroke1": "#cca486", - "stroke2": "#ba9578", + "stroke2": "#bc9577", "stroke3": "#7c5432", "stroke4": "#5f3917", "surface1": "#eed0b9", "surface2": "#f2d9c6", "surface3": "#f6e2d3", - "surface4": "#ebcab0", - "surface5": "#e8c2a5", + "surface4": "#edccb2", + "surface5": "#e5bfa2", "surface6": "#e1b28d", }, "warnings": undefined, @@ -737,25 +703,23 @@ exports[`buildRamps > accent ramp snapshots for admin themes 1`] = ` "ramp": { "bgFill1": "#916745", "bgFill2": "#835a38", - "bgFillDark": "#201d1b", "bgFillInverted1": "#492400", "bgFillInverted2": "#201d1b", "fgFill": "#f7f5f4", - "fgFillDark": "#f2efee", "fgFillInverted": "#f2efee", - "fgSurface1": "#d4a783", "fgSurface2": "#b28663", - "fgSurface3": "#916745", - "fgSurface4": "#361300", + "fgSurface3": "#956a47", + "fgSurface4": "#6f4e33", + "fgSurface5": "#1d1108", "stroke1": "#dfc1aa", - "stroke2": "#d4ae90", + "stroke2": "#cdae96", "stroke3": "#916745", "stroke4": "#734b2a", - "surface1": "#fff5ed", + "surface1": "#fff9f1", "surface2": "#fff", "surface3": "#fff", - "surface4": "#fbeee4", - "surface5": "#f8e6d8", + "surface4": "#fdf0e6", + "surface5": "#f4e2d4", "surface6": "#efd3be", }, "warnings": undefined, @@ -781,25 +745,23 @@ exports[`buildRamps > accent ramp snapshots for admin themes 1`] = ` "ramp": { "bgFill1": "#646c3e", "bgFill2": "#717a4b", - "bgFillDark": "#000", "bgFillInverted1": "#d6e0ae", "bgFillInverted2": "#eff2e3", "fgFill": "#fff", - "fgFillDark": "#eff2e3", "fgFillInverted": "#1e1e1c", - "fgSurface1": "#41481b", "fgSurface2": "#5b6235", - "fgSurface3": "#b1ba89", - "fgSurface4": "#edf7c3", + "fgSurface3": "#727b47", + "fgSurface4": "#d5e58a", + "fgSurface5": "#fdfef8", "stroke1": "#61664b", - "stroke2": "#6d7355", + "stroke2": "#6d7357", "stroke3": "#b1ba89", "stroke4": "#d9e4b1", "surface1": "#000", "surface2": "#000", - "surface3": "#0c0d05", - "surface4": "#13150a", - "surface5": "#1a1c0f", + "surface3": "#040401", + "surface4": "#0a0c03", + "surface5": "#191b0e", "surface6": "#262918", }, "warnings": undefined, @@ -823,25 +785,23 @@ exports[`buildRamps > accent ramp snapshots for admin themes 1`] = ` "ramp": { "bgFill1": "#646c3e", "bgFill2": "#717a4b", - "bgFillDark": "#000", "bgFillInverted1": "#d6e0ae", "bgFillInverted2": "#eff2e3", "fgFill": "#fff", - "fgFillDark": "#eff2e3", "fgFillInverted": "#1e1e1c", - "fgSurface1": "#434a1c", "fgSurface2": "#5c6436", - "fgSurface3": "#b1ba89", - "fgSurface4": "#edf7c3", + "fgSurface3": "#767f4a", + "fgSurface4": "#d5e58a", + "fgSurface5": "#fdfef8", "stroke1": "#61664b", - "stroke2": "#6d7355", + "stroke2": "#6d7357", "stroke3": "#b1ba89", "stroke4": "#d9e4b1", "surface1": "#000", "surface2": "#030401", - "surface3": "#0f1107", - "surface4": "#15180c", - "surface5": "#1c1e10", + "surface3": "#0d0f06", + "surface4": "#101207", + "surface5": "#1d1f11", "surface6": "#282b19", }, "warnings": undefined, @@ -865,25 +825,23 @@ exports[`buildRamps > accent ramp snapshots for admin themes 1`] = ` "ramp": { "bgFill1": "#646c3e", "bgFill2": "#717a4b", - "bgFillDark": "#000", "bgFillInverted1": "#d6e0ae", "bgFillInverted2": "#eff2e3", "fgFill": "#fff", - "fgFillDark": "#eff2e3", "fgFillInverted": "#1e1e1c", - "fgSurface1": "#4b5225", "fgSurface2": "#656d3f", - "fgSurface3": "#b1ba89", - "fgSurface4": "#edf7c3", + "fgSurface3": "#808a51", + "fgSurface4": "#d7e78b", + "fgSurface5": "#fdfef8", "stroke1": "#61664b", - "stroke2": "#6d7355", + "stroke2": "#6d7357", "stroke3": "#b1ba89", "stroke4": "#d9e4b1", - "surface1": "#0c0d05", + "surface1": "#0d0e05", "surface2": "#15170b", "surface3": "#1d1f11", - "surface4": "#212414", - "surface5": "#262918", + "surface4": "#1e2111", + "surface5": "#282b19", "surface6": "#31341f", }, "warnings": undefined, @@ -907,25 +865,23 @@ exports[`buildRamps > accent ramp snapshots for admin themes 1`] = ` "ramp": { "bgFill1": "#646c3e", "bgFill2": "#717a4b", - "bgFillDark": "#000", "bgFillInverted1": "#d6e0ae", "bgFillInverted2": "#eff2e3", "fgFill": "#fff", - "fgFillDark": "#eff2e3", "fgFillInverted": "#1e1e1c", - "fgSurface1": "#5d6537", "fgSurface2": "#788152", - "fgSurface3": "#b1ba89", - "fgSurface4": "#edf7c3", + "fgSurface3": "#95a15f", + "fgSurface4": "#dded8e", + "fgSurface5": "#fdfef8", "stroke1": "#61664b", - "stroke2": "#6d7355", + "stroke2": "#6d7357", "stroke3": "#b1ba89", "stroke4": "#d9e4b1", "surface1": "#272b19", "surface2": "#2c301c", "surface3": "#323520", - "surface4": "#353923", - "surface5": "#3a3e26", + "surface4": "#343822", + "surface5": "#3c4028", "surface6": "#43482d", }, "warnings": undefined, @@ -949,25 +905,23 @@ exports[`buildRamps > accent ramp snapshots for admin themes 1`] = ` "ramp": { "bgFill1": "#646c3e", "bgFill2": "#717a4b", - "bgFillDark": "#000", "bgFillInverted1": "#d6e0ae", "bgFillInverted2": "#eff2e3", "fgFill": "#fff", - "fgFillDark": "#eff2e3", "fgFillInverted": "#1e1e1c", - "fgSurface1": "#788052", - "fgSurface2": "#969f6f", - "fgSurface3": "#b9c391", - "fgSurface4": "#edf7c3", + "fgSurface2": "#97a070", + "fgSurface3": "#b7c576", + "fgSurface4": "#e0f090", + "fgSurface5": "#fdfef8", "stroke1": "#676d4f", - "stroke2": "#737a5a", + "stroke2": "#737a5b", "stroke3": "#b9c391", "stroke4": "#e3edba", "surface1": "#41462b", "surface2": "#464b2f", "surface3": "#4b5032", - "surface4": "#4e5435", - "surface5": "#525838", + "surface4": "#4d5334", + "surface5": "#545a3a", "surface6": "#5c623f", }, "warnings": undefined, @@ -991,25 +945,23 @@ exports[`buildRamps > accent ramp snapshots for admin themes 1`] = ` "ramp": { "bgFill1": "#646c3e", "bgFill2": "#575f31", - "bgFillDark": "#080908", "bgFillInverted1": "#1d2200", "bgFillInverted2": "#080908", "fgFill": "#eff2e3", - "fgFillDark": "#eff2e3", "fgFillInverted": "#eff2e3", - "fgSurface1": "#626a3c", "fgSurface2": "#495023", - "fgSurface3": "#2f3505", - "fgSurface4": "#070a00", + "fgSurface3": "#30351b", + "fgSurface4": "#252914", + "fgSurface5": "#000", "stroke1": "#767c5c", - "stroke2": "#696e51", + "stroke2": "#696f4f", "stroke3": "#2f3505", "stroke4": "#0b0e00", "surface1": "#959f68", "surface2": "#9ba56d", "surface3": "#a1ac71", - "surface4": "#909a65", - "surface5": "#8b9562", + "surface4": "#929c67", + "surface5": "#889260", "surface6": "#808959", }, "warnings": undefined, @@ -1033,25 +985,23 @@ exports[`buildRamps > accent ramp snapshots for admin themes 1`] = ` "ramp": { "bgFill1": "#646c3e", "bgFill2": "#575f31", - "bgFillDark": "#1e1e1c", "bgFillInverted1": "#2b3101", "bgFillInverted2": "#1e1e1c", "fgFill": "#eff2e3", - "fgFillDark": "#eff2e3", "fgFillInverted": "#eff2e3", - "fgSurface1": "#7b8455", "fgSurface2": "#60673a", - "fgSurface3": "#464d20", - "fgSurface4": "#1c2100", + "fgSurface3": "#464c2a", + "fgSurface4": "#3d4324", + "fgSurface5": "#000", "stroke1": "#909770", - "stroke2": "#828865", + "stroke2": "#828863", "stroke3": "#464d20", "stroke4": "#2b3101", "surface1": "#b3be7e", "surface2": "#b9c683", "surface3": "#c1cd88", - "surface4": "#aeb97b", - "surface5": "#a8b376", + "surface4": "#b0bb7d", + "surface5": "#a6b174", "surface6": "#9ca66d", }, "warnings": undefined, @@ -1075,25 +1025,23 @@ exports[`buildRamps > accent ramp snapshots for admin themes 1`] = ` "ramp": { "bgFill1": "#646c3e", "bgFill2": "#575f31", - "bgFillDark": "#1e1e1c", "bgFillInverted1": "#2b3101", "bgFillInverted2": "#1e1e1c", "fgFill": "#eff2e3", - "fgFillDark": "#eff2e3", "fgFillInverted": "#eff2e3", - "fgSurface1": "#949d6d", "fgSurface2": "#767e4f", - "fgSurface3": "#5a6234", - "fgSurface4": "#1c2100", + "fgSurface3": "#5b6238", + "fgSurface4": "#515831", + "fgSurface5": "#131508", "stroke1": "#aab386", - "stroke2": "#9aa279", + "stroke2": "#9aa277", "stroke3": "#5a6234", "stroke4": "#404719", "surface1": "#d1df94", "surface2": "#d9e79a", "surface3": "#e1f0a0", - "surface4": "#ccd991", - "surface5": "#c5d28c", + "surface4": "#cedb93", + "surface5": "#c2cf89", "surface6": "#b7c382", }, "warnings": undefined, @@ -1117,25 +1065,23 @@ exports[`buildRamps > accent ramp snapshots for admin themes 1`] = ` "ramp": { "bgFill1": "#646c3e", "bgFill2": "#575f31", - "bgFillDark": "#1e1e1c", "bgFillInverted1": "#2b3101", "bgFillInverted2": "#1e1e1c", "fgFill": "#eff2e3", - "fgFillDark": "#eff2e3", "fgFillInverted": "#eff2e3", - "fgSurface1": "#abb484", "fgSurface2": "#8a9363", - "fgSurface3": "#646c3e", - "fgSurface4": "#1c2100", + "fgSurface3": "#6e7745", + "fgSurface4": "#525832", + "fgSurface5": "#131508", "stroke1": "#b7c090", - "stroke2": "#a6af83", + "stroke2": "#a6b081", "stroke3": "#646c3e", "stroke4": "#495023", - "surface1": "#effead", + "surface1": "#f4ffb2", "surface2": "#fff", "surface3": "#fff", - "surface4": "#e9f8a6", - "surface5": "#e2f0a0", + "surface4": "#ecfba9", + "surface5": "#dfed9d", "surface6": "#d2df95", }, "warnings": undefined, @@ -1161,25 +1107,23 @@ exports[`buildRamps > accent ramp snapshots for admin themes 1`] = ` "ramp": { "bgFill1": "#ad631e", "bgFill2": "#bd7230", - "bgFillDark": "#000", "bgFillInverted1": "#ffd3a7", "bgFillInverted2": "#f3efed", "fgFill": "#000", - "fgFillDark": "#f3efed", "fgFillInverted": "#211d1b", - "fgSurface1": "#733200", "fgSurface2": "#934c00", - "fgSurface3": "#efa060", - "fgSurface4": "#ffe8cd", + "fgSurface3": "#ae641e", + "fgSurface4": "#fcd6ba", + "fgSurface5": "#fffdfc", "stroke1": "#7a5a41", - "stroke2": "#8a664a", + "stroke2": "#89674d", "stroke3": "#efa060", "stroke4": "#ffd1a5", "surface1": "#000", "surface2": "#000", - "surface3": "#180a02", - "surface4": "#201005", - "surface5": "#291708", + "surface3": "#0c0200", + "surface4": "#160701", + "surface5": "#281607", "surface6": "#392310", }, "warnings": undefined, @@ -1203,25 +1147,23 @@ exports[`buildRamps > accent ramp snapshots for admin themes 1`] = ` "ramp": { "bgFill1": "#ad631e", "bgFill2": "#bd7230", - "bgFillDark": "#000", "bgFillInverted1": "#ffd3a7", "bgFillInverted2": "#f3efed", "fgFill": "#000", - "fgFillDark": "#f3efed", "fgFillInverted": "#211d1b", - "fgSurface1": "#753300", "fgSurface2": "#954d00", - "fgSurface3": "#efa060", - "fgSurface4": "#ffe8cd", + "fgSurface3": "#b3671f", + "fgSurface4": "#fcd6ba", + "fgSurface5": "#fffdfc", "stroke1": "#7a5a41", - "stroke2": "#8a664a", + "stroke2": "#89674d", "stroke3": "#efa060", "stroke4": "#ffd1a5", "surface1": "#000", "surface2": "#070200", - "surface3": "#1a0c03", - "surface4": "#221206", - "surface5": "#2b1809", + "surface3": "#170902", + "surface4": "#1c0d03", + "surface5": "#2c190a", "surface6": "#3b2411", }, "warnings": undefined, @@ -1245,25 +1187,23 @@ exports[`buildRamps > accent ramp snapshots for admin themes 1`] = ` "ramp": { "bgFill1": "#ad631e", "bgFill2": "#bd7230", - "bgFillDark": "#000", "bgFillInverted1": "#ffd3a7", "bgFillInverted2": "#f3efed", "fgFill": "#000", - "fgFillDark": "#f3efed", "fgFillInverted": "#211d1b", - "fgSurface1": "#803a00", - "fgSurface2": "#9e5606", - "fgSurface3": "#efa060", - "fgSurface4": "#ffe8cd", + "fgSurface2": "#9f5707", + "fgSurface3": "#c47123", + "fgSurface4": "#fcd8bd", + "fgSurface5": "#fffdfc", "stroke1": "#7a5a41", - "stroke2": "#8a664a", + "stroke2": "#89674d", "stroke3": "#efa060", "stroke4": "#ffd1a5", - "surface1": "#150802", + "surface1": "#160902", "surface2": "#221205", - "surface3": "#2c190a", - "surface4": "#321d0c", - "surface5": "#392210", + "surface3": "#2d1a0b", + "surface4": "#2f1b0a", + "surface5": "#3b2412", "surface6": "#472c17", }, "warnings": undefined, @@ -1287,25 +1227,23 @@ exports[`buildRamps > accent ramp snapshots for admin themes 1`] = ` "ramp": { "bgFill1": "#ad631e", "bgFill2": "#bd7230", - "bgFillDark": "#000", "bgFillInverted1": "#ffd3a7", "bgFillInverted2": "#f3efed", "fgFill": "#000", - "fgFillDark": "#f3efed", "fgFillInverted": "#211d1b", - "fgSurface1": "#944c00", "fgSurface2": "#b36925", - "fgSurface3": "#efa060", - "fgSurface4": "#ffe8cd", + "fgSurface3": "#e1822a", + "fgSurface4": "#fddfc9", + "fgSurface5": "#fffdfc", "stroke1": "#7a5a41", - "stroke2": "#8a664a", + "stroke2": "#89674d", "stroke3": "#efa060", "stroke4": "#ffd1a5", "surface1": "#392210", "surface2": "#402714", "surface3": "#472c17", - "surface4": "#4c3019", - "surface5": "#51341c", + "surface4": "#4a2e17", + "surface5": "#53361e", "surface6": "#5e3d22", }, "warnings": undefined, @@ -1329,25 +1267,23 @@ exports[`buildRamps > accent ramp snapshots for admin themes 1`] = ` "ramp": { "bgFill1": "#ad631e", "bgFill2": "#bd7230", - "bgFillDark": "#000", "bgFillInverted1": "#ffd3a7", "bgFillInverted2": "#f3efed", "fgFill": "#000", - "fgFillDark": "#f3efed", "fgFillInverted": "#211d1b", - "fgSurface1": "#b26824", - "fgSurface2": "#d38646", + "fgSurface2": "#d48747", "fgSurface3": "#f9a96a", - "fgSurface4": "#ffe8cd", + "fgSurface4": "#fde3d0", + "fgSurface5": "#fffdfc", "stroke1": "#836046", - "stroke2": "#946c4e", + "stroke2": "#926e53", "stroke3": "#f9a96a", "stroke4": "#ffdfbe", - "surface1": "#5a3a21", + "surface1": "#5b3a21", "surface2": "#613f24", - "surface3": "#674327", - "surface4": "#6b4729", - "surface5": "#714a2c", + "surface3": "#684327", + "surface4": "#6a4628", + "surface5": "#744d2e", "surface6": "#7e5332", }, "warnings": undefined, @@ -1371,25 +1307,23 @@ exports[`buildRamps > accent ramp snapshots for admin themes 1`] = ` "ramp": { "bgFill1": "#ad631e", "bgFill2": "#9e5505", - "bgFillDark": "#020201", "bgFillInverted1": "#361000", "bgFillInverted2": "#020201", "fgFill": "#fff", - "fgFillDark": "#f3efed", "fgFillInverted": "#f3efed", - "fgSurface1": "#995000", "fgSurface2": "#7a3600", - "fgSurface3": "#522000", - "fgSurface4": "#070000", + "fgSurface3": "#492607", + "fgSurface4": "#391d04", + "fgSurface5": "#000", "stroke1": "#986c4a", - "stroke2": "#846147", + "stroke2": "#875d3e", "stroke3": "#522000", "stroke4": "#110100", "surface1": "#c78857", "surface2": "#cf8e5b", "surface3": "#d7945f", - "surface4": "#c18454", - "surface5": "#bb7f51", + "surface4": "#c38656", + "surface5": "#b87d4f", "surface6": "#ac7549", }, "warnings": undefined, @@ -1413,25 +1347,23 @@ exports[`buildRamps > accent ramp snapshots for admin themes 1`] = ` "ramp": { "bgFill1": "#ad631e", "bgFill2": "#9e5505", - "bgFillDark": "#211d1b", "bgFillInverted1": "#501f00", "bgFillInverted2": "#211d1b", "fgFill": "#fff", - "fgFillDark": "#f3efed", "fgFillInverted": "#f3efed", - "fgSurface1": "#b56a27", "fgSurface2": "#964e00", - "fgSurface3": "#763400", - "fgSurface4": "#371100", + "fgSurface3": "#6b3b0e", + "fgSurface4": "#5d320b", + "fgSurface5": "#000", "stroke1": "#b48764", - "stroke2": "#a3795a", + "stroke2": "#a57958", "stroke3": "#763400", "stroke4": "#4d1c00", "surface1": "#e8a775", "surface2": "#eab083", "surface3": "#edb891", - "surface4": "#e4a16e", - "surface5": "#df9b66", + "surface4": "#e6a370", + "surface5": "#dc9863", "surface6": "#d08f5b", }, "warnings": undefined, @@ -1455,25 +1387,23 @@ exports[`buildRamps > accent ramp snapshots for admin themes 1`] = ` "ramp": { "bgFill1": "#ad631e", "bgFill2": "#9e5505", - "bgFillDark": "#211d1b", "bgFillInverted1": "#501f00", "bgFillInverted2": "#211d1b", "fgFill": "#fff", - "fgFillDark": "#f3efed", "fgFillInverted": "#f3efed", - "fgSurface1": "#d18444", "fgSurface2": "#b06521", - "fgSurface3": "#914900", - "fgSurface4": "#371100", + "fgSurface3": "#894d15", + "fgSurface4": "#7c4512", + "fgSurface5": "#210e02", "stroke1": "#d5a27b", - "stroke2": "#c3926d", + "stroke2": "#c4926c", "stroke3": "#914900", "stroke4": "#6e2f00", - "surface1": "#f3ceb2", + "surface1": "#f2cdb2", "surface2": "#f6d7c0", - "surface3": "#fae1cf", - "surface4": "#f1c8a9", - "surface5": "#efc09d", + "surface3": "#f9e0ce", + "surface4": "#f2c9aa", + "surface5": "#ecbd9a", "surface6": "#eaae81", }, "warnings": undefined, @@ -1497,25 +1427,23 @@ exports[`buildRamps > accent ramp snapshots for admin themes 1`] = ` "ramp": { "bgFill1": "#ad631e", "bgFill2": "#9e5505", - "bgFillDark": "#211d1b", "bgFillInverted1": "#501f00", "bgFillInverted2": "#211d1b", "fgFill": "#fff", - "fgFillDark": "#f3efed", "fgFillInverted": "#f3efed", - "fgSurface1": "#ed9d5e", "fgSurface2": "#c87c3c", - "fgSurface3": "#a85e17", - "fgSurface4": "#371100", + "fgSurface3": "#a9611d", + "fgSurface4": "#7d4613", + "fgSurface5": "#210e02", "stroke1": "#e6c1a6", - "stroke2": "#ddad88", + "stroke2": "#d3ac8f", "stroke3": "#a85e17", "stroke4": "#884100", - "surface1": "#fff4eb", + "surface1": "#fff8ef", "surface2": "#fff", "surface3": "#fff", - "surface4": "#feede1", - "surface5": "#fbe5d4", + "surface4": "#ffefe3", + "surface5": "#f7e2d1", "surface6": "#f5d2b9", }, "warnings": undefined, @@ -1548,7 +1476,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFill2": "#e6e6e6", "bgFillInverted1": "#dbdbdb", "bgFillInverted2": "#f0f0f0", - "fgFill": "#595959", + "fgFill": "#1e1e1e", "fgFillInverted": "#1e1e1e", "fgSurface2": "#5e5e5e", "fgSurface3": "#767676", @@ -1588,7 +1516,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFill2": "#e6e6e6", "bgFillInverted1": "#dbdbdb", "bgFillInverted2": "#f0f0f0", - "fgFill": "#595959", + "fgFill": "#1e1e1e", "fgFillInverted": "#1e1e1e", "fgSurface2": "#606060", "fgSurface3": "#7a7a7a", @@ -1628,7 +1556,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFill2": "#e6e6e6", "bgFillInverted1": "#dbdbdb", "bgFillInverted2": "#f0f0f0", - "fgFill": "#595959", + "fgFill": "#1e1e1e", "fgFillInverted": "#1e1e1e", "fgSurface2": "#696969", "fgSurface3": "#868686", @@ -1668,7 +1596,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFill2": "#e6e6e6", "bgFillInverted1": "#dbdbdb", "bgFillInverted2": "#f0f0f0", - "fgFill": "#595959", + "fgFill": "#1e1e1e", "fgFillInverted": "#1e1e1e", "fgSurface2": "#7c7c7c", "fgSurface3": "#9b9b9b", @@ -1708,7 +1636,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFill2": "#e6e6e6", "bgFillInverted1": "#dbdbdb", "bgFillInverted2": "#f0f0f0", - "fgFill": "#5a5a5a", + "fgFill": "#1e1e1e", "fgFillInverted": "#1e1e1e", "fgSurface2": "#9a9a9a", "fgSurface3": "#bebebe", @@ -1748,7 +1676,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFill2": "#e6e6e6", "bgFillInverted1": "#1e1e1e", "bgFillInverted2": "#050505", - "fgFill": "#666", + "fgFill": "#1e1e1e", "fgFillInverted": "#f0f0f0", "fgSurface2": "#4b4b4b", "fgSurface3": "#313131", @@ -1788,7 +1716,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFill2": "#e6e6e6", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#666", + "fgFill": "#1e1e1e", "fgFillInverted": "#f0f0f0", "fgSurface2": "#626262", "fgSurface3": "#484848", @@ -1828,7 +1756,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFill2": "#e6e6e6", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#666", + "fgFill": "#1e1e1e", "fgFillInverted": "#f0f0f0", "fgSurface2": "#787878", "fgSurface3": "#5d5d5d", @@ -1868,7 +1796,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFill2": "#e6e6e6", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#666", + "fgFill": "#1e1e1e", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", @@ -1911,20 +1839,20 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFillInverted1": "#c8dcff", "bgFillInverted2": "#eff0f2", "fgFill": "#fefeff", - "fgFillDark": "#eff0f2", "fgFillInverted": "#1a1e27", "fgSurface2": "#314ede", - "fgSurface3": "#8eb0ff", - "fgSurface4": "#e4f0ff", + "fgSurface3": "#496ceb", + "fgSurface4": "#d1ddfa", + "fgSurface5": "#fdfeff", "stroke1": "#435caa", - "stroke2": "#516ab3", + "stroke2": "#506ab9", "stroke3": "#8eb0ff", "stroke4": "#c6daff", "surface1": "#000", "surface2": "#000", - "surface3": "#04092b", - "surface4": "#070f39", - "surface5": "#0a1647", + "surface3": "#01001d", + "surface4": "#03042d", + "surface5": "#091546", "surface6": "#122161", }, "warnings": undefined, @@ -1951,19 +1879,19 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFillInverted1": "#c8dcff", "bgFillInverted2": "#eff0f2", "fgFill": "#fefeff", - "fgFillDark": "#eff0f2", "fgFillInverted": "#1a1e27", "fgSurface2": "#324fe0", - "fgSurface3": "#8eb0ff", - "fgSurface4": "#e4f0ff", + "fgSurface3": "#4d71ec", + "fgSurface4": "#d2ddfa", + "fgSurface5": "#fdfeff", "stroke1": "#435caa", - "stroke2": "#516ab3", + "stroke2": "#506ab9", "stroke3": "#8eb0ff", "stroke4": "#c6daff", "surface1": "#000", "surface2": "#010212", - "surface3": "#050b30", - "surface4": "#07113d", + "surface3": "#04082d", + "surface4": "#040a36", "surface5": "#0c174b", "surface6": "#132264", }, @@ -1991,21 +1919,20 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFillInverted1": "#c8dcff", "bgFillInverted2": "#eff0f2", "fgFill": "#fefeff", - "fgFillDark": "#eff0f2", - "fgFillInverted": "#1a1e28", - "fgSurface1": "#2439ca", - "fgSurface2": "#3858e9", - "fgSurface3": "#8eb0ff", - "fgSurface4": "#e4f0ff", + "fgFillInverted": "#1a1e27", + "fgSurface2": "#3959ea", + "fgSurface3": "#5b7eed", + "fgSurface4": "#d4dffa", + "fgSurface5": "#fdfeff", "stroke1": "#435caa", - "stroke2": "#516ab3", + "stroke2": "#506ab9", "stroke3": "#8eb0ff", "stroke4": "#c6daff", - "surface1": "#030725", + "surface1": "#030826", "surface2": "#07103b", - "surface3": "#0c174b", - "surface4": "#0e1b54", - "surface5": "#12205f", + "surface3": "#0d184c", + "surface4": "#0c1851", + "surface5": "#132261", "surface6": "#182a75", }, "warnings": undefined, @@ -2032,20 +1959,20 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFillInverted1": "#c8dcff", "bgFillInverted2": "#eff0f2", "fgFill": "#fefeff", - "fgFillDark": "#eff0f2", "fgFillInverted": "#1a1e27", "fgSurface2": "#486dff", - "fgSurface3": "#8eb0ff", - "fgSurface4": "#e4f0ff", + "fgSurface3": "#7696f0", + "fgSurface4": "#dbe5fb", + "fgSurface5": "#fdfeff", "stroke1": "#435caa", - "stroke2": "#516ab3", + "stroke2": "#506ab9", "stroke3": "#8eb0ff", "stroke4": "#c6daff", - "surface1": "#111f5e", + "surface1": "#12205f", "surface2": "#15256a", - "surface3": "#182974", - "surface4": "#1a2d7c", - "surface5": "#1d3185", + "surface3": "#192a75", + "surface4": "#192b7a", + "surface5": "#1f3387", "surface6": "#233999", }, "warnings": undefined, @@ -2072,20 +1999,20 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFillInverted1": "#c8dcff", "bgFillInverted2": "#eff0f2", "fgFill": "#fefeff", - "fgFillDark": "#eff0f2", "fgFillInverted": "#1a1e27", "fgSurface2": "#6a91ff", - "fgSurface3": "#99b8ff", - "fgSurface4": "#e4f0ff", + "fgSurface3": "#a0b8f5", + "fgSurface4": "#e0e8fb", + "fgSurface5": "#fdfeff", "stroke1": "#4962ad", - "stroke2": "#5870b4", + "stroke2": "#5771bc", "stroke3": "#99b8ff", "stroke4": "#d3e3ff", "surface1": "#213792", "surface2": "#243b9c", "surface3": "#273fa6", - "surface4": "#2942ad", - "surface5": "#2c46b5", + "surface4": "#2841ac", + "surface5": "#2e48b7", "surface6": "#3551bf", }, "warnings": undefined, @@ -2112,20 +2039,20 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFillInverted1": "#0b0070", "bgFillInverted2": "#030407", "fgFill": "#eff0f2", - "fgFillDark": "#eff0f2", "fgFillInverted": "#eff0f2", "fgSurface2": "#2235c6", - "fgSurface3": "#1504a6", - "fgSurface4": "#010023", + "fgSurface3": "#131b91", + "fgSurface4": "#0d1374", + "fgSurface5": "#000", "stroke1": "#5d74b6", - "stroke2": "#4864bf", + "stroke2": "#435aa3", "stroke3": "#1504a6", "stroke4": "#020032", "surface1": "#7e96d6", "surface2": "#869cd9", "surface3": "#8ea3db", - "surface4": "#7991d5", - "surface5": "#728cd3", + "surface4": "#7a92d6", + "surface5": "#6f89d0", "surface6": "#647fce", }, "warnings": undefined, @@ -2151,20 +2078,21 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFill2": "#2e49d9", "bgFillInverted1": "#13009f", "bgFillInverted2": "#1a1e27", - "fgFill": "#e7e8ea", + "fgFill": "#eff0f2", "fgFillInverted": "#eff0f2", "fgSurface2": "#3351e2", - "fgSurface3": "#2031c3", - "fgSurface4": "#0b0070", + "fgSurface3": "#202bce", + "fgSurface4": "#1b24b5", + "fgSurface5": "#000", "stroke1": "#7d91c4", - "stroke2": "#6c82bd", + "stroke2": "#6d81b5", "stroke3": "#2031c3", "stroke4": "#13009d", "surface1": "#a4b6e2", "surface2": "#adbde5", "surface3": "#b6c4e8", - "surface4": "#9eb1e1", - "surface5": "#97abde", + "surface4": "#a0b3e3", + "surface5": "#94a8db", "surface6": "#879ed9", }, "warnings": undefined, @@ -2190,20 +2118,21 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFill2": "#2e49d9", "bgFillInverted1": "#13009f", "bgFillInverted2": "#1a1e27", - "fgFill": "#e7e8ea", + "fgFill": "#eff0f2", "fgFillInverted": "#eff0f2", "fgSurface2": "#466bfd", - "fgSurface3": "#2f4cdc", - "fgSurface4": "#0b0070", + "fgSurface3": "#2d47e7", + "fgSurface4": "#2636e6", + "fgSurface5": "#03063b", "stroke1": "#9dacd2", - "stroke2": "#8a9cca", + "stroke2": "#8b9bc2", "stroke3": "#2f4cdc", "stroke4": "#1d29bc", "surface1": "#cbd6ef", "surface2": "#d6def2", "surface3": "#e0e6f6", - "surface4": "#c5d0ed", - "surface5": "#bdcaea", + "surface4": "#c7d2ef", + "surface5": "#bac7e7", "surface6": "#abbbe4", }, "warnings": undefined, @@ -2229,20 +2158,21 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFill2": "#2e49d9", "bgFillInverted1": "#13009f", "bgFillInverted2": "#1a1e27", - "fgFill": "#e7e8ea", + "fgFill": "#eff0f2", "fgFillInverted": "#eff0f2", "fgSurface2": "#6088ff", - "fgSurface3": "#3858e9", - "fgSurface4": "#0b0070", + "fgSurface3": "#4669eb", + "fgSurface4": "#2531e5", + "fgSurface5": "#03063b", "stroke1": "#aebbda", - "stroke2": "#9aaad1", + "stroke2": "#9ba9c9", "stroke3": "#3858e9", "stroke4": "#2337c8", - "surface1": "#f4f7fd", + "surface1": "#f8fbff", "surface2": "#fff", "surface3": "#fff", - "surface4": "#ecf0fa", - "surface5": "#e3e9f7", + "surface4": "#eef2fc", + "surface5": "#e0e6f4", "surface6": "#cfd9f0", }, "warnings": undefined, @@ -2269,21 +2199,22 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFill1": "#0090ff", "bgFill2": "#37a3ff", "bgFillInverted1": "#b8e0ff", - "bgFillInverted2": "#eef0f3", - "fgFill": "#23282d", + "bgFillInverted2": "#eef0f2", + "fgFill": "#1a1f24", "fgFillInverted": "#1a1f24", "fgSurface2": "#005bbf", - "fgSurface3": "#0090ff", - "fgSurface4": "#dcf3ff", + "fgSurface3": "#0078d5", + "fgSurface4": "#c4e0ff", + "fgSurface5": "#fcfeff", "stroke1": "#27476a", - "stroke2": "#30547a", + "stroke2": "#325478", "stroke3": "#0090ff", "stroke4": "#67b7ff", "surface1": "#000", "surface2": "#000", - "surface3": "#000c22", - "surface4": "#00142d", - "surface5": "#011c37", + "surface3": "#000315", + "surface4": "#000a22", + "surface5": "#001b36", "surface6": "#07294a", }, "warnings": undefined, @@ -2308,21 +2239,22 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFill1": "#0090ff", "bgFill2": "#37a3ff", "bgFillInverted1": "#b8e0ff", - "bgFillInverted2": "#eef0f3", - "fgFill": "#23282d", + "bgFillInverted2": "#eef0f2", + "fgFill": "#1a1f24", "fgFillInverted": "#1a1f24", "fgSurface2": "#005dc1", - "fgSurface3": "#0090ff", - "fgSurface4": "#dcf3ff", + "fgSurface3": "#007cdd", + "fgSurface4": "#c5e0ff", + "fgSurface5": "#fcfeff", "stroke1": "#27476a", - "stroke2": "#30547a", + "stroke2": "#325478", "stroke3": "#0090ff", "stroke4": "#67b7ff", "surface1": "#000", "surface2": "#000312", - "surface3": "#001027", - "surface4": "#001730", - "surface5": "#021d3a", + "surface3": "#000e25", + "surface4": "#00112a", + "surface5": "#031e3b", "surface6": "#072a4c", }, "warnings": undefined, @@ -2347,22 +2279,22 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFill1": "#0090ff", "bgFill2": "#37a3ff", "bgFillInverted1": "#b8e0ff", - "bgFillInverted2": "#eef0f3", - "fgFill": "#23282d", + "bgFillInverted2": "#eef0f2", + "fgFill": "#1a1f24", "fgFillInverted": "#1a1f24", - "fgSurface1": "#004ba3", - "fgSurface2": "#0065d0", - "fgSurface3": "#0090ff", - "fgSurface4": "#dcf3ff", + "fgSurface2": "#0266d1", + "fgSurface3": "#0087f0", + "fgSurface4": "#c8e1ff", + "fgSurface5": "#fcfeff", "stroke1": "#27476a", - "stroke2": "#30547a", + "stroke2": "#325478", "stroke3": "#0090ff", "stroke4": "#67b7ff", - "surface1": "#000c21", + "surface1": "#000d22", "surface2": "#001630", - "surface3": "#021e3b", - "surface4": "#042342", - "surface5": "#062849", + "surface3": "#021f3c", + "surface4": "#02203f", + "surface5": "#082a4b", "surface6": "#0d3359", }, "warnings": undefined, @@ -2387,21 +2319,22 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFill1": "#0090ff", "bgFill2": "#37a3ff", "bgFillInverted1": "#b8e0ff", - "bgFillInverted2": "#eef0f3", - "fgFill": "#23282d", + "bgFillInverted2": "#eef0f2", + "fgFill": "#1a1f24", "fgFillInverted": "#1a1f24", "fgSurface2": "#007be8", - "fgSurface3": "#6bb9ff", - "fgSurface4": "#dcf3ff", + "fgSurface3": "#3d9eff", + "fgSurface4": "#d2e7ff", + "fgSurface5": "#fcfeff", "stroke1": "#3c6490", - "stroke2": "#4571a1", + "stroke2": "#48719f", "stroke3": "#6bb9ff", "stroke4": "#b8e0ff", "surface1": "#07294b", "surface2": "#0a2f53", - "surface3": "#0d345a", - "surface4": "#103860", - "surface5": "#133d67", + "surface3": "#0e345a", + "surface4": "#0e375f", + "surface5": "#153e69", "surface6": "#194675", }, "warnings": undefined, @@ -2426,21 +2359,22 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFill1": "#0090ff", "bgFill2": "#37a3ff", "bgFillInverted1": "#b8e0ff", - "bgFillInverted2": "#eef0f3", - "fgFill": "#23282d", + "bgFillInverted2": "#eef0f2", + "fgFill": "#1a1f24", "fgFillInverted": "#1a1f24", "fgSurface2": "#289eff", - "fgSurface3": "#7fc3ff", - "fgSurface4": "#dcf3ff", + "fgSurface3": "#89c1ff", + "fgSurface4": "#d7eaff", + "fgSurface5": "#fcfeff", "stroke1": "#416b9a", - "stroke2": "#4a79ab", + "stroke2": "#4c79a8", "stroke3": "#7fc3ff", "stroke4": "#cdebff", "surface1": "#174472", "surface2": "#1a4979", "surface3": "#1e4e80", - "surface4": "#205285", - "surface5": "#22568b", + "surface4": "#1f5184", + "surface5": "#25588e", "surface6": "#28609a", }, "warnings": undefined, @@ -2465,21 +2399,22 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFill1": "#0090ff", "bgFill2": "#007fed", "bgFillInverted1": "#001a4f", - "bgFillInverted2": "#050609", + "bgFillInverted2": "#040609", "fgFill": "#12161a", - "fgFillInverted": "#eef0f3", + "fgFillInverted": "#eef0f2", "fgSurface2": "#00489e", - "fgSurface3": "#002e70", - "fgSurface4": "#000327", + "fgSurface3": "#00325e", + "fgSurface4": "#00274c", + "fgSurface5": "#000", "stroke1": "#4b7aae", - "stroke2": "#426d9c", + "stroke2": "#3f6d9f", "stroke3": "#002e70", "stroke4": "#00062c", "surface1": "#609cdd", "surface2": "#6ba2df", "surface3": "#76a9e2", - "surface4": "#5a98db", - "surface5": "#4392e4", + "surface4": "#5b99dc", + "surface5": "#4090e1", "surface6": "#4087d0", }, "warnings": undefined, @@ -2506,19 +2441,20 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFillInverted1": "#002a69", "bgFillInverted2": "#1a1f24", "fgFill": "#12161a", - "fgFillInverted": "#eef0f3", + "fgFillInverted": "#eef0f2", "fgSurface2": "#005fc6", - "fgSurface3": "#004599", - "fgSurface4": "#001b4f", + "fgSurface3": "#004986", + "fgSurface4": "#003f75", + "fgSurface5": "#000", "stroke1": "#6896c8", - "stroke2": "#5587be", + "stroke2": "#5986b9", "stroke3": "#004599", "stroke4": "#002968", "surface1": "#92bae8", "surface2": "#9dc1ea", "surface3": "#a8c8ed", - "surface4": "#8bb6e6", - "surface5": "#81b0e4", + "surface4": "#8cb7e7", + "surface5": "#7eade1", "surface6": "#6da3e0", }, "warnings": undefined, @@ -2545,19 +2481,20 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFillInverted1": "#002a69", "bgFillInverted2": "#1a1f24", "fgFill": "#12161a", - "fgFillInverted": "#eef0f3", + "fgFillInverted": "#eef0f2", "fgSurface2": "#0077e4", - "fgSurface3": "#005abc", - "fgSurface4": "#001b4f", + "fgSurface3": "#005eaa", + "fgSurface4": "#00549a", + "fgSurface5": "#00132a", "stroke1": "#8db0d7", - "stroke2": "#77a0cf", + "stroke2": "#7b9ec7", "stroke3": "#005abc", "stroke4": "#003f8e", - "surface1": "#bdd9f9", + "surface1": "#c1d8f3", "surface2": "#cde0f6", "surface3": "#dae8f9", - "surface4": "#bad3f1", - "surface5": "#afcdef", + "surface4": "#bcd5f3", + "surface5": "#accaec", "surface6": "#9abfe9", }, "warnings": undefined, @@ -2584,19 +2521,20 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFillInverted1": "#002a69", "bgFillInverted2": "#1a1f24", "fgFill": "#12161a", - "fgFillInverted": "#eef0f3", + "fgFillInverted": "#eef0f2", "fgSurface2": "#0190ff", - "fgSurface3": "#006fdb", - "fgSurface4": "#001b4f", + "fgSurface3": "#0075d0", + "fgSurface4": "#005499", + "fgSurface5": "#00132a", "stroke1": "#b4cbe5", - "stroke2": "#9cbadc", + "stroke2": "#9fb7d3", "stroke3": "#006fdb", "stroke4": "#0052af", - "surface1": "#f0f7ff", + "surface1": "#f4fbff", "surface2": "#fff", "surface3": "#fff", - "surface4": "#e7f1fd", - "surface5": "#dceafa", + "surface4": "#e9f3ff", + "surface5": "#d9e7f7", "surface6": "#c5dbf4", }, "warnings": undefined, @@ -2625,20 +2563,20 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFillInverted1": "#86f29c", "bgFillInverted2": "#ecf2ec", "fgFill": "#1b1f1c", - "fgFillDark": "#ecf2ec", "fgFillInverted": "#1b1f1c", "fgSurface2": "#006e1e", - "fgSurface3": "#4ab866", - "fgSurface4": "#b9ffc6", + "fgSurface3": "#348648", + "fgSurface4": "#6af88d", + "fgSurface5": "#fafffb", "stroke1": "#3d5d42", - "stroke2": "#466a4c", + "stroke2": "#486a4e", "stroke3": "#4ab866", "stroke4": "#73df8b", "surface1": "#000", "surface2": "#000", - "surface3": "#030f05", - "surface4": "#07170a", - "surface5": "#0a1f0f", + "surface3": "#000501", + "surface4": "#020e03", + "surface5": "#091e0e", "surface6": "#122d18", }, "warnings": undefined, @@ -2665,20 +2603,20 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFillInverted1": "#86f29c", "bgFillInverted2": "#ecf2ec", "fgFill": "#1b1f1c", - "fgFillDark": "#ecf2ec", "fgFillInverted": "#1b1f1c", "fgSurface2": "#007020", - "fgSurface3": "#4ab866", - "fgSurface4": "#b9ffc6", + "fgSurface3": "#368a4b", + "fgSurface4": "#6af88d", + "fgSurface5": "#fafffb", "stroke1": "#3d5d42", - "stroke2": "#466a4c", + "stroke2": "#486a4e", "stroke3": "#4ab866", "stroke4": "#73df8b", "surface1": "#000", "surface2": "#010401", - "surface3": "#051307", - "surface4": "#081a0c", - "surface5": "#0b2110", + "surface3": "#031005", + "surface4": "#041407", + "surface5": "#0c2211", "surface6": "#132f19", }, "warnings": undefined, @@ -2705,21 +2643,20 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFillInverted1": "#86f29c", "bgFillInverted2": "#ecf2ec", "fgFill": "#1b1f1c", - "fgFillDark": "#ecf2ec", "fgFillInverted": "#1b1f1c", - "fgSurface1": "#005d11", "fgSurface2": "#007b2b", - "fgSurface3": "#4ab866", - "fgSurface4": "#b9ffc6", + "fgSurface3": "#3c9853", + "fgSurface4": "#78f895", + "fgSurface5": "#fafffb", "stroke1": "#3d5d42", - "stroke2": "#466a4c", + "stroke2": "#486a4e", "stroke3": "#4ab866", "stroke4": "#73df8b", "surface1": "#051107", "surface2": "#081b0c", - "surface3": "#0d2312", - "surface4": "#0f2815", - "surface5": "#132e19", + "surface3": "#0e2413", + "surface4": "#0d2613", + "surface5": "#15301b", "surface6": "#1a3a21", }, "warnings": undefined, @@ -2746,20 +2683,20 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFillInverted1": "#86f29c", "bgFillInverted2": "#ecf2ec", "fgFill": "#1b1f1c", - "fgFillDark": "#ecf2ec", "fgFillInverted": "#1b1f1c", "fgSurface2": "#149141", - "fgSurface3": "#4ab866", - "fgSurface4": "#b9ffc6", + "fgSurface3": "#47b062", + "fgSurface4": "#9df9ae", + "fgSurface5": "#fafffb", "stroke1": "#3d5d42", - "stroke2": "#466a4c", + "stroke2": "#486a4e", "stroke3": "#4ab866", "stroke4": "#73df8b", "surface1": "#14301a", "surface2": "#17351e", "surface3": "#1b3b22", - "surface4": "#1e3f25", - "surface5": "#214428", + "surface4": "#1c3d23", + "surface5": "#224529", "surface6": "#274e2f", }, "warnings": undefined, @@ -2786,21 +2723,20 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFillInverted1": "#86f29c", "bgFillInverted2": "#ecf2ec", "fgFill": "#1b1f1c", - "fgFillDark": "#ecf2ec", "fgFillInverted": "#1b1f1c", - "fgSurface1": "#149141", - "fgSurface2": "#42b15f", - "fgSurface3": "#69d682", - "fgSurface4": "#b9ffc6", + "fgSurface2": "#43b260", + "fgSurface3": "#59d979", + "fgSurface4": "#aafab8", + "fgSurface5": "#fafffb", "stroke1": "#4d7454", - "stroke2": "#57825e", + "stroke2": "#5a8260", "stroke3": "#69d682", "stroke4": "#a0ffb4", - "surface1": "#254d2e", + "surface1": "#264d2e", "surface2": "#2a5232", "surface3": "#2d5836", - "surface4": "#2f5b38", - "surface5": "#32603c", + "surface4": "#2e5a37", + "surface5": "#34623e", "surface6": "#396b43", }, "warnings": undefined, @@ -2827,20 +2763,20 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFillInverted1": "#002a00", "bgFillInverted2": "#0d0f0d", "fgFill": "#1b1f1c", - "fgFillDark": "#ecf2ec", "fgFillInverted": "#ecf2ec", "fgSurface2": "#005c11", - "fgSurface3": "#003e03", - "fgSurface4": "#001300", + "fgSurface3": "#133c1d", + "fgSurface4": "#0e3016", + "fgSurface5": "#000", "stroke1": "#598561", - "stroke2": "#4f7756", + "stroke2": "#4d7855", "stroke3": "#003e03", "stroke4": "#001600", "surface1": "#63ac71", "surface2": "#59b66e", "surface3": "#6cba7b", - "surface4": "#60a76e", - "surface5": "#5ca26a", + "surface4": "#62a96f", + "surface5": "#599f67", "surface6": "#549561", }, "warnings": undefined, @@ -2867,20 +2803,20 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFillInverted1": "#003701", "bgFillInverted2": "#1b1f1c", "fgFill": "#1b1f1c", - "fgFillDark": "#ecf2ec", "fgFillInverted": "#ecf2ec", "fgSurface2": "#007627", - "fgSurface3": "#00580f", - "fgSurface4": "#002900", + "fgSurface3": "#1e552c", + "fgSurface4": "#194b26", + "fgSurface5": "#000", "stroke1": "#6ea277", - "stroke2": "#63926b", + "stroke2": "#609369", "stroke3": "#00580f", "stroke4": "#003902", "surface1": "#78cd89", "surface2": "#7dd58e", "surface3": "#82dd94", - "surface4": "#74c885", - "surface5": "#70c280", + "surface4": "#76ca86", + "surface5": "#6dbf7d", "surface6": "#5ab66f", }, "warnings": undefined, @@ -2907,20 +2843,20 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFillInverted1": "#003701", "bgFillInverted2": "#1b1f1c", "fgFill": "#1b1f1c", - "fgFillDark": "#ecf2ec", "fgFillInverted": "#ecf2ec", "fgSurface2": "#0f8e3f", - "fgSurface3": "#007020", - "fgSurface4": "#002900", + "fgSurface3": "#286d3a", + "fgSurface4": "#246233", + "fgSurface5": "#041708", "stroke1": "#83be8d", - "stroke2": "#6daf79", + "stroke2": "#71ad7b", "stroke3": "#007020", "stroke4": "#00510c", - "surface1": "#9beba9", + "surface1": "#8fefa2", "surface2": "#a4f5b2", "surface3": "#c1f8ca", - "surface4": "#89e99c", - "surface5": "#85e297", + "surface4": "#8cec9f", + "surface5": "#82df94", "surface6": "#7bd28c", }, "warnings": undefined, @@ -2947,20 +2883,20 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFillInverted1": "#003701", "bgFillInverted2": "#1b1f1c", "fgFill": "#1b1f1c", - "fgFillDark": "#ecf2ec", "fgFillInverted": "#ecf2ec", "fgSurface2": "#31a352", - "fgSurface3": "#008334", - "fgSurface4": "#002900", + "fgSurface3": "#328246", + "fgSurface4": "#236032", + "fgSurface5": "#041708", "stroke1": "#96d8a1", - "stroke2": "#88c592", + "stroke2": "#85c690", "stroke3": "#008334", "stroke4": "#006415", - "surface1": "#e2fde6", + "surface1": "#e7ffeb", "surface2": "#fff", "surface3": "#fff", - "surface4": "#d2fbd8", - "surface5": "#b9f7c3", + "surface4": "#d4fdda", + "surface5": "#b6f4c0", "surface6": "#8deda0", }, "warnings": undefined, @@ -2987,21 +2923,22 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFill1": "#f0d148", "bgFill2": "#ffe762", "bgFillInverted1": "#f9da53", - "bgFillInverted2": "#fef1bc", - "fgFill": "#5b5a53", + "bgFillInverted2": "#faf1ca", + "fgFill": "#1f1e1b", "fgFillInverted": "#1f1e1b", "fgSurface2": "#725c00", - "fgSurface3": "#f0d148", - "fgSurface4": "#fff075", + "fgSurface3": "#887525", + "fgSurface4": "#fadc58", + "fgSurface5": "#fffef8", "stroke1": "#817854", "stroke2": "#8e8661", "stroke3": "#f0d148", "stroke4": "#fff", "surface1": "#000", "surface2": "#000", - "surface3": "#0f0d04", - "surface4": "#171408", - "surface5": "#1f1b0c", + "surface3": "#050400", + "surface4": "#0e0b02", + "surface5": "#1e1a0b", "surface6": "#2d2814", }, "warnings": undefined, @@ -3026,20 +2963,21 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFill1": "#f0d148", "bgFill2": "#ffe762", "bgFillInverted1": "#f9da53", - "bgFillInverted2": "#fef1bc", - "fgFill": "#5b5a53", + "bgFillInverted2": "#faf1ca", + "fgFill": "#1f1e1b", "fgFillInverted": "#1f1e1b", "fgSurface2": "#745e00", - "fgSurface3": "#f0d148", - "fgSurface4": "#fff075", + "fgSurface3": "#8c7926", + "fgSurface4": "#fadc58", + "fgSurface5": "#fffef8", "stroke1": "#817854", "stroke2": "#8e8661", "stroke3": "#f0d148", "stroke4": "#fff", "surface1": "#000", "surface2": "#040301", - "surface3": "#121006", - "surface4": "#1a1609", + "surface3": "#0f0d04", + "surface4": "#141005", "surface5": "#211d0d", "surface6": "#2e2915", }, @@ -3065,22 +3003,22 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFill1": "#f0d148", "bgFill2": "#ffe762", "bgFillInverted1": "#f9da53", - "bgFillInverted2": "#fef1bc", - "fgFill": "#5b5a53", + "bgFillInverted2": "#faf1ca", + "fgFill": "#1f1e1b", "fgFillInverted": "#1f1e1b", - "fgSurface1": "#604c00", - "fgSurface2": "#7e6600", - "fgSurface3": "#f0d148", - "fgSurface4": "#fff075", + "fgSurface2": "#7f6703", + "fgSurface3": "#9a852b", + "fgSurface4": "#fadd62", + "fgSurface5": "#fffef8", "stroke1": "#817854", "stroke2": "#8e8661", "stroke3": "#f0d148", "stroke4": "#fff", - "surface1": "#0e0c04", + "surface1": "#0f0d04", "surface2": "#191609", - "surface3": "#221e0e", - "surface4": "#272210", - "surface5": "#2c2814", + "surface3": "#231f0f", + "surface4": "#25200e", + "surface5": "#2d2915", "surface6": "#38321b", }, "warnings": undefined, @@ -3105,22 +3043,23 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFill1": "#f0d148", "bgFill2": "#ffe762", "bgFillInverted1": "#f9da53", - "bgFillInverted2": "#fef1bc", - "fgFill": "#5b5a53", + "bgFillInverted2": "#faf1ca", + "fgFill": "#1f1e1b", "fgFillInverted": "#1f1e1b", "fgSurface2": "#957a00", - "fgSurface3": "#f0d148", - "fgSurface4": "#fff075", + "fgSurface3": "#b19a33", + "fgSurface4": "#fbe485", + "fgSurface5": "#fffef8", "stroke1": "#817854", "stroke2": "#8e8661", "stroke3": "#f0d148", "stroke4": "#fff", - "surface1": "#2e2812", + "surface1": "#2d2814", "surface2": "#332e18", "surface3": "#39331b", - "surface4": "#3d371d", - "surface5": "#423b20", - "surface6": "#4d4523", + "surface4": "#3c361c", + "surface5": "#443d22", + "surface6": "#4c4526", }, "warnings": undefined, }, @@ -3144,22 +3083,22 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFill1": "#f0d148", "bgFill2": "#ffe762", "bgFillInverted1": "#f9da53", - "bgFillInverted2": "#fef1bc", - "fgFill": "#5b5a53", + "bgFillInverted2": "#faf1ca", + "fgFill": "#1f1e1b", "fgFillInverted": "#1f1e1b", - "fgSurface1": "#947901", - "fgSurface2": "#b69800", - "fgSurface3": "#f0d148", - "fgSurface4": "#fff075", + "fgSurface2": "#b69801", + "fgSurface3": "#d9bc40", + "fgSurface4": "#fce895", + "fgSurface5": "#fffef8", "stroke1": "#817854", "stroke2": "#8e8661", "stroke3": "#f0d148", "stroke4": "#fff", - "surface1": "#4a4323", + "surface1": "#4a4325", "surface2": "#4f4828", "surface3": "#554d2b", - "surface4": "#58502e", - "surface5": "#5d5430", + "surface4": "#574f2d", + "surface5": "#605732", "surface6": "#685e36", }, "warnings": undefined, @@ -3185,20 +3124,21 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFill2": "#dcbe2f", "bgFillInverted1": "#281d00", "bgFillInverted2": "#060605", - "fgFill": "#4f4e48", - "fgFillInverted": "#fef1bb", + "fgFill": "#1f1e1b", + "fgFillInverted": "#faf1c9", "fgSurface2": "#5c4900", - "fgSurface3": "#3e3000", - "fgSurface4": "#0b0500", + "fgSurface3": "#39310a", + "fgSurface4": "#2d2506", + "fgSurface5": "#000", "stroke1": "#817854", - "stroke2": "#726b4a", + "stroke2": "#736c49", "stroke3": "#3e3000", "stroke4": "#100900", "surface1": "#a7985b", "surface2": "#ae9f5f", "surface3": "#b5a563", - "surface4": "#a39459", - "surface5": "#9d8f55", + "surface4": "#a4955a", + "surface5": "#9a8c52", "surface6": "#90834e", }, "warnings": undefined, @@ -3224,8 +3164,8 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFill2": "#dcbe2f", "bgFillInverted1": "#392c00", "bgFillInverted2": "#1f1e1b", - "fgFill": "#4f4e48", - "fgFillInverted": "#fef1bb", + "fgFill": "#1f1e1b", + "fgFillInverted": "#faf1c9", "fgSurface2": "#776000", "fgSurface3": "#544814", "fgSurface4": "#493e10", @@ -3237,8 +3177,8 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "surface1": "#c8b76f", "surface2": "#d0be73", "surface3": "#d8c578", - "surface4": "#c3b26c", - "surface5": "#bdac68", + "surface4": "#c5b46e", + "surface5": "#baa965", "surface6": "#af9f60", }, "warnings": undefined, @@ -3264,20 +3204,21 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFill2": "#dcbe2f", "bgFillInverted1": "#392c00", "bgFillInverted2": "#1f1e1b", - "fgFill": "#4f4e48", - "fgFillInverted": "#fef1bb", + "fgFill": "#1f1e1b", + "fgFillInverted": "#faf1c9", "fgSurface2": "#907600", - "fgSurface3": "#715b00", - "fgSurface4": "#281d00", + "fgSurface3": "#6b5d1c", + "fgSurface4": "#605318", + "fgSurface5": "#181402", "stroke1": "#b9ad7b", - "stroke2": "#a89d70", + "stroke2": "#a99d6c", "stroke3": "#715b00", "stroke4": "#524100", "surface1": "#ead682", "surface2": "#f3de88", "surface3": "#fde78e", - "surface4": "#e7d175", - "surface5": "#ddca7b", + "surface4": "#e8d276", + "surface5": "#dac778", "surface6": "#cebc72", }, "warnings": undefined, @@ -3303,20 +3244,21 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFill2": "#dcbe2f", "bgFillInverted1": "#392c00", "bgFillInverted2": "#1f1e1b", - "fgFill": "#4f4e48", + "fgFill": "#1f1e1b", "fgFillInverted": "#faf1c9", "fgSurface2": "#aa8c00", - "fgSurface3": "#886f00", - "fgSurface4": "#281d00", + "fgSurface3": "#847224", + "fgSurface4": "#625418", + "fgSurface5": "#181402", "stroke1": "#d6c88f", - "stroke2": "#c3b782", + "stroke2": "#c4b780", "stroke3": "#886f00", "stroke4": "#685300", - "surface1": "#fff7c4", + "surface1": "#fffcc9", "surface2": "#fff", "surface3": "#fff", - "surface4": "#fff1af", - "surface5": "#fde993", + "surface4": "#fff3b1", + "surface5": "#f9e590", "surface6": "#edd884", }, "warnings": undefined, @@ -3343,21 +3285,22 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFill1": "#f0b849", "bgFill2": "#ffcd61", "bgFillInverted1": "#ffd670", - "bgFillInverted2": "#f6efe5", - "fgFill": "#524e48", - "fgFillInverted": "#201e1b", + "bgFillInverted2": "#f5efe5", + "fgFill": "#1f1e1b", + "fgFillInverted": "#1f1e1b", "fgSurface2": "#815600", - "fgSurface3": "#f0b849", - "fgSurface4": "#ffebb0", + "fgSurface3": "#947029", + "fgSurface4": "#fad99f", + "fgSurface5": "#fffdfb", "stroke1": "#7b6a4c", - "stroke2": "#897755", + "stroke2": "#887758", "stroke3": "#f0b849", "stroke4": "#ffeaad", "surface1": "#000", "surface2": "#000", - "surface3": "#110c04", - "surface4": "#1a1307", - "surface5": "#221a0b", + "surface3": "#060300", + "surface4": "#100a02", + "surface5": "#21190a", "surface6": "#312613", }, "warnings": undefined, @@ -3382,20 +3325,21 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFill1": "#f0b849", "bgFill2": "#ffcd61", "bgFillInverted1": "#ffd670", - "bgFillInverted2": "#f6efe5", - "fgFill": "#524e48", - "fgFillInverted": "#201e1b", + "bgFillInverted2": "#f5efe5", + "fgFill": "#1f1e1b", + "fgFillInverted": "#1f1e1b", "fgSurface2": "#825700", - "fgSurface3": "#f0b849", - "fgSurface4": "#ffebb0", + "fgSurface3": "#99752b", + "fgSurface4": "#fad99f", + "fgSurface5": "#fffdfb", "stroke1": "#7b6a4c", - "stroke2": "#897755", + "stroke2": "#887758", "stroke3": "#f0b849", "stroke4": "#ffeaad", "surface1": "#000", "surface2": "#050301", - "surface3": "#150f05", - "surface4": "#1c1508", + "surface3": "#130d04", + "surface4": "#171004", "surface5": "#241c0c", "surface6": "#322814", }, @@ -3422,21 +3366,21 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFill2": "#ffcd61", "bgFillInverted1": "#ffd670", "bgFillInverted2": "#f5efe5", - "fgFill": "#524e49", + "fgFill": "#1f1e1b", "fgFillInverted": "#1f1e1b", - "fgSurface1": "#6c4700", - "fgSurface2": "#8e5f00", - "fgSurface3": "#f0b849", - "fgSurface4": "#ffebb0", + "fgSurface2": "#8f6003", + "fgSurface3": "#a88030", + "fgSurface4": "#fadaa4", + "fgSurface5": "#fffdfb", "stroke1": "#7b6a4c", - "stroke2": "#897755", + "stroke2": "#887758", "stroke3": "#f0b849", "stroke4": "#ffeaad", - "surface1": "#100b03", + "surface1": "#110c03", "surface2": "#1c1508", - "surface3": "#251d0d", - "surface4": "#2a2110", - "surface5": "#302613", + "surface3": "#261e0e", + "surface4": "#281f0e", + "surface5": "#322815", "surface6": "#3d301a", }, "warnings": undefined, @@ -3462,21 +3406,21 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFill2": "#ffcd61", "bgFillInverted1": "#ffd670", "bgFillInverted2": "#f5efe5", - "fgFill": "#524e49", + "fgFill": "#1f1e1b", "fgFillInverted": "#1f1e1b", - "fgSurface1": "#835700", "fgSurface2": "#a67100", - "fgSurface3": "#f0b849", - "fgSurface4": "#ffebb0", + "fgSurface3": "#c19339", + "fgSurface4": "#fbe1b5", + "fgSurface5": "#fffdfb", "stroke1": "#7b6a4c", - "stroke2": "#897755", + "stroke2": "#887758", "stroke3": "#f0b849", "stroke4": "#ffeaad", "surface1": "#312714", "surface2": "#372c17", "surface3": "#3d311b", - "surface4": "#42351d", - "surface5": "#473920", + "surface4": "#40331b", + "surface5": "#493b22", "surface6": "#524226", }, "warnings": undefined, @@ -3502,21 +3446,21 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFill2": "#ffcd61", "bgFillInverted1": "#ffd670", "bgFillInverted2": "#f5efe5", - "fgFill": "#524e49", + "fgFill": "#1f1e1b", "fgFillInverted": "#1f1e1b", - "fgSurface1": "#a57100", - "fgSurface2": "#c69009", - "fgSurface3": "#f0b849", - "fgSurface4": "#ffebb0", + "fgSurface2": "#c6900a", + "fgSurface3": "#ebb447", + "fgSurface4": "#fbe5be", + "fgSurface5": "#fffdfb", "stroke1": "#7b6a4c", - "stroke2": "#897755", + "stroke2": "#887758", "stroke3": "#f0b849", "stroke4": "#ffeaad", "surface1": "#4f4025", "surface2": "#554528", "surface3": "#5b4a2b", - "surface4": "#5f4d2d", - "surface5": "#645130", + "surface4": "#5e4c2c", + "surface5": "#665332", "surface6": "#6f5b36", }, "warnings": undefined, @@ -3543,20 +3487,20 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFillInverted1": "#2d1900", "bgFillInverted2": "#050504", "fgFill": "#1f1e1b", - "fgFillDark": "#f5efe5", "fgFillInverted": "#f5efe5", "fgSurface2": "#674300", - "fgSurface3": "#462b00", - "fgSurface4": "#0b0300", + "fgSurface3": "#3e2e0c", + "fgSurface4": "#312308", + "fgSurface5": "#000", "stroke1": "#877554", - "stroke2": "#78684a", + "stroke2": "#796848", "stroke3": "#462b00", "stroke4": "#120600", "surface1": "#b2935b", "surface2": "#b9995f", "surface3": "#c19f64", - "surface4": "#ad8f59", - "surface5": "#a78a55", + "surface4": "#ae905a", + "surface5": "#a48853", "surface6": "#9a7f4e", }, "warnings": undefined, @@ -3581,12 +3525,12 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFill1": "#f0b849", "bgFill2": "#dda633", "bgFillInverted1": "#422800", - "bgFillInverted2": "#201e1b", - "fgFill": "#45423d", - "fgFillInverted": "#f6efe5", + "bgFillInverted2": "#1f1e1b", + "fgFill": "#1f1e1b", + "fgFillInverted": "#f5efe5", "fgSurface2": "#855900", "fgSurface3": "#5b4416", - "fgSurface4": "#503b12", + "fgSurface4": "#4f3b12", "fgSurface5": "#000", "stroke1": "#a58e67", "stroke2": "#96805a", @@ -3595,8 +3539,8 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "surface1": "#d5b16f", "surface2": "#deb874", "surface3": "#e6bf78", - "surface4": "#d0ac6c", - "surface5": "#c9a668", + "surface4": "#d2ae6e", + "surface5": "#c7a466", "surface6": "#ba9a60", }, "warnings": undefined, @@ -3621,21 +3565,22 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFill1": "#f0b849", "bgFill2": "#dda633", "bgFillInverted1": "#422800", - "bgFillInverted2": "#201e1b", - "fgFill": "#45423d", - "fgFillInverted": "#f6efe5", + "bgFillInverted2": "#1f1e1b", + "fgFill": "#1f1e1b", + "fgFillInverted": "#f5efe5", "fgSurface2": "#a26e00", - "fgSurface3": "#7f5400", - "fgSurface4": "#2e1900", + "fgSurface3": "#75581f", + "fgSurface4": "#694f1b", + "fgSurface5": "#1b1203", "stroke1": "#c3a97b", - "stroke2": "#b19970", + "stroke2": "#b3996c", "stroke3": "#7f5400", "stroke4": "#5c3b00", "surface1": "#f5d190", "surface2": "#f8daa4", "surface3": "#fce3b8", - "surface4": "#f2cb83", - "surface5": "#ebc47e", + "surface4": "#f4cd85", + "surface5": "#e8c17b", "surface6": "#dbb672", }, "warnings": undefined, @@ -3660,21 +3605,22 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFill1": "#f0b849", "bgFill2": "#dda633", "bgFillInverted1": "#422800", - "bgFillInverted2": "#201e1b", - "fgFill": "#45423d", + "bgFillInverted2": "#1f1e1b", + "fgFill": "#1f1e1b", "fgFillInverted": "#f5efe5", "fgSurface2": "#ba8500", - "fgSurface3": "#986700", - "fgSurface4": "#2e1900", + "fgSurface3": "#906d28", + "fgSurface4": "#6b501b", + "fgSurface5": "#1b1203", "stroke1": "#e2c491", - "stroke2": "#ceb383", + "stroke2": "#d0b381", "stroke3": "#986700", "stroke4": "#754e00", - "surface1": "#fff5de", + "surface1": "#fffae3", "surface2": "#fff", "surface3": "#fff", - "surface4": "#ffeed0", - "surface5": "#fde6be", + "surface4": "#fff0d2", + "surface5": "#fae3bb", "surface6": "#f6d498", }, "warnings": undefined, @@ -3703,20 +3649,20 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFillInverted1": "#ffd0c7", "bgFillInverted2": "#f2efef", "fgFill": "#fefdfd", - "fgFillDark": "#f2efef", "fgFillInverted": "#231c1b", "fgSurface2": "#c10008", - "fgSurface3": "#ff8879", - "fgSurface4": "#ffe6e0", + "fgSurface3": "#e81d1d", + "fgSurface4": "#fdd4cd", + "fgSurface5": "#fffdfd", "stroke1": "#8c4840", - "stroke2": "#9e5249", + "stroke2": "#9c554d", "stroke3": "#ff8879", "stroke4": "#ffc3b9", "surface1": "#000", "surface2": "#000", - "surface3": "#1e0604", - "surface4": "#290a07", - "surface5": "#340f0b", + "surface3": "#100", + "surface4": "#1d0201", + "surface5": "#320e0a", "surface6": "#481814", }, "warnings": undefined, @@ -3743,19 +3689,19 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFillInverted1": "#ffd0c7", "bgFillInverted2": "#f2efef", "fgFill": "#fefdfd", - "fgFillDark": "#f2efef", "fgFillInverted": "#231c1b", "fgSurface2": "#c2000b", - "fgSurface3": "#ff8879", - "fgSurface4": "#ffe6e0", + "fgSurface3": "#ef1e1e", + "fgSurface4": "#fdd4cd", + "fgSurface5": "#fffdfd", "stroke1": "#8c4840", - "stroke2": "#9e5249", + "stroke2": "#9c554d", "stroke3": "#ff8879", "stroke4": "#ffc3b9", "surface1": "#000", "surface2": "#0a0101", - "surface3": "#220705", - "surface4": "#2c0b08", + "surface3": "#1f0503", + "surface4": "#250504", "surface5": "#37100c", "surface6": "#4a1914", }, @@ -3783,21 +3729,20 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFillInverted1": "#ffd0c7", "bgFillInverted2": "#f2efef", "fgFill": "#fefdfd", - "fgFillDark": "#f2efef", "fgFillInverted": "#231c1b", - "fgSurface1": "#a10000", - "fgSurface2": "#cd1a19", - "fgSurface3": "#ff8879", - "fgSurface4": "#ffe6e0", + "fgSurface2": "#ce1b1a", + "fgSurface3": "#fa3730", + "fgSurface4": "#fed6d0", + "fgSurface5": "#fffdfd", "stroke1": "#8c4840", - "stroke2": "#9e5249", + "stroke2": "#9c554d", "stroke3": "#ff8879", "stroke4": "#ffc3b9", - "surface1": "#190403", + "surface1": "#1a0503", "surface2": "#2a0a07", - "surface3": "#37100c", - "surface4": "#3e130f", - "surface5": "#461713", + "surface3": "#38110d", + "surface4": "#3b100c", + "surface5": "#481915", "surface6": "#581f1a", }, "warnings": undefined, @@ -3824,21 +3769,20 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFillInverted1": "#ffd0c7", "bgFillInverted2": "#f2efef", "fgFill": "#fefdfd", - "fgFillDark": "#f2efef", "fgFillInverted": "#231c1b", - "fgSurface1": "#c10009", - "fgSurface2": "#e3372f", - "fgSurface3": "#ff8879", - "fgSurface4": "#ffe6e0", + "fgSurface2": "#e3382f", + "fgSurface3": "#fb6a5c", + "fgSurface4": "#feddd8", + "fgSurface5": "#fffdfd", "stroke1": "#8c4840", - "stroke2": "#9e5249", + "stroke2": "#9c554d", "stroke3": "#ff8879", "stroke4": "#ffc3b9", - "surface1": "#421914", + "surface1": "#451712", "surface2": "#4e1b16", "surface3": "#571f19", - "surface4": "#5d211c", - "surface5": "#64251f", + "surface4": "#5b1f1b", + "surface5": "#662721", "surface6": "#732c25", }, "warnings": undefined, @@ -3865,20 +3809,20 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFillInverted1": "#ffd0c7", "bgFillInverted2": "#f2efef", "fgFill": "#fefdfd", - "fgFillDark": "#f2efef", "fgFillInverted": "#231c1b", "fgSurface2": "#ff6153", - "fgSurface3": "#ff9f91", - "fgSurface4": "#ffe6e0", + "fgSurface3": "#fca094", + "fgSurface4": "#fee1dd", + "fgSurface5": "#fffdfd", "stroke1": "#9b5047", - "stroke2": "#ad5b51", + "stroke2": "#ab5e54", "stroke3": "#ff9f91", "stroke4": "#ffd9d1", - "surface1": "#6e2923", + "surface1": "#6f2923", "surface2": "#762d26", - "surface3": "#7d3029", - "surface4": "#83332b", - "surface5": "#8a362e", + "surface3": "#7e3029", + "surface4": "#82322a", + "surface5": "#8d3830", "surface6": "#993d34", }, "warnings": undefined, @@ -3893,32 +3837,32 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "value": 0.7, }, }, - "seedComputed": "#fffaf7", + "seedComputed": "#fff5f1", "seedOriginal": "#cc1818", "seedUnchanged": false, }, "output": { "direction": "darker", "ramp": { - "bgFill1": "#fffaf7", - "bgFill2": "#e9e4e1", - "bgFillInverted1": "#1e1a18", + "bgFill1": "#fff5f1", + "bgFill2": "#e9dfdb", + "bgFillInverted1": "#211917", "bgFillInverted2": "#000", "fgFill": "#221d1b", - "fgFillDark": "#f9ede9", "fgFillInverted": "#f9ede9", "fgSurface2": "#4f4744", - "fgSurface3": "#342d2a", - "fgSurface4": "#000", + "fgSurface3": "#551c00", + "fgSurface4": "#421400", + "fgSurface5": "#000", "stroke1": "#9a6956", - "stroke2": "#8a5d4c", + "stroke2": "#8c5c4a", "stroke3": "#342d2a", "stroke4": "#060302", "surface1": "#cf8264", "surface2": "#d4896c", "surface3": "#d79176", - "surface4": "#cb7d5f", - "surface5": "#c5785b", + "surface4": "#cd7e60", + "surface5": "#c27659", "surface6": "#b66e53", }, "warnings": undefined, @@ -3944,20 +3888,21 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFill2": "#b90000", "bgFillInverted1": "#640000", "bgFillInverted2": "#231c1b", - "fgFill": "#eae6e5", + "fgFill": "#f2efef", "fgFillInverted": "#f2efef", "fgSurface2": "#c3030d", - "fgSurface3": "#930000", - "fgSurface4": "#470000", + "fgSurface3": "#8f0d0d", + "fgSurface4": "#7e0a0a", + "fgSurface5": "#000", "stroke1": "#ce786d", - "stroke2": "#c3675c", + "stroke2": "#be695e", "stroke3": "#930000", "stroke4": "#5e0000", "surface1": "#e9a298", "surface2": "#ebaca2", "surface3": "#edb5ac", - "surface4": "#e89c91", - "surface5": "#e69489", + "surface4": "#ea9e93", + "surface5": "#e49287", "surface6": "#e38276", }, "warnings": undefined, @@ -3982,21 +3927,22 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFill1": "#cc1818", "bgFill2": "#b90000", "bgFillInverted1": "#640000", - "bgFillInverted2": "#241c1b", - "fgFill": "#eae6e6", - "fgFillInverted": "#f3efef", + "bgFillInverted2": "#231c1b", + "fgFill": "#f2efef", + "fgFillInverted": "#f2efef", "fgSurface2": "#e1342d", - "fgSurface3": "#bd0002", - "fgSurface4": "#470000", + "fgSurface3": "#b81515", + "fgSurface4": "#a61111", + "fgSurface5": "#260101", "stroke1": "#da9d94", - "stroke2": "#d3887e", + "stroke2": "#c88980", "stroke3": "#bd0002", "stroke4": "#8a0000", "surface1": "#f3ccc6", "surface2": "#f6d6d1", "surface3": "#f8e0dc", - "surface4": "#f1c5bf", - "surface5": "#efbdb6", + "surface4": "#f3c7c1", + "surface5": "#ecbab4", "surface6": "#ebaba2", }, "warnings": undefined, @@ -4022,20 +3968,21 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "bgFill2": "#b90000", "bgFillInverted1": "#640000", "bgFillInverted2": "#231c1b", - "fgFill": "#eae6e5", + "fgFill": "#f2efef", "fgFillInverted": "#f2efef", "fgSurface2": "#fd5145", - "fgSurface3": "#cc1818", - "fgSurface4": "#470000", + "fgSurface3": "#e21c1c", + "fgSurface4": "#a11010", + "fgSurface5": "#260101", "stroke1": "#e0afa7", - "stroke2": "#d99a91", + "stroke2": "#ce9b93", "stroke3": "#cc1818", "stroke4": "#9d0000", - "surface1": "#fef4f2", + "surface1": "#fff8f6", "surface2": "#fff", "surface3": "#fff", - "surface4": "#fcedea", - "surface5": "#fae4e0", + "surface4": "#feefec", + "surface5": "#f7e1dd", "surface6": "#f4d1cb", }, "warnings": undefined, @@ -4058,25 +4005,23 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "ramp": { "bgFill1": "#c38278", "bgFill2": "#d49287", - "bgFillDark": "#000", "bgFillInverted1": "#ffd0c6", "bgFillInverted2": "#f2efef", "fgFill": "#34120e", - "fgFillDark": "#f2efef", "fgFillInverted": "#34120e", - "fgSurface1": "#594a47", - "fgSurface2": "#766360", - "fgSurface3": "#bfb0ae", - "fgSurface4": "#f2efef", + "fgSurface2": "#786562", + "fgSurface3": "#98807c", + "fgSurface4": "#e4dedd", + "fgSurface5": "#fefdfd", "stroke1": "#341612", - "stroke2": "#49221d", - "stroke3": "#9d5147", + "stroke2": "#482622", + "stroke3": "#9f5349", "stroke4": "#c76a5d", - "surface1": "#190302", + "surface1": "#1b0403", "surface2": "#300", - "surface3": "#380e09", - "surface4": "#3f110c", - "surface5": "#471610", + "surface3": "#3a100b", + "surface4": "#3d0f0a", + "surface5": "#491812", "surface6": "#571f19", }, "warnings": undefined, @@ -4093,25 +4038,23 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "ramp": { "bgFill1": "#989968", "bgFill2": "#a8aa78", - "bgFillDark": "#000", "bgFillInverted1": "#dcdfab", "bgFillInverted2": "#f1f2df", "fgFill": "#1f1f12", - "fgFillDark": "#f1f2df", "fgFillInverted": "#1f1f12", - "fgSurface1": "#63645b", "fgSurface2": "#7f8075", - "fgSurface3": "#b5b6a7", - "fgSurface4": "#f1f2df", + "fgSurface3": "#9fa093", + "fgSurface4": "#e6e7d5", + "fgSurface5": "#fefff2", "stroke1": "#393a24", - "stroke2": "#45462d", + "stroke2": "#45462f", "stroke3": "#818256", "stroke4": "#a2a36d", "surface1": "#2d2d12", "surface2": "#330", "surface3": "#373817", - "surface4": "#3b3c1a", - "surface5": "#3f401d", + "surface4": "#393a18", + "surface5": "#41421f", "surface6": "#494a24", }, "warnings": undefined, @@ -4128,25 +4071,23 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "ramp": { "bgFill1": "#73a46e", "bgFill2": "#83b57e", - "bgFillDark": "#000", "bgFillInverted1": "#b4e9af", "bgFillInverted2": "#ebf3ea", "fgFill": "#11230f", - "fgFillDark": "#ebf3ea", "fgFillInverted": "#11230f", - "fgSurface1": "#576056", "fgSurface2": "#707c6f", - "fgSurface3": "#a9baa7", - "fgSurface4": "#ebf3ea", + "fgSurface3": "#8c9a8a", + "fgSurface4": "#dae8d9", + "fgSurface5": "#fcfefc", "stroke1": "#21381f", - "stroke2": "#2a4528", + "stroke2": "#2d452b", "stroke3": "#558451", "stroke4": "#6ca768", - "surface1": "#0f2b0d", + "surface1": "#0e2a0c", "surface2": "#030", - "surface3": "#153713", - "surface4": "#183b16", - "surface5": "#1b4019", + "surface3": "#143612", + "surface4": "#163914", + "surface5": "#1d421b", "surface6": "#234b20", }, "warnings": undefined, @@ -4163,25 +4104,23 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "ramp": { "bgFill1": "#719f9e", "bgFill2": "#81b0af", - "bgFillDark": "#000", "bgFillInverted1": "#b4e4e3", "bgFillInverted2": "#e8f3f2", "fgFill": "#122121", - "fgFillDark": "#e8f3f2", "fgFillInverted": "#122121", - "fgSurface1": "#596161", "fgSurface2": "#737d7d", - "fgSurface3": "#a9b8b7", - "fgSurface4": "#e8f3f2", + "fgSurface3": "#909c9c", + "fgSurface4": "#d7e8e8", + "fgSurface5": "#fbfefe", "stroke1": "#233838", - "stroke2": "#2c4545", + "stroke2": "#2f4545", "stroke3": "#588382", "stroke4": "#70a5a4", "surface1": "#112b2b", "surface2": "#033", "surface3": "#173737", - "surface4": "#1a3b3b", - "surface5": "#1d403f", + "surface4": "#183939", + "surface5": "#1f4241", "surface6": "#254a4a", }, "warnings": undefined, @@ -4198,25 +4137,23 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "ramp": { "bgFill1": "#7694d3", "bgFill2": "#85a5e5", - "bgFillDark": "#000", "bgFillInverted1": "#c4ddff", "bgFillInverted2": "#eff0f2", "fgFill": "#0e1c3d", - "fgFillDark": "#eff0f2", "fgFillInverted": "#0e1c3d", - "fgSurface1": "#3f485a", - "fgSurface2": "#586275", - "fgSurface3": "#b0b4bd", - "fgSurface4": "#eff0f2", + "fgSurface2": "#596376", + "fgSurface3": "#777f8f", + "fgSurface4": "#dcdee2", + "fgSurface5": "#fdfefe", "stroke1": "#061436", - "stroke2": "#102455", - "stroke3": "#3b5eae", + "stroke2": "#182951", + "stroke3": "#3d60b0", "stroke4": "#5f7ebd", "surface1": "#000", "surface2": "#003", - "surface3": "#010d3e", - "surface4": "#02134a", - "surface5": "#041955", + "surface3": "#021041", + "surface4": "#011047", + "surface5": "#051b57", "surface6": "#0a256b", }, "warnings": undefined, @@ -4233,25 +4170,23 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "ramp": { "bgFill1": "#b87eb5", "bgFill2": "#c98ec6", - "bgFillDark": "#000", "bgFillInverted1": "#ffc9ff", "bgFillInverted2": "#f2eff2", "fgFill": "#2e112d", - "fgFillDark": "#f2eff2", "fgFillInverted": "#2e112d", - "fgSurface1": "#584c57", - "fgSurface2": "#756573", - "fgSurface3": "#bfaebe", - "fgSurface4": "#f2eff2", + "fgSurface2": "#776775", + "fgSurface3": "#968394", + "fgSurface4": "#e5dee4", + "fgSurface5": "#fefdfe", "stroke1": "#331932", - "stroke2": "#462445", - "stroke3": "#935390", + "stroke2": "#462945", + "stroke3": "#955592", "stroke4": "#ba6bb8", - "surface1": "#1e061e", + "surface1": "#200820", "surface2": "#303", - "surface3": "#361036", - "surface4": "#3d133c", - "surface5": "#441743", + "surface3": "#381238", + "surface4": "#3b113a", + "surface5": "#461945", "surface6": "#512050", }, "warnings": undefined, @@ -4268,25 +4203,23 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "ramp": { "bgFill1": "#bb8680", "bgFill2": "#cc9690", - "bgFillDark": "#000", "bgFillInverted1": "#ffd0c9", "bgFillInverted2": "#f2efef", "fgFill": "#321311", - "fgFillDark": "#f2efef", "fgFillInverted": "#321311", - "fgSurface1": "#584a49", - "fgSurface2": "#756361", - "fgSurface3": "#beb1af", - "fgSurface4": "#f2efef", + "fgSurface2": "#766462", + "fgSurface3": "#95807d", + "fgSurface4": "#e3dedd", + "fgSurface5": "#fefdfd", "stroke1": "#321715", - "stroke2": "#472320", - "stroke3": "#99544e", + "stroke2": "#462724", + "stroke3": "#9a554f", "stroke4": "#c16d66", - "surface1": "#170403", + "surface1": "#190504", "surface2": "#2e0505", - "surface3": "#36100e", - "surface4": "#3d1311", - "surface5": "#451815", + "surface3": "#37110f", + "surface4": "#3a110f", + "surface5": "#471a17", "surface6": "#54221e", }, "warnings": undefined, @@ -4303,25 +4236,23 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "ramp": { "bgFill1": "#97996f", "bgFill2": "#a8a97f", - "bgFillDark": "#000", "bgFillInverted1": "#dcdeb2", "bgFillInverted2": "#f1f2e0", "fgFill": "#1f1f11", - "fgFillDark": "#f1f2e0", "fgFillInverted": "#1f1f11", - "fgSurface1": "#5f5f58", "fgSurface2": "#7b7b72", - "fgSurface3": "#b5b5a8", - "fgSurface4": "#f1f2e0", + "fgSurface3": "#999a8e", + "fgSurface4": "#e4e5d4", + "fgSurface5": "#fefff2", "stroke1": "#353522", - "stroke2": "#41422b", + "stroke2": "#41422e", "stroke3": "#7c7d55", "stroke4": "#9c9e6c", "surface1": "#272810", "surface2": "#2e2e05", "surface3": "#333316", - "surface4": "#363719", - "surface5": "#3b3b1c", + "surface4": "#353618", + "surface5": "#3d3d1e", "surface6": "#454523", }, "warnings": undefined, @@ -4338,25 +4269,23 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "ramp": { "bgFill1": "#78a275", "bgFill2": "#88b385", - "bgFillDark": "#000", "bgFillInverted1": "#bae7b6", "bgFillInverted2": "#ebf3eb", "fgFill": "#112310", - "fgFillDark": "#ebf3eb", "fgFillInverted": "#112310", - "fgSurface1": "#545c53", "fgSurface2": "#6d786c", - "fgSurface3": "#aab9a8", - "fgSurface4": "#ebf3eb", + "fgSurface3": "#8a9789", + "fgSurface4": "#d9e6d8", + "fgSurface5": "#fcfefc", "stroke1": "#1f331e", - "stroke2": "#294127", - "stroke3": "#547f51", + "stroke2": "#2c412a", + "stroke3": "#558052", "stroke4": "#6ba168", - "surface1": "#0e260c", + "surface1": "#0d260c", "surface2": "#052e05", "surface3": "#153313", - "surface4": "#173716", - "surface5": "#1b3c19", + "surface4": "#153514", + "surface5": "#1d3e1b", "surface6": "#224721", }, "warnings": undefined, @@ -4373,25 +4302,23 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "ramp": { "bgFill1": "#779e9d", "bgFill2": "#87afae", - "bgFillDark": "#000", "bgFillInverted1": "#bae3e2", "bgFillInverted2": "#e8f3f2", "fgFill": "#112121", - "fgFillDark": "#e8f3f2", "fgFillInverted": "#112121", - "fgSurface1": "#565d5d", "fgSurface2": "#6f7978", - "fgSurface3": "#aab7b7", - "fgSurface4": "#e8f3f2", + "fgSurface3": "#8c9897", + "fgSurface4": "#d6e6e6", + "fgSurface5": "#fbfefe", "stroke1": "#213434", - "stroke2": "#2a4141", - "stroke3": "#567e7d", + "stroke2": "#2d4141", + "stroke3": "#577f7e", "stroke4": "#6e9f9f", "surface1": "#0f2626", "surface2": "#052e2e", "surface3": "#163332", - "surface4": "#193737", - "surface5": "#1c3c3b", + "surface4": "#173535", + "surface5": "#1e3e3d", "surface6": "#244646", }, "warnings": undefined, @@ -4408,25 +4335,23 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "ramp": { "bgFill1": "#8492c7", "bgFill2": "#94a3d8", - "bgFillDark": "#000", "bgFillInverted1": "#ccdaff", "bgFillInverted2": "#eff0f2", "fgFill": "#151b3a", - "fgFillDark": "#eff0f2", "fgFillInverted": "#151b3a", - "fgSurface1": "#444959", - "fgSurface2": "#5d6275", - "fgSurface3": "#b1b4bc", - "fgSurface4": "#eff0f2", + "fgSurface2": "#5f6478", + "fgSurface3": "#7d818f", + "fgSurface4": "#dcdee1", + "fgSurface5": "#fdfdfe", "stroke1": "#101635", - "stroke2": "#1d2550", - "stroke3": "#4e5ea7", + "stroke2": "#21294c", + "stroke3": "#5060aa", "stroke4": "#6e7db7", - "surface1": "#000", + "surface1": "#010101", "surface2": "#05052e", - "surface3": "#0c103b", - "surface4": "#101545", - "surface5": "#151b4f", + "surface3": "#0f143f", + "surface4": "#0f1444", + "surface5": "#171e52", "surface6": "#1e2663", }, "warnings": undefined, @@ -4443,25 +4368,23 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "ramp": { "bgFill1": "#b382b0", "bgFill2": "#c492c1", - "bgFillDark": "#000", "bgFillInverted1": "#fecafb", "bgFillInverted2": "#f2eff2", "fgFill": "#2d122c", - "fgFillDark": "#f2eff2", "fgFillInverted": "#2d122c", - "fgSurface1": "#574c56", - "fgSurface2": "#736572", - "fgSurface3": "#beaebd", - "fgSurface4": "#f2eff2", + "fgSurface2": "#756774", + "fgSurface3": "#948293", + "fgSurface4": "#e4dee4", + "fgSurface5": "#fefdfe", "stroke1": "#311930", - "stroke2": "#432542", - "stroke3": "#8f558d", + "stroke2": "#432942", + "stroke3": "#91578f", "stroke4": "#b66db4", - "surface1": "#1b061b", + "surface1": "#1d071d", "surface2": "#2e052e", - "surface3": "#341133", - "surface4": "#3a1539", - "surface5": "#411940", + "surface3": "#361335", + "surface4": "#381337", + "surface5": "#431b42", "surface6": "#4f224e", }, "warnings": undefined, @@ -4478,25 +4401,23 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "ramp": { "bgFill1": "#b38986", "bgFill2": "#c49996", - "bgFillDark": "#000", "bgFillInverted1": "#fdd0cd", "bgFillInverted2": "#f2efef", "fgFill": "#301514", - "fgFillDark": "#f2efef", "fgFillInverted": "#301514", - "fgSurface1": "#574b4a", - "fgSurface2": "#736463", - "fgSurface3": "#bdb1b0", - "fgSurface4": "#f2efef", + "fgSurface2": "#746564", + "fgSurface3": "#93807f", + "fgSurface4": "#e2dedd", + "fgSurface5": "#fefdfd", "stroke1": "#301817", - "stroke2": "#442523", - "stroke3": "#935754", + "stroke2": "#432826", + "stroke3": "#945855", "stroke4": "#bb716d", - "surface1": "#160505", + "surface1": "#170606", "surface2": "#290a0a", - "surface3": "#331212", - "surface4": "#3a1615", - "surface5": "#411a1a", + "surface3": "#341313", + "surface4": "#371312", + "surface5": "#431c1c", "surface6": "#502423", }, "warnings": undefined, @@ -4513,25 +4434,23 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "ramp": { "bgFill1": "#979876", "bgFill2": "#a7a986", - "bgFillDark": "#000", "bgFillInverted1": "#dddeba", "bgFillInverted2": "#f1f2e1", "fgFill": "#1f1f11", - "fgFillDark": "#f1f2e1", "fgFillInverted": "#1f1f11", - "fgSurface1": "#5b5b54", - "fgSurface2": "#76766e", - "fgSurface3": "#b5b5a8", - "fgSurface4": "#f1f2e1", + "fgSurface2": "#77776f", + "fgSurface3": "#95958b", + "fgSurface4": "#e3e4d4", + "fgSurface5": "#fefef3", "stroke1": "#303020", - "stroke2": "#3d3d29", - "stroke3": "#777854", + "stroke2": "#3d3e2c", + "stroke3": "#787955", "stroke4": "#97986b", - "surface1": "#22220f", + "surface1": "#232310", "surface2": "#29290a", - "surface3": "#2e2e16", - "surface4": "#323218", - "surface5": "#37371c", + "surface3": "#2f2f17", + "surface4": "#313117", + "surface5": "#39391e", "surface6": "#414123", }, "warnings": undefined, @@ -4548,25 +4467,23 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "ramp": { "bgFill1": "#7ea07c", "bgFill2": "#8eb18c", - "bgFillDark": "#000", "bgFillInverted1": "#c0e5be", "bgFillInverted2": "#ecf2eb", "fgFill": "#112311", - "fgFillDark": "#ecf2eb", "fgFillInverted": "#112311", - "fgSurface1": "#515951", - "fgSurface2": "#6a746a", - "fgSurface3": "#aab9a9", - "fgSurface4": "#ecf2eb", + "fgSurface2": "#6b756b", + "fgSurface3": "#879386", + "fgSurface4": "#d8e5d7", + "fgSurface5": "#fcfefc", "stroke1": "#1d2f1c", - "stroke2": "#273d26", - "stroke3": "#537b51", + "stroke2": "#2a3d29", + "stroke3": "#547c52", "stroke4": "#6b9c69", - "surface1": "#0c200c", + "surface1": "#0d210d", "surface2": "#0a290a", - "surface3": "#142e14", - "surface4": "#173316", - "surface5": "#1b381a", + "surface3": "#152f15", + "surface4": "#153114", + "surface5": "#1d3a1c", "surface6": "#234322", }, "warnings": undefined, @@ -4583,25 +4500,23 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "ramp": { "bgFill1": "#7c9c9c", "bgFill2": "#8cadad", - "bgFillDark": "#000", "bgFillInverted1": "#c0e2e1", "bgFillInverted2": "#e9f2f2", "fgFill": "#112121", - "fgFillDark": "#e9f2f2", "fgFillInverted": "#112121", - "fgSurface1": "#535a59", "fgSurface2": "#6c7574", - "fgSurface3": "#aab7b7", - "fgSurface4": "#e9f2f2", + "fgSurface3": "#889393", + "fgSurface4": "#d5e5e5", + "fgSurface5": "#fbfefe", "stroke1": "#1f2f2f", - "stroke2": "#293d3d", - "stroke3": "#567979", + "stroke2": "#2c3d3d", + "stroke3": "#577a7a", "stroke4": "#6e9a99", "surface1": "#0e2121", "surface2": "#0a2929", "surface3": "#162e2e", - "surface4": "#183232", - "surface5": "#1c3737", + "surface4": "#173131", + "surface5": "#1e3939", "surface6": "#244242", }, "warnings": undefined, @@ -4618,25 +4533,23 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "ramp": { "bgFill1": "#8c92bb", "bgFill2": "#9ca2cc", - "bgFillDark": "#000", "bgFillInverted1": "#d2d9ff", "bgFillInverted2": "#f0f0f1", "fgFill": "#181b37", - "fgFillDark": "#f0f0f1", "fgFillInverted": "#181b37", - "fgSurface1": "#484a58", - "fgSurface2": "#616474", - "fgSurface3": "#b2b4bb", - "fgSurface4": "#f0f0f1", + "fgSurface2": "#626575", + "fgSurface3": "#7f818e", + "fgSurface4": "#dddee1", + "fgSurface5": "#fdfdfe", "stroke1": "#161934", - "stroke2": "#24274c", - "stroke3": "#595f9f", + "stroke2": "#272a49", + "stroke3": "#5a60a1", "stroke4": "#777eb1", - "surface1": "#010108", + "surface1": "#02020c", "surface2": "#0a0a29", - "surface3": "#121339", - "surface4": "#171841", - "surface5": "#1c1d4a", + "surface3": "#14153b", + "surface4": "#15163e", + "surface5": "#1e1f4c", "surface6": "#26285c", }, "warnings": undefined, @@ -4653,25 +4566,23 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "ramp": { "bgFill1": "#ad86ab", "bgFill2": "#be96bc", - "bgFillDark": "#000", "bgFillInverted1": "#f7cdf5", "bgFillInverted2": "#f2eff2", "fgFill": "#2b142b", - "fgFillDark": "#f2eff2", "fgFillInverted": "#2b142b", - "fgSurface1": "#564c55", - "fgSurface2": "#726571", - "fgSurface3": "#bdafbc", - "fgSurface4": "#f2eff2", + "fgSurface2": "#736672", + "fgSurface3": "#928291", + "fgSurface4": "#e3dde3", + "fgSurface5": "#fefdfe", "stroke1": "#2f1a2e", - "stroke2": "#412640", - "stroke3": "#8b578a", + "stroke2": "#412a40", + "stroke3": "#8c588b", "stroke4": "#b170af", - "surface1": "#190619", + "surface1": "#1a071a", "surface2": "#290a29", - "surface3": "#311331", - "surface4": "#371636", - "surface5": "#3e1b3d", + "surface3": "#321432", + "surface4": "#351434", + "surface5": "#401d3f", "surface6": "#4b244b", }, "warnings": undefined, @@ -4688,25 +4599,23 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "ramp": { "bgFill1": "#aa8d8c", "bgFill2": "#ba9d9c", - "bgFillDark": "#000", "bgFillInverted1": "#f2d3d2", "bgFillInverted2": "#f1efef", "fgFill": "#2d1717", - "fgFillDark": "#f1efef", "fgFillInverted": "#2d1717", - "fgSurface1": "#564c4b", - "fgSurface2": "#726565", - "fgSurface3": "#bbb2b1", - "fgSurface4": "#f1efef", + "fgSurface2": "#736666", + "fgSurface3": "#918181", + "fgSurface4": "#e2dedd", + "fgSurface5": "#fefdfd", "stroke1": "#2e1b1a", - "stroke2": "#412727", - "stroke3": "#8c5b5a", + "stroke2": "#3f2a29", + "stroke3": "#8d5c5b", "stroke4": "#b27574", - "surface1": "#160607", + "surface1": "#170708", "surface2": "#240f0f", - "surface3": "#301515", - "surface4": "#361919", - "surface5": "#3e1e1e", + "surface3": "#311616", + "surface4": "#331617", + "surface5": "#401f1f", "surface6": "#4c2828", }, "warnings": undefined, @@ -4723,25 +4632,23 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "ramp": { "bgFill1": "#97977f", "bgFill2": "#a7a88f", - "bgFillDark": "#000", "bgFillInverted1": "#dcddc3", "bgFillInverted2": "#f1f1e3", "fgFill": "#1f1f12", - "fgFillDark": "#f1f1e3", "fgFillInverted": "#1f1f12", - "fgSurface1": "#575751", "fgSurface2": "#72726a", - "fgSurface3": "#b5b5aa", - "fgSurface4": "#f1f1e3", + "fgSurface3": "#909087", + "fgSurface4": "#e2e2d4", + "fgSurface5": "#fefef4", "stroke1": "#2b2c1e", - "stroke2": "#393928", - "stroke3": "#737355", + "stroke2": "#39392a", + "stroke3": "#747456", "stroke4": "#92936c", "surface1": "#1d1d0e", "surface2": "#24240f", "surface3": "#2a2a16", - "surface4": "#2e2e19", - "surface5": "#32331c", + "surface4": "#2c2c17", + "surface5": "#34351e", "surface6": "#3d3d25", }, "warnings": undefined, @@ -4758,25 +4665,23 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "ramp": { "bgFill1": "#859d83", "bgFill2": "#95ae94", - "bgFillDark": "#000", "bgFillInverted1": "#c8e2c7", "bgFillInverted2": "#ecf2ec", "fgFill": "#132313", - "fgFillDark": "#ecf2ec", "fgFillInverted": "#132313", - "fgSurface1": "#4f564f", "fgSurface2": "#697168", - "fgSurface3": "#acb8ab", - "fgSurface4": "#ecf2ec", + "fgSurface3": "#848f84", + "fgSurface4": "#d8e4d7", + "fgSurface5": "#fcfefc", "stroke1": "#1d2c1c", - "stroke2": "#273a27", + "stroke2": "#293a29", "stroke3": "#557754", "stroke4": "#6d986c", "surface1": "#0c1c0c", "surface2": "#0f240f", "surface3": "#152a15", - "surface4": "#182f18", - "surface5": "#1c341c", + "surface4": "#162d16", + "surface5": "#1e361e", "surface6": "#243f24", }, "warnings": undefined, @@ -4793,25 +4698,23 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "ramp": { "bgFill1": "#839b9a", "bgFill2": "#93abab", - "bgFillDark": "#000", "bgFillInverted1": "#c7e0df", "bgFillInverted2": "#e9f2f2", "fgFill": "#132121", - "fgFillDark": "#e9f2f2", "fgFillInverted": "#132121", - "fgSurface1": "#505656", "fgSurface2": "#6a7171", - "fgSurface3": "#acb7b7", - "fgSurface4": "#e9f2f2", + "fgSurface3": "#868f8f", + "fgSurface4": "#d6e3e3", + "fgSurface5": "#fbfefe", "stroke1": "#1e2b2b", - "stroke2": "#283939", - "stroke3": "#567575", + "stroke2": "#2b3939", + "stroke3": "#577676", "stroke4": "#6f9595", "surface1": "#0d1c1c", "surface2": "#0f2424", "surface3": "#162a2a", - "surface4": "#192e2e", - "surface5": "#1d3333", + "surface4": "#182d2d", + "surface5": "#1f3535", "surface6": "#253e3e", }, "warnings": undefined, @@ -4828,25 +4731,23 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "ramp": { "bgFill1": "#9093af", "bgFill2": "#a1a3bf", - "bgFillDark": "#000", "bgFillInverted1": "#d6d9f7", "bgFillInverted2": "#f0f0f1", "fgFill": "#1b1b33", - "fgFillDark": "#f0f0f1", "fgFillInverted": "#1b1b33", - "fgSurface1": "#4a4b56", - "fgSurface2": "#646572", - "fgSurface3": "#b3b4ba", - "fgSurface4": "#f0f0f1", + "fgSurface2": "#656673", + "fgSurface3": "#81828d", + "fgSurface4": "#dddee0", + "fgSurface5": "#fdfdfe", "stroke1": "#1b1c32", - "stroke2": "#292947", - "stroke3": "#606196", + "stroke2": "#2b2c44", + "stroke3": "#616297", "stroke4": "#7e80aa", - "surface1": "#050412", + "surface1": "#060513", "surface2": "#0f0f24", - "surface3": "#171735", - "surface4": "#1c1b3c", - "surface5": "#212045", + "surface3": "#181836", + "surface4": "#191839", + "surface5": "#222247", "surface6": "#2b2b54", }, "warnings": undefined, @@ -4863,25 +4764,23 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "ramp": { "bgFill1": "#a68ba5", "bgFill2": "#b79bb6", - "bgFillDark": "#000", "bgFillInverted1": "#efd2ed", "bgFillInverted2": "#f2eff2", "fgFill": "#291629", - "fgFillDark": "#f2eff2", "fgFillInverted": "#291629", - "fgSurface1": "#554c54", "fgSurface2": "#716670", - "fgSurface3": "#bcb0bb", - "fgSurface4": "#f2eff2", + "fgSurface3": "#8f828f", + "fgSurface4": "#e2dde2", + "fgSurface5": "#fefdfe", "stroke1": "#2d1b2d", - "stroke2": "#3e283e", - "stroke3": "#855a85", + "stroke2": "#3e2a3e", + "stroke3": "#865b86", "stroke4": "#aa74a9", - "surface1": "#170817", + "surface1": "#180818", "surface2": "#240f24", "surface3": "#2e162e", - "surface4": "#341934", - "surface5": "#3a1e3a", + "surface4": "#311631", + "surface5": "#3b1f3b", "surface6": "#472747", }, "warnings": undefined, @@ -4898,25 +4797,23 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "ramp": { "bgFill1": "#9f9191", "bgFill2": "#b0a2a1", - "bgFillDark": "#000", "bgFillInverted1": "#e6d7d7", "bgFillInverted2": "#f1f0f0", "fgFill": "#291919", - "fgFillDark": "#f1f0f0", "fgFillInverted": "#291919", - "fgSurface1": "#544d4d", "fgSurface2": "#706767", - "fgSurface3": "#b9b2b2", - "fgSurface4": "#f1f0f0", + "fgSurface3": "#8f8483", + "fgSurface4": "#e1dede", + "fgSurface5": "#fefdfd", "stroke1": "#2c1e1e", - "stroke2": "#3d2b2b", - "stroke3": "#836060", + "stroke2": "#3c2d2d", + "stroke3": "#846161", "stroke4": "#a77c7c", - "surface1": "#160a0a", + "surface1": "#170b0b", "surface2": "#1f1414", - "surface3": "#2c1919", - "surface4": "#321d1e", - "surface5": "#392223", + "surface3": "#2d1a1a", + "surface4": "#2f1a1b", + "surface5": "#3a2324", "surface6": "#452c2c", }, "warnings": undefined, @@ -4933,25 +4830,23 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "ramp": { "bgFill1": "#969689", "bgFill2": "#a6a799", - "bgFillDark": "#000", "bgFillInverted1": "#dcdcce", "bgFillInverted2": "#f1f1e6", "fgFill": "#1f1f15", - "fgFillDark": "#f1f1e6", "fgFillInverted": "#1f1f15", - "fgSurface1": "#53534f", - "fgSurface2": "#6e6e68", - "fgSurface3": "#b5b5ac", - "fgSurface4": "#f1f1e6", + "fgSurface2": "#6f6f69", + "fgSurface3": "#8c8c85", + "fgSurface4": "#e1e1d6", + "fgSurface5": "#fefef6", "stroke1": "#27271e", - "stroke2": "#353529", - "stroke3": "#6f6f59", + "stroke2": "#36362c", + "stroke3": "#70705a", "stroke4": "#8e8e72", - "surface1": "#17170d", + "surface1": "#18180e", "surface2": "#1f1f14", - "surface3": "#252518", - "surface4": "#29291b", - "surface5": "#2e2e1f", + "surface3": "#262619", + "surface4": "#28281a", + "surface5": "#303021", "surface6": "#393928", }, "warnings": undefined, @@ -4968,25 +4863,23 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "ramp": { "bgFill1": "#8c998c", "bgFill2": "#9daa9c", - "bgFillDark": "#000", "bgFillInverted1": "#d1dfd0", "bgFillInverted2": "#edf1ed", "fgFill": "#162216", - "fgFillDark": "#edf1ed", "fgFillInverted": "#162216", - "fgSurface1": "#4e534e", - "fgSurface2": "#686e68", - "fgSurface3": "#aeb7ad", - "fgSurface4": "#edf1ed", + "fgSurface2": "#696f69", + "fgSurface3": "#848c84", + "fgSurface4": "#d9e2d9", + "fgSurface5": "#fdfefc", "stroke1": "#1d281d", - "stroke2": "#293629", - "stroke3": "#5a7359", + "stroke2": "#2b362b", + "stroke3": "#5b745a", "stroke4": "#739273", - "surface1": "#0c170c", + "surface1": "#0d180d", "surface2": "#141f14", - "surface3": "#172617", - "surface4": "#1b2b1b", - "surface5": "#1f301f", + "surface3": "#182718", + "surface4": "#192919", + "surface5": "#213221", "surface6": "#283b28", }, "warnings": undefined, @@ -5003,25 +4896,23 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "ramp": { "bgFill1": "#8b9898", "bgFill2": "#9ca9a8", - "bgFillDark": "#000", "bgFillInverted1": "#d0dede", "bgFillInverted2": "#ebf2f2", "fgFill": "#162020", - "fgFillDark": "#ebf2f2", "fgFillInverted": "#162020", - "fgSurface1": "#4f5353", "fgSurface2": "#686e6e", - "fgSurface3": "#aeb6b6", - "fgSurface4": "#ebf2f2", + "fgSurface3": "#858c8c", + "fgSurface4": "#d7e2e2", + "fgSurface5": "#fcfefe", "stroke1": "#1e2727", - "stroke2": "#293535", - "stroke3": "#5a7171", + "stroke2": "#2b3535", + "stroke3": "#5b7272", "stroke4": "#749090", - "surface1": "#0d1717", + "surface1": "#0e1818", "surface2": "#141f1f", - "surface3": "#182525", - "surface4": "#1c2a2a", - "surface5": "#202f2f", + "surface3": "#192626", + "surface4": "#1a2828", + "surface5": "#223131", "surface6": "#293a3a", }, "warnings": undefined, @@ -5038,25 +4929,23 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "ramp": { "bgFill1": "#9394a2", "bgFill2": "#a4a4b2", - "bgFillDark": "#000", "bgFillInverted1": "#d9d9e8", "bgFillInverted2": "#f0f0f1", "fgFill": "#1c1c2d", - "fgFillDark": "#f0f0f1", "fgFillInverted": "#1c1c2d", - "fgSurface1": "#4d4d55", "fgSurface2": "#676770", - "fgSurface3": "#b3b4b8", - "fgSurface4": "#f0f0f1", + "fgSurface3": "#84848b", + "fgSurface4": "#dedee0", + "fgSurface5": "#fdfdfe", "stroke1": "#1f1e2e", - "stroke2": "#2c2c41", + "stroke2": "#2d2d3f", "stroke3": "#656589", "stroke4": "#8383a0", "surface1": "#0a0a16", "surface2": "#14141f", "surface3": "#1b1b30", - "surface4": "#201f37", - "surface5": "#25243e", + "surface4": "#1d1c34", + "surface5": "#272640", "surface6": "#302f4c", }, "warnings": undefined, @@ -5073,25 +4962,23 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "ramp": { "bgFill1": "#9e909e", "bgFill2": "#aea0ae", - "bgFillDark": "#000", "bgFillInverted1": "#e5d6e5", "bgFillInverted2": "#f1eff1", "fgFill": "#261926", - "fgFillDark": "#f1eff1", "fgFillInverted": "#261926", - "fgSurface1": "#534d53", "fgSurface2": "#6f676f", - "fgSurface3": "#b9b1b9", - "fgSurface4": "#f1eff1", + "fgSurface3": "#8d838c", + "fgSurface4": "#e1dee1", + "fgSurface5": "#fefdfe", "stroke1": "#2a1e2a", - "stroke2": "#3b2b3b", + "stroke2": "#3b2d3b", "stroke3": "#7e607e", "stroke4": "#a17ba1", "surface1": "#160b16", "surface2": "#1f141f", "surface3": "#2a192a", - "surface4": "#301d30", - "surface5": "#362236", + "surface4": "#2d1a2d", + "surface5": "#372337", "surface6": "#422c42", }, "warnings": undefined, @@ -5108,25 +4995,23 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "ramp": { "bgFill1": "#959595", "bgFill2": "#a5a5a5", - "bgFillDark": "#000", "bgFillInverted1": "#dbdbdb", "bgFillInverted2": "#f0f0f0", "fgFill": "#1e1e1e", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#1e1e1e", - "fgSurface1": "#505050", - "fgSurface2": "#6b6b6b", - "fgSurface3": "#b4b4b4", - "fgSurface4": "#f0f0f0", + "fgSurface2": "#6c6c6c", + "fgSurface3": "#898989", + "fgSurface4": "#dfdfdf", + "fgSurface5": "#fdfdfd", "stroke1": "#242424", - "stroke2": "#313131", - "stroke3": "#6b6b6b", + "stroke2": "#323232", + "stroke3": "#6c6c6c", "stroke4": "#898989", - "surface1": "#111", + "surface1": "#121212", "surface2": "#1a1a1a", - "surface3": "#212121", - "surface4": "#252525", - "surface5": "#2a2a2a", + "surface3": "#222", + "surface4": "#232323", + "surface5": "#2c2c2c", "surface6": "#353535", }, "warnings": undefined, @@ -5143,25 +5028,23 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "ramp": { "bgFill1": "#959595", "bgFill2": "#a5a5a5", - "bgFillDark": "#000", "bgFillInverted1": "#dbdbdb", "bgFillInverted2": "#f0f0f0", "fgFill": "#1e1e1e", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#1e1e1e", - "fgSurface1": "#505050", - "fgSurface2": "#6b6b6b", - "fgSurface3": "#b4b4b4", - "fgSurface4": "#f0f0f0", + "fgSurface2": "#6c6c6c", + "fgSurface3": "#898989", + "fgSurface4": "#dfdfdf", + "fgSurface5": "#fdfdfd", "stroke1": "#242424", - "stroke2": "#313131", - "stroke3": "#6b6b6b", + "stroke2": "#323232", + "stroke3": "#6c6c6c", "stroke4": "#898989", - "surface1": "#111", + "surface1": "#121212", "surface2": "#1a1a1a", - "surface3": "#212121", - "surface4": "#252525", - "surface5": "#2a2a2a", + "surface3": "#222", + "surface4": "#232323", + "surface5": "#2c2c2c", "surface6": "#353535", }, "warnings": undefined, @@ -5178,25 +5061,23 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "ramp": { "bgFill1": "#959595", "bgFill2": "#a5a5a5", - "bgFillDark": "#000", "bgFillInverted1": "#dbdbdb", "bgFillInverted2": "#f0f0f0", "fgFill": "#1e1e1e", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#1e1e1e", - "fgSurface1": "#505050", - "fgSurface2": "#6b6b6b", - "fgSurface3": "#b4b4b4", - "fgSurface4": "#f0f0f0", + "fgSurface2": "#6c6c6c", + "fgSurface3": "#898989", + "fgSurface4": "#dfdfdf", + "fgSurface5": "#fdfdfd", "stroke1": "#242424", - "stroke2": "#313131", - "stroke3": "#6b6b6b", + "stroke2": "#323232", + "stroke3": "#6c6c6c", "stroke4": "#898989", - "surface1": "#111", + "surface1": "#121212", "surface2": "#1a1a1a", - "surface3": "#212121", - "surface4": "#252525", - "surface5": "#2a2a2a", + "surface3": "#222", + "surface4": "#232323", + "surface5": "#2c2c2c", "surface6": "#353535", }, "warnings": undefined, @@ -5213,25 +5094,23 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "ramp": { "bgFill1": "#959595", "bgFill2": "#a5a5a5", - "bgFillDark": "#000", "bgFillInverted1": "#dbdbdb", "bgFillInverted2": "#f0f0f0", "fgFill": "#1e1e1e", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#1e1e1e", - "fgSurface1": "#505050", - "fgSurface2": "#6b6b6b", - "fgSurface3": "#b4b4b4", - "fgSurface4": "#f0f0f0", + "fgSurface2": "#6c6c6c", + "fgSurface3": "#898989", + "fgSurface4": "#dfdfdf", + "fgSurface5": "#fdfdfd", "stroke1": "#242424", - "stroke2": "#313131", - "stroke3": "#6b6b6b", + "stroke2": "#323232", + "stroke3": "#6c6c6c", "stroke4": "#898989", - "surface1": "#111", + "surface1": "#121212", "surface2": "#1a1a1a", - "surface3": "#212121", - "surface4": "#252525", - "surface5": "#2a2a2a", + "surface3": "#222", + "surface4": "#232323", + "surface5": "#2c2c2c", "surface6": "#353535", }, "warnings": undefined, @@ -5248,25 +5127,23 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "ramp": { "bgFill1": "#959595", "bgFill2": "#a5a5a5", - "bgFillDark": "#000", "bgFillInverted1": "#dbdbdb", "bgFillInverted2": "#f0f0f0", "fgFill": "#1e1e1e", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#1e1e1e", - "fgSurface1": "#505050", - "fgSurface2": "#6b6b6b", - "fgSurface3": "#b4b4b4", - "fgSurface4": "#f0f0f0", + "fgSurface2": "#6c6c6c", + "fgSurface3": "#898989", + "fgSurface4": "#dfdfdf", + "fgSurface5": "#fdfdfd", "stroke1": "#242424", - "stroke2": "#313131", - "stroke3": "#6b6b6b", + "stroke2": "#323232", + "stroke3": "#6c6c6c", "stroke4": "#898989", - "surface1": "#111", + "surface1": "#121212", "surface2": "#1a1a1a", - "surface3": "#212121", - "surface4": "#252525", - "surface5": "#2a2a2a", + "surface3": "#222", + "surface4": "#232323", + "surface5": "#2c2c2c", "surface6": "#353535", }, "warnings": undefined, @@ -5283,25 +5160,23 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "ramp": { "bgFill1": "#959595", "bgFill2": "#a5a5a5", - "bgFillDark": "#000", "bgFillInverted1": "#dbdbdb", "bgFillInverted2": "#f0f0f0", "fgFill": "#1e1e1e", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#1e1e1e", - "fgSurface1": "#505050", - "fgSurface2": "#6b6b6b", - "fgSurface3": "#b4b4b4", - "fgSurface4": "#f0f0f0", + "fgSurface2": "#6c6c6c", + "fgSurface3": "#898989", + "fgSurface4": "#dfdfdf", + "fgSurface5": "#fdfdfd", "stroke1": "#242424", - "stroke2": "#313131", - "stroke3": "#6b6b6b", + "stroke2": "#323232", + "stroke3": "#6c6c6c", "stroke4": "#898989", - "surface1": "#111", + "surface1": "#121212", "surface2": "#1a1a1a", - "surface3": "#212121", - "surface4": "#252525", - "surface5": "#2a2a2a", + "surface3": "#222", + "surface4": "#232323", + "surface5": "#2c2c2c", "surface6": "#353535", }, "warnings": undefined, @@ -5323,25 +5198,23 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "ramp": { "bgFill1": "#da7365", "bgFill2": "#ec8374", - "bgFillDark": "#000", "bgFillInverted1": "#ffd0c6", "bgFillInverted2": "#f2efef", "fgFill": "#2f1612", - "fgFillDark": "#f2efef", "fgFillInverted": "#2f1612", - "fgSurface1": "#725d5a", - "fgSurface2": "#8f7874", - "fgSurface3": "#bfb0ae", - "fgSurface4": "#f2efef", + "fgSurface2": "#917a76", + "fgSurface3": "#ae9a97", + "fgSurface4": "#ebe7e6", + "fgSurface5": "#fefefd", "stroke1": "#572a23", - "stroke2": "#69342c", - "stroke3": "#be6357", + "stroke2": "#673730", + "stroke3": "#c06559", "stroke4": "#d58b7f", - "surface1": "#4e1610", + "surface1": "#501811", "surface2": "#600", - "surface3": "#601e16", - "surface4": "#662019", - "surface5": "#6d241c", + "surface3": "#622017", + "surface4": "#651f18", + "surface5": "#6f261e", "surface6": "#7a2c23", }, "warnings": undefined, @@ -5358,25 +5231,23 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "ramp": { "bgFill1": "#c0c177", "bgFill2": "#d3d48a", - "bgFillDark": "#000", "bgFillInverted1": "#ebeda2", "bgFillInverted2": "#fff", "fgFill": "#1e1e1b", - "fgFillDark": "#f1f2df", "fgFillInverted": "#1e1e1b", - "fgSurface1": "#8a8a7e", "fgSurface2": "#aaab9d", - "fgSurface3": "#d0d1c0", - "fgSurface4": "#fff", + "fgSurface3": "#cfd0c0", + "fgSurface4": "#e8e9d7", + "fgSurface5": "#fefff2", "stroke1": "#5c5d3d", - "stroke2": "#696a45", + "stroke2": "#696a49", "stroke3": "#acae75", "stroke4": "#d4d590", "surface1": "#515125", "surface2": "#575700", "surface3": "#5b5b2a", - "surface4": "#5f5f2d", - "surface5": "#636430", + "surface4": "#5e5e2c", + "surface5": "#656632", "surface6": "#6d6e38", }, "warnings": undefined, @@ -5393,25 +5264,23 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "ramp": { "bgFill1": "#7dd077", "bgFill2": "#90e389", - "bgFillDark": "#000", "bgFillInverted1": "#a9fda2", "bgFillInverted2": "#fff", "fgFill": "#1b1f1b", - "fgFillDark": "#ebf3ea", "fgFillInverted": "#1b1f1b", - "fgSurface1": "#808d7f", "fgSurface2": "#9fae9d", - "fgSurface3": "#c2d5c0", - "fgSurface4": "#fff", + "fgSurface3": "#c0d2be", + "fgSurface4": "#dfebde", + "fgSurface5": "#fcfefc", "stroke1": "#3f643c", - "stroke2": "#487144", + "stroke2": "#4b7147", "stroke3": "#79b974", "stroke4": "#96e48f", - "surface1": "#275a24", + "surface1": "#255822", "surface2": "#006200", - "surface3": "#2d6529", - "surface4": "#2f682c", - "surface5": "#326d2f", + "surface3": "#2b6327", + "surface4": "#2d662b", + "surface5": "#346f31", "surface6": "#3b7837", }, "warnings": undefined, @@ -5428,25 +5297,23 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "ramp": { "bgFill1": "#7fcac9", "bgFill2": "#92dddc", - "bgFillDark": "#000", "bgFillInverted1": "#aaf6f5", "bgFillInverted2": "#fff", "fgFill": "#1c1f1f", - "fgFillDark": "#e8f3f2", "fgFillInverted": "#1c1f1f", - "fgSurface1": "#818c8c", "fgSurface2": "#9fadac", - "fgSurface3": "#c2d3d2", - "fgSurface4": "#fff", + "fgSurface3": "#c1d2d1", + "fgSurface4": "#daebeb", + "fgSurface5": "#fbfefe", "stroke1": "#406160", - "stroke2": "#496e6d", + "stroke2": "#4c6e6d", "stroke3": "#7ab4b3", "stroke4": "#98dedd", - "surface1": "#285756", + "surface1": "#275655", "surface2": "#005e5e", - "surface3": "#2e6161", - "surface4": "#306564", - "surface5": "#336969", + "surface3": "#2d6060", + "surface4": "#2e6362", + "surface5": "#366b6b", "surface6": "#3c7473", }, "warnings": undefined, @@ -5463,25 +5330,23 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "ramp": { "bgFill1": "#6291f7", "bgFill2": "#73a3ff", - "bgFillDark": "#000", "bgFillInverted1": "#c4ddff", "bgFillInverted2": "#eff0f2", "fgFill": "#05184c", - "fgFillDark": "#eff0f2", "fgFillInverted": "#05184c", - "fgSurface1": "#465064", - "fgSurface2": "#616a7d", - "fgSurface3": "#b0b4bd", - "fgSurface4": "#eff0f2", + "fgSurface2": "#646d80", + "fgSurface3": "#828a98", + "fgSurface4": "#dfe1e5", + "fgSurface5": "#fdfefe", "stroke1": "#0d204d", - "stroke2": "#142d67", - "stroke3": "#4568b4", + "stroke2": "#1c3364", + "stroke3": "#476bb7", "stroke4": "#6b88c3", - "surface1": "#010b38", + "surface1": "#020f3c", "surface2": "#006", - "surface3": "#03175c", + "surface3": "#051b60", "surface4": "#031b65", - "surface5": "#052170", + "surface5": "#072473", "surface6": "#0b2b83", }, "warnings": undefined, @@ -5498,25 +5363,23 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "ramp": { "bgFill1": "#d273ce", "bgFill2": "#e383e0", - "bgFillDark": "#000", "bgFillInverted1": "#ffc9ff", "bgFillInverted2": "#f2eff2", "fgFill": "#261925", - "fgFillDark": "#f2eff2", "fgFillInverted": "#261925", - "fgSurface1": "#776375", - "fgSurface2": "#938091", - "fgSurface3": "#bfaebe", - "fgSurface4": "#f2eff2", + "fgSurface2": "#958293", + "fgSurface3": "#b6a2b5", + "fgSurface4": "#ebe6eb", + "fgSurface5": "#fefdfe", "stroke1": "#582f56", - "stroke2": "#683966", - "stroke3": "#b869b5", + "stroke2": "#673c65", + "stroke3": "#bb6bb8", "stroke4": "#da8bd7", - "surface1": "#511c50", + "surface1": "#531e52", "surface2": "#606", - "surface3": "#60225f", + "surface3": "#622461", "surface4": "#652564", - "surface5": "#6c286a", + "surface5": "#6f2b6d", "surface6": "#783177", }, "warnings": undefined, @@ -5533,25 +5396,23 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "ramp": { "bgFill1": "#d2796f", "bgFill2": "#e4897e", - "bgFillDark": "#000", "bgFillInverted1": "#ffd0c8", "bgFillInverted2": "#f2efef", "fgFill": "#301512", - "fgFillDark": "#f2efef", "fgFillInverted": "#301512", - "fgSurface1": "#6d5b58", - "fgSurface2": "#8b7572", - "fgSurface3": "#bfb1af", - "fgSurface4": "#f2efef", + "fgSurface2": "#8c7673", + "fgSurface3": "#a99693", + "fgSurface4": "#e9e4e4", + "fgSurface5": "#fefdfd", "stroke1": "#522823", - "stroke2": "#63322d", - "stroke3": "#b76259", + "stroke2": "#613430", + "stroke3": "#b9635a", "stroke4": "#d1877e", - "surface1": "#481612", + "surface1": "#491713", "surface2": "#5c0a0a", - "surface3": "#5a1d19", - "surface4": "#60201c", - "surface5": "#66241f", + "surface3": "#5b1e1a", + "surface4": "#5f1f1b", + "surface5": "#692621", "surface6": "#742c27", }, "warnings": undefined, @@ -5568,25 +5429,23 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "ramp": { "bgFill1": "#bfc178", "bgFill2": "#d2d58a", - "bgFillDark": "#000", "bgFillInverted1": "#ebeda2", "bgFillInverted2": "#fff", "fgFill": "#1e1e1b", - "fgFillDark": "#f1f2df", "fgFillInverted": "#1e1e1b", - "fgSurface1": "#898a7f", - "fgSurface2": "#aaab9d", - "fgSurface3": "#d0d1c0", - "fgSurface4": "#fff", + "fgSurface2": "#abac9e", + "fgSurface3": "#d0d1c1", + "fgSurface4": "#e8e9d7", + "fgSurface5": "#fefff2", "stroke1": "#5c5d3d", - "stroke2": "#696a45", - "stroke3": "#acae75", + "stroke2": "#696a49", + "stroke3": "#adaf76", "stroke4": "#d3d691", "surface1": "#515125", "surface2": "#575701", "surface3": "#5b5c2b", - "surface4": "#5e5f2d", - "surface5": "#636430", + "surface4": "#5d5e2c", + "surface5": "#656632", "surface6": "#6d6e38", }, "warnings": undefined, @@ -5603,25 +5462,23 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "ramp": { "bgFill1": "#7cc677", "bgFill2": "#8fd989", - "bgFillDark": "#000", "bgFillInverted1": "#a6f2a0", "bgFillInverted2": "#f1f7f1", "fgFill": "#1b1f1b", - "fgFillDark": "#ebf3ea", "fgFillInverted": "#1b1f1b", - "fgSurface1": "#7b8779", "fgSurface2": "#98a797", - "fgSurface3": "#baccb9", - "fgSurface4": "#f1f7f1", + "fgSurface3": "#bacbb8", + "fgSurface4": "#dfebde", + "fgSurface5": "#fcfefc", "stroke1": "#3c5e39", - "stroke2": "#456b42", + "stroke2": "#486b45", "stroke3": "#75b170", "stroke4": "#91db8c", - "surface1": "#255323", + "surface1": "#245222", "surface2": "#0a5c0a", - "surface3": "#2b5f28", - "surface4": "#2d622a", - "surface5": "#30672e", + "surface3": "#2a5e27", + "surface4": "#2c6129", + "surface5": "#326930", "surface6": "#397135", }, "warnings": undefined, @@ -5638,25 +5495,23 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "ramp": { "bgFill1": "#80c6c5", "bgFill2": "#93d9d9", - "bgFillDark": "#000", "bgFillInverted1": "#abf2f1", "bgFillInverted2": "#f8fdfd", "fgFill": "#1c1f1f", - "fgFillDark": "#e8f3f2", "fgFillInverted": "#1c1f1f", - "fgSurface1": "#7f8a89", "fgSurface2": "#9daaaa", - "fgSurface3": "#c0d0cf", - "fgSurface4": "#f8fdfd", + "fgSurface3": "#c0d0d0", + "fgSurface4": "#dbebea", + "fgSurface5": "#fbfefe", "stroke1": "#3f5f5e", - "stroke2": "#486c6b", - "stroke3": "#7ab1b1", + "stroke2": "#4b6c6b", + "stroke3": "#7bb2b2", "stroke4": "#97dad9", "surface1": "#285454", "surface2": "#0a5c5c", "surface3": "#2e5f5e", - "surface4": "#306262", - "surface5": "#346767", + "surface4": "#2f6161", + "surface5": "#366969", "surface6": "#3c7271", }, "warnings": undefined, @@ -5673,25 +5528,23 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "ramp": { "bgFill1": "#7490e8", "bgFill2": "#83a0fa", - "bgFillDark": "#000", "bgFillInverted1": "#c9dbff", "bgFillInverted2": "#eff0f2", "fgFill": "#0e1848", - "fgFillDark": "#eff0f2", "fgFillInverted": "#0e1848", - "fgSurface1": "#4a5063", - "fgSurface2": "#646b7d", - "fgSurface3": "#b1b4bd", - "fgSurface4": "#eff0f2", + "fgSurface2": "#666d7f", + "fgSurface3": "#858a98", + "fgSurface4": "#dfe0e4", + "fgSurface5": "#fdfefe", "stroke1": "#16214b", - "stroke2": "#1f2d63", - "stroke3": "#5168b0", + "stroke2": "#253260", + "stroke3": "#536ab2", "stroke4": "#7488c0", - "surface1": "#060d38", + "surface1": "#07103b", "surface2": "#0a0a5c", - "surface3": "#0f1959", - "surface4": "#121e62", - "surface5": "#16226c", + "surface3": "#111c5c", + "surface4": "#111c60", + "surface5": "#18246e", "surface6": "#1d2d7d", }, "warnings": undefined, @@ -5708,25 +5561,23 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "ramp": { "bgFill1": "#c573c3", "bgFill2": "#d783d4", - "bgFillDark": "#000", "bgFillInverted1": "#ffc9ff", "bgFillInverted2": "#f2eff2", "fgFill": "#281827", - "fgFillDark": "#f2eff2", "fgFillInverted": "#281827", - "fgSurface1": "#716070", - "fgSurface2": "#8e7c8c", - "fgSurface3": "#bfaebe", - "fgSurface4": "#f2eff2", + "fgSurface2": "#907e8e", + "fgSurface3": "#b19cb0", + "fgSurface4": "#ebe6eb", + "fgSurface5": "#fefdfe", "stroke1": "#532d51", - "stroke2": "#623761", - "stroke3": "#b067ae", + "stroke2": "#613960", + "stroke3": "#b369b1", "stroke4": "#d786d4", - "surface1": "#491a49", + "surface1": "#4b1c4b", "surface2": "#5c0a5c", - "surface3": "#592158", + "surface3": "#5b235a", "surface4": "#5e245d", - "surface5": "#642763", + "surface5": "#672a66", "surface6": "#71306f", }, "warnings": undefined, @@ -5743,25 +5594,23 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "ramp": { "bgFill1": "#c77f79", "bgFill2": "#d98f89", - "bgFillDark": "#000", "bgFillInverted1": "#ffd0ca", "bgFillInverted2": "#f2efef", "fgFill": "#2f1514", - "fgFillDark": "#f2efef", "fgFillInverted": "#2f1514", - "fgSurface1": "#6a5a58", - "fgSurface2": "#877472", - "fgSurface3": "#beb1af", - "fgSurface4": "#f2efef", + "fgSurface2": "#897674", + "fgSurface3": "#a79492", + "fgSurface4": "#e8e3e3", + "fgSurface5": "#fefdfd", "stroke1": "#4d2825", - "stroke2": "#5e322f", - "stroke3": "#af635e", + "stroke2": "#5c3532", + "stroke3": "#b16560", "stroke4": "#cc8680", - "surface1": "#421615", + "surface1": "#431716", "surface2": "#521414", - "surface3": "#541f1d", - "surface4": "#592220", - "surface5": "#602523", + "surface3": "#55201e", + "surface4": "#58211f", + "surface5": "#632825", "surface6": "#6e2e2c", }, "warnings": undefined, @@ -5778,25 +5627,23 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "ramp": { "bgFill1": "#b8b97d", "bgFill2": "#cacc8f", - "bgFillDark": "#000", "bgFillInverted1": "#e2e4a6", "bgFillInverted2": "#f6f7e5", "fgFill": "#1e1e1b", - "fgFillDark": "#f1f2e0", "fgFillInverted": "#1e1e1b", - "fgSurface1": "#84857a", "fgSurface2": "#a4a498", "fgSurface3": "#c8c9ba", - "fgSurface4": "#f6f7e5", + "fgSurface4": "#e8e9d8", + "fgSurface5": "#fefff2", "stroke1": "#57583b", - "stroke2": "#636444", + "stroke2": "#636446", "stroke3": "#a5a773", "stroke4": "#cccd8f", "surface1": "#4c4c25", "surface2": "#525214", "surface3": "#56562b", - "surface4": "#595a2e", - "surface5": "#5e5e31", + "surface4": "#58592d", + "surface5": "#606033", "surface6": "#686839", }, "warnings": undefined, @@ -5813,25 +5660,23 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "ramp": { "bgFill1": "#7bb878", "bgFill2": "#8dca89", - "bgFillDark": "#000", "bgFillInverted1": "#aceba7", "bgFillInverted2": "#ebf2eb", "fgFill": "#1a201a", - "fgFillDark": "#ebf2eb", "fgFillInverted": "#1a201a", - "fgSurface1": "#727d71", "fgSurface2": "#8f9c8d", - "fgSurface3": "#afbfae", - "fgSurface4": "#ebf2eb", + "fgSurface3": "#b0c0ae", + "fgSurface4": "#e0ebdf", + "fgSurface5": "#fcfffc", "stroke1": "#375535", - "stroke2": "#40613e", - "stroke3": "#6fa56c", + "stroke2": "#426140", + "stroke3": "#70a66d", "stroke4": "#8acc86", "surface1": "#224a21", "surface2": "#145214", "surface3": "#285527", - "surface4": "#2b5929", - "surface5": "#2e5d2c", + "surface4": "#2a5828", + "surface5": "#31602e", "surface6": "#366834", }, "warnings": undefined, @@ -5848,25 +5693,23 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "ramp": { "bgFill1": "#7eb8b7", "bgFill2": "#90caca", - "bgFillDark": "#000", "bgFillInverted1": "#abe6e5", "bgFillInverted2": "#e8f3f2", "fgFill": "#1b1f1f", - "fgFillDark": "#e8f3f2", "fgFillInverted": "#1b1f1f", - "fgSurface1": "#767f7f", "fgSurface2": "#939e9e", "fgSurface3": "#b4c2c2", - "fgSurface4": "#e8f3f2", + "fgSurface4": "#dbebea", + "fgSurface5": "#fbfefe", "stroke1": "#395555", - "stroke2": "#436262", + "stroke2": "#456262", "stroke3": "#73a5a4", "stroke4": "#8fcccb", "surface1": "#254b4b", "surface2": "#145252", "surface3": "#2b5555", - "surface4": "#2d5958", - "surface5": "#305d5d", + "surface4": "#2c5857", + "surface5": "#336060", "surface6": "#396867", }, "warnings": undefined, @@ -5883,25 +5726,23 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "ramp": { "bgFill1": "#8290d7", "bgFill2": "#91a0e9", - "bgFillDark": "#000", "bgFillInverted1": "#cedaff", "bgFillInverted2": "#f0f0f2", "fgFill": "#151842", - "fgFillDark": "#f0f0f2", "fgFillInverted": "#151842", - "fgSurface1": "#4e5263", - "fgSurface2": "#696d7d", - "fgSurface3": "#b2b4bc", - "fgSurface4": "#f0f0f2", + "fgSurface2": "#6a6e7e", + "fgSurface3": "#888b98", + "fgSurface4": "#dfe0e4", + "fgSurface5": "#fdfdfe", "stroke1": "#1e234a", - "stroke2": "#293060", - "stroke3": "#5d69ab", + "stroke2": "#2c335d", + "stroke3": "#5e6bad", "stroke4": "#7f89bc", - "surface1": "#0f113b", + "surface1": "#10123c", "surface2": "#141452", - "surface3": "#1a1d56", - "surface4": "#1d215e", - "surface5": "#212566", + "surface3": "#1b1e57", + "surface4": "#1b1f5c", + "surface5": "#232869", "surface6": "#2a3077", }, "warnings": undefined, @@ -5918,25 +5759,23 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "ramp": { "bgFill1": "#bd7abb", "bgFill2": "#ce8acb", - "bgFillDark": "#000", "bgFillInverted1": "#ffc9ff", "bgFillInverted2": "#f2eff2", "fgFill": "#281728", - "fgFillDark": "#f2eff2", "fgFillInverted": "#281728", - "fgSurface1": "#6c5e6c", - "fgSurface2": "#897988", - "fgSurface3": "#beaebd", - "fgSurface4": "#f2eff2", + "fgSurface2": "#8a7a89", + "fgSurface3": "#ab98aa", + "fgSurface4": "#eae5e9", + "fgSurface5": "#fefdfe", "stroke1": "#4c2b4b", - "stroke2": "#5c355b", - "stroke3": "#a866a6", + "stroke2": "#5b385a", + "stroke3": "#aa68a8", "stroke4": "#d083ce", - "surface1": "#431a43", + "surface1": "#441b44", "surface2": "#521452", - "surface3": "#522152", - "surface4": "#572457", - "surface5": "#5d285d", + "surface3": "#532253", + "surface4": "#562356", + "surface5": "#602a60", "surface6": "#6a3069", }, "warnings": undefined, @@ -5953,25 +5792,23 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "ramp": { "bgFill1": "#b98684", "bgFill2": "#ca9694", - "bgFillDark": "#000", "bgFillInverted1": "#ffd0cd", "bgFillInverted2": "#f2efef", "fgFill": "#2d1717", - "fgFillDark": "#f2efef", "fgFillInverted": "#2d1717", - "fgSurface1": "#685b5a", "fgSurface2": "#867675", - "fgSurface3": "#bcb1b1", - "fgSurface4": "#f2efef", + "fgSurface3": "#a49494", + "fgSurface4": "#e7e3e3", + "fgSurface5": "#fefdfd", "stroke1": "#492a2a", - "stroke2": "#5a3534", - "stroke3": "#a86866", + "stroke2": "#583736", + "stroke3": "#a96866", "stroke4": "#c58a88", - "surface1": "#3e1a1a", + "surface1": "#3f1a1a", "surface2": "#471f1f", - "surface3": "#4f2323", - "surface4": "#542626", - "surface5": "#5b2a2a", + "surface3": "#502323", + "surface4": "#532525", + "surface5": "#5d2c2c", "surface6": "#673333", }, "warnings": undefined, @@ -5988,25 +5825,23 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "ramp": { "bgFill1": "#aaab7f", "bgFill2": "#bcbd90", - "bgFillDark": "#000", "bgFillInverted1": "#dddeb0", "bgFillInverted2": "#f1f2e2", "fgFill": "#1e1e1a", - "fgFillDark": "#f1f2e2", "fgFillInverted": "#1e1e1a", - "fgSurface1": "#797a71", "fgSurface2": "#97988d", "fgSurface3": "#babaae", - "fgSurface4": "#f1f2e2", + "fgSurface4": "#e8e9d9", + "fgSurface5": "#fefef4", "stroke1": "#4d4e37", - "stroke2": "#595a40", + "stroke2": "#595a42", "stroke3": "#999a6f", "stroke4": "#bdbe8b", "surface1": "#424224", "surface2": "#47471f", "surface3": "#4c4c2a", - "surface4": "#4f502c", - "surface5": "#54542f", + "surface4": "#4e4f2b", + "surface5": "#565631", "surface6": "#5e5e37", }, "warnings": undefined, @@ -6023,25 +5858,23 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "ramp": { "bgFill1": "#7fab7d", "bgFill2": "#8fbc8d", - "bgFillDark": "#000", "bgFillInverted1": "#b9e7b6", "bgFillInverted2": "#ecf2ec", "fgFill": "#192019", - "fgFillDark": "#ecf2ec", "fgFillInverted": "#192019", - "fgSurface1": "#6b746a", "fgSurface2": "#879286", - "fgSurface3": "#abb9aa", - "fgSurface4": "#ecf2ec", + "fgSurface3": "#a6b3a5", + "fgSurface4": "#e1eae0", + "fgSurface5": "#fcfefc", "stroke1": "#334c32", - "stroke2": "#3c593b", + "stroke2": "#3f593e", "stroke3": "#6c9a6b", "stroke4": "#88c085", - "surface1": "#214120", + "surface1": "#20401f", "surface2": "#1f471f", - "surface3": "#284c27", - "surface4": "#2a5029", - "surface5": "#2d542d", + "surface3": "#274b26", + "surface4": "#284e27", + "surface5": "#2f562f", "surface6": "#355e34", }, "warnings": undefined, @@ -6058,25 +5891,23 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "ramp": { "bgFill1": "#80aaaa", "bgFill2": "#91bcbb", - "bgFillDark": "#000", "bgFillInverted1": "#b7e3e3", "bgFillInverted2": "#e9f2f2", "fgFill": "#1a1f1f", - "fgFillDark": "#e9f2f2", "fgFillInverted": "#1a1f1f", - "fgSurface1": "#6d7676", "fgSurface2": "#8a9493", - "fgSurface3": "#abb7b7", - "fgSurface4": "#e9f2f2", + "fgSurface3": "#a9b5b5", + "fgSurface4": "#dceaea", + "fgSurface5": "#fbfefe", "stroke1": "#364c4c", - "stroke2": "#3f5959", + "stroke2": "#425959", "stroke3": "#6f9999", "stroke4": "#8bbebe", - "surface1": "#234141", + "surface1": "#224040", "surface2": "#1f4747", - "surface3": "#2a4c4c", - "surface4": "#2c4f4f", - "surface5": "#2f5454", + "surface3": "#294b4b", + "surface4": "#2b4e4e", + "surface5": "#315656", "surface6": "#375e5e", }, "warnings": undefined, @@ -6093,25 +5924,23 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "ramp": { "bgFill1": "#8c91c3", "bgFill2": "#9ca1d4", - "bgFillDark": "#000", "bgFillInverted1": "#d3d8ff", "bgFillInverted2": "#f0f0f1", "fgFill": "#191a39", - "fgFillDark": "#f0f0f1", "fgFillInverted": "#191a39", - "fgSurface1": "#555664", - "fgSurface2": "#70717f", - "fgSurface3": "#b3b4ba", - "fgSurface4": "#f0f0f1", + "fgSurface2": "#717280", + "fgSurface3": "#8f909a", + "fgSurface4": "#e1e1e4", + "fgSurface5": "#fdfdfe", "stroke1": "#27284a", - "stroke2": "#33345d", - "stroke3": "#6a6ea3", + "stroke2": "#35365b", + "stroke3": "#6b6fa4", "stroke4": "#8a8eb7", - "surface1": "#19183e", + "surface1": "#1a193f", "surface2": "#1f1f47", - "surface3": "#242353", - "surface4": "#27275a", - "surface5": "#2c2c61", + "surface3": "#252454", + "surface4": "#262658", + "surface5": "#2e2e63", "surface6": "#353670", }, "warnings": undefined, @@ -6128,25 +5957,23 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "ramp": { "bgFill1": "#b282b1", "bgFill2": "#c392c2", - "bgFillDark": "#000", "bgFillInverted1": "#fdcafc", "bgFillInverted2": "#f2eff2", "fgFill": "#281727", - "fgFillDark": "#f2eff2", "fgFillInverted": "#281727", - "fgSurface1": "#695d69", - "fgSurface2": "#867885", - "fgSurface3": "#bdafbc", - "fgSurface4": "#f2eff2", + "fgSurface2": "#877986", + "fgSurface3": "#a898a7", + "fgSurface4": "#e8e4e8", + "fgSurface5": "#fefdfe", "stroke1": "#482d48", - "stroke2": "#583757", - "stroke3": "#a16aa0", + "stroke2": "#573a56", + "stroke3": "#a26ba1", "stroke4": "#c886c7", - "surface1": "#3e1c3d", + "surface1": "#3f1d3e", "surface2": "#471f47", - "surface3": "#4d244c", - "surface4": "#512751", - "surface5": "#572b57", + "surface3": "#4e254d", + "surface4": "#502650", + "surface5": "#592d59", "surface6": "#633463", }, "warnings": undefined, @@ -6163,25 +5990,23 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "ramp": { "bgFill1": "#a88e8e", "bgFill2": "#b89e9e", - "bgFillDark": "#000", "bgFillInverted1": "#f0d4d4", "bgFillInverted2": "#f1f0f0", "fgFill": "#281a1a", - "fgFillDark": "#f1f0f0", "fgFillInverted": "#281a1a", - "fgSurface1": "#685e5e", "fgSurface2": "#857a79", - "fgSurface3": "#bab2b2", - "fgSurface4": "#f1f0f0", + "fgSurface3": "#a39999", + "fgSurface4": "#e7e4e4", + "fgSurface5": "#fefdfd", "stroke1": "#463030", - "stroke2": "#563b3b", + "stroke2": "#553d3d", "stroke3": "#9e7171", "stroke4": "#bb9393", "surface1": "#3b2122", "surface2": "#3d2929", "surface3": "#4a2b2b", - "surface4": "#4f2e2e", - "surface5": "#543233", + "surface4": "#4d2c2d", + "surface5": "#563435", "surface6": "#613b3c", }, "warnings": undefined, @@ -6198,25 +6023,23 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "ramp": { "bgFill1": "#9d9e86", "bgFill2": "#aeae97", - "bgFillDark": "#000", "bgFillInverted1": "#dcddc4", "bgFillInverted2": "#f1f1e5", "fgFill": "#1e1e19", - "fgFillDark": "#f1f1e5", "fgFillInverted": "#1e1e19", - "fgSurface1": "#6f6f69", "fgSurface2": "#8c8c85", - "fgSurface3": "#b5b5ac", - "fgSurface4": "#f1f1e5", + "fgSurface3": "#adada4", + "fgSurface4": "#e8e8dd", + "fgSurface5": "#fefef6", "stroke1": "#444434", - "stroke2": "#50503e", + "stroke2": "#505040", "stroke3": "#8d8e6f", "stroke4": "#b0b08b", "surface1": "#383824", "surface2": "#3d3d29", "surface3": "#42422b", - "surface4": "#46462e", - "surface5": "#4a4a31", + "surface4": "#45452d", + "surface5": "#4c4c33", "surface6": "#545439", }, "warnings": undefined, @@ -6233,25 +6056,23 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "ramp": { "bgFill1": "#869e86", "bgFill2": "#96ae96", - "bgFillDark": "#000", "bgFillInverted1": "#c9e2c8", "bgFillInverted2": "#edf2ed", "fgFill": "#192019", - "fgFillDark": "#edf2ed", "fgFillInverted": "#192019", - "fgSurface1": "#666c65", "fgSurface2": "#818981", - "fgSurface3": "#adb8ad", - "fgSurface4": "#edf2ed", + "fgSurface3": "#a0aaa0", + "fgSurface4": "#e2eae2", + "fgSurface5": "#fcfefc", "stroke1": "#324432", - "stroke2": "#3c503c", + "stroke2": "#3d503d", "stroke3": "#6e906e", "stroke4": "#8bb38a", "surface1": "#223823", "surface2": "#293d29", "surface3": "#2a432a", - "surface4": "#2d472d", - "surface5": "#304c30", + "surface4": "#2c462c", + "surface5": "#324e32", "surface6": "#385639", }, "warnings": undefined, @@ -6268,25 +6089,23 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "ramp": { "bgFill1": "#879e9d", "bgFill2": "#97aeae", - "bgFillDark": "#000", "bgFillInverted1": "#c7e0df", "bgFillInverted2": "#ebf2f2", "fgFill": "#191f1f", - "fgFillDark": "#ebf2f2", "fgFillInverted": "#191f1f", - "fgSurface1": "#676d6d", "fgSurface2": "#838a8a", - "fgSurface3": "#adb6b6", - "fgSurface4": "#ebf2f2", + "fgSurface3": "#a3abab", + "fgSurface4": "#dfeaea", + "fgSurface5": "#fcfefe", "stroke1": "#344444", - "stroke2": "#3e5050", - "stroke3": "#708e8e", + "stroke2": "#3f5050", + "stroke3": "#718f8f", "stroke4": "#8db2b2", "surface1": "#243838", "surface2": "#293d3d", "surface3": "#2b4343", - "surface4": "#2e4646", - "surface5": "#324b4b", + "surface4": "#2d4545", + "surface5": "#344d4d", "surface6": "#3a5555", }, "warnings": undefined, @@ -6303,25 +6122,23 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "ramp": { "bgFill1": "#9293ac", "bgFill2": "#a2a3bd", - "bgFillDark": "#000", "bgFillInverted1": "#d8d9f4", "bgFillInverted2": "#f0f0f1", "fgFill": "#1c1c2e", - "fgFillDark": "#f0f0f1", "fgFillInverted": "#1c1c2e", - "fgSurface1": "#5c5d67", "fgSurface2": "#787881", - "fgSurface3": "#b3b4b8", - "fgSurface4": "#f0f0f1", + "fgSurface3": "#96979d", + "fgSurface4": "#e3e3e5", + "fgSurface5": "#fdfdfe", "stroke1": "#303049", - "stroke2": "#3c3c59", + "stroke2": "#3d3d58", "stroke3": "#757699", "stroke4": "#9697b1", "surface1": "#23223e", "surface2": "#29293d", "surface3": "#2e2c4f", - "surface4": "#313054", - "surface5": "#36345b", + "surface4": "#302e52", + "surface5": "#38365d", "surface6": "#3f3e67", }, "warnings": undefined, @@ -6338,25 +6155,23 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "ramp": { "bgFill1": "#a58ca4", "bgFill2": "#b59cb5", - "bgFillDark": "#000", "bgFillInverted1": "#edd2ec", "bgFillInverted2": "#f1eff1", "fgFill": "#251925", - "fgFillDark": "#f1eff1", "fgFillInverted": "#251925", - "fgSurface1": "#686068", "fgSurface2": "#857b85", - "fgSurface3": "#bab1ba", - "fgSurface4": "#f1eff1", + "fgSurface3": "#a599a5", + "fgSurface4": "#e7e4e7", + "fgSurface5": "#fefdfe", "stroke1": "#443044", - "stroke2": "#533b53", + "stroke2": "#523d52", "stroke3": "#987198", "stroke4": "#bd8fbd", "surface1": "#3a223a", "surface2": "#3d293d", "surface3": "#472b48", - "surface4": "#4c2e4c", - "surface5": "#513251", + "surface4": "#4a2c4a", + "surface5": "#533453", "surface6": "#5d3b5d", }, "warnings": undefined, @@ -6373,25 +6188,23 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "ramp": { "bgFill1": "#959595", "bgFill2": "#a5a5a5", - "bgFillDark": "#000", "bgFillInverted1": "#dbdbdb", "bgFillInverted2": "#f0f0f0", "fgFill": "#1e1e1e", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#1e1e1e", - "fgSurface1": "#656565", "fgSurface2": "#828282", - "fgSurface3": "#b4b4b4", - "fgSurface4": "#f0f0f0", + "fgSurface3": "#a2a2a2", + "fgSurface4": "#e6e6e6", + "fgSurface5": "#fdfdfd", "stroke1": "#3a3a3a", "stroke2": "#474747", - "stroke3": "#828282", + "stroke3": "#838383", "stroke4": "#a2a2a2", - "surface1": "#2d2d2d", + "surface1": "#2e2e2e", "surface2": "#333", - "surface3": "#383838", - "surface4": "#3c3c3c", - "surface5": "#404040", + "surface3": "#393939", + "surface4": "#3b3b3b", + "surface5": "#424242", "surface6": "#4a4a4a", }, "warnings": undefined, @@ -6408,25 +6221,23 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "ramp": { "bgFill1": "#959595", "bgFill2": "#a5a5a5", - "bgFillDark": "#000", "bgFillInverted1": "#dbdbdb", "bgFillInverted2": "#f0f0f0", "fgFill": "#1e1e1e", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#1e1e1e", - "fgSurface1": "#656565", "fgSurface2": "#828282", - "fgSurface3": "#b4b4b4", - "fgSurface4": "#f0f0f0", + "fgSurface3": "#a2a2a2", + "fgSurface4": "#e6e6e6", + "fgSurface5": "#fdfdfd", "stroke1": "#3a3a3a", "stroke2": "#474747", - "stroke3": "#828282", + "stroke3": "#838383", "stroke4": "#a2a2a2", - "surface1": "#2d2d2d", + "surface1": "#2e2e2e", "surface2": "#333", - "surface3": "#383838", - "surface4": "#3c3c3c", - "surface5": "#404040", + "surface3": "#393939", + "surface4": "#3b3b3b", + "surface5": "#424242", "surface6": "#4a4a4a", }, "warnings": undefined, @@ -6443,25 +6254,23 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "ramp": { "bgFill1": "#959595", "bgFill2": "#a5a5a5", - "bgFillDark": "#000", "bgFillInverted1": "#dbdbdb", "bgFillInverted2": "#f0f0f0", "fgFill": "#1e1e1e", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#1e1e1e", - "fgSurface1": "#656565", "fgSurface2": "#828282", - "fgSurface3": "#b4b4b4", - "fgSurface4": "#f0f0f0", + "fgSurface3": "#a2a2a2", + "fgSurface4": "#e6e6e6", + "fgSurface5": "#fdfdfd", "stroke1": "#3a3a3a", "stroke2": "#474747", - "stroke3": "#828282", + "stroke3": "#838383", "stroke4": "#a2a2a2", - "surface1": "#2d2d2d", + "surface1": "#2e2e2e", "surface2": "#333", - "surface3": "#383838", - "surface4": "#3c3c3c", - "surface5": "#404040", + "surface3": "#393939", + "surface4": "#3b3b3b", + "surface5": "#424242", "surface6": "#4a4a4a", }, "warnings": undefined, @@ -6478,25 +6287,23 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "ramp": { "bgFill1": "#959595", "bgFill2": "#a5a5a5", - "bgFillDark": "#000", "bgFillInverted1": "#dbdbdb", "bgFillInverted2": "#f0f0f0", "fgFill": "#1e1e1e", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#1e1e1e", - "fgSurface1": "#656565", "fgSurface2": "#828282", - "fgSurface3": "#b4b4b4", - "fgSurface4": "#f0f0f0", + "fgSurface3": "#a2a2a2", + "fgSurface4": "#e6e6e6", + "fgSurface5": "#fdfdfd", "stroke1": "#3a3a3a", "stroke2": "#474747", - "stroke3": "#828282", + "stroke3": "#838383", "stroke4": "#a2a2a2", - "surface1": "#2d2d2d", + "surface1": "#2e2e2e", "surface2": "#333", - "surface3": "#383838", - "surface4": "#3c3c3c", - "surface5": "#404040", + "surface3": "#393939", + "surface4": "#3b3b3b", + "surface5": "#424242", "surface6": "#4a4a4a", }, "warnings": undefined, @@ -6513,25 +6320,23 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "ramp": { "bgFill1": "#959595", "bgFill2": "#a5a5a5", - "bgFillDark": "#000", "bgFillInverted1": "#dbdbdb", "bgFillInverted2": "#f0f0f0", "fgFill": "#1e1e1e", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#1e1e1e", - "fgSurface1": "#656565", "fgSurface2": "#828282", - "fgSurface3": "#b4b4b4", - "fgSurface4": "#f0f0f0", + "fgSurface3": "#a2a2a2", + "fgSurface4": "#e6e6e6", + "fgSurface5": "#fdfdfd", "stroke1": "#3a3a3a", "stroke2": "#474747", - "stroke3": "#828282", + "stroke3": "#838383", "stroke4": "#a2a2a2", - "surface1": "#2d2d2d", + "surface1": "#2e2e2e", "surface2": "#333", - "surface3": "#383838", - "surface4": "#3c3c3c", - "surface5": "#404040", + "surface3": "#393939", + "surface4": "#3b3b3b", + "surface5": "#424242", "surface6": "#4a4a4a", }, "warnings": undefined, @@ -6548,25 +6353,23 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "ramp": { "bgFill1": "#959595", "bgFill2": "#a5a5a5", - "bgFillDark": "#000", "bgFillInverted1": "#dbdbdb", "bgFillInverted2": "#f0f0f0", "fgFill": "#1e1e1e", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#1e1e1e", - "fgSurface1": "#656565", "fgSurface2": "#828282", - "fgSurface3": "#b4b4b4", - "fgSurface4": "#f0f0f0", + "fgSurface3": "#a2a2a2", + "fgSurface4": "#e6e6e6", + "fgSurface5": "#fdfdfd", "stroke1": "#3a3a3a", "stroke2": "#474747", - "stroke3": "#828282", + "stroke3": "#838383", "stroke4": "#a2a2a2", - "surface1": "#2d2d2d", + "surface1": "#2e2e2e", "surface2": "#333", - "surface3": "#383838", - "surface4": "#3c3c3c", - "surface5": "#404040", + "surface3": "#393939", + "surface4": "#3b3b3b", + "surface5": "#424242", "surface6": "#4a4a4a", }, "warnings": undefined, @@ -6588,25 +6391,23 @@ exports[`buildRamps > background ramp snapshots at lightness 30 1`] = ` "ramp": { "bgFill1": "#ff8f7e", "bgFill2": "#ffab9c", - "bgFillDark": "#000", "bgFillInverted1": "#ffd0c6", "bgFillInverted2": "#f2efef", "fgFill": "#241c1b", - "fgFillDark": "#f2efef", "fgFillInverted": "#241c1b", - "fgSurface1": "#947772", - "fgSurface2": "#ac9894", - "fgSurface3": "#c9bdbb", - "fgSurface4": "#f2efef", + "fgSurface2": "#af9b97", + "fgSurface3": "#ccc0be", + "fgSurface4": "#ebe7e6", + "fgSurface5": "#fefefd", "stroke1": "#7d3e36", - "stroke2": "#8f483f", - "stroke3": "#d5897d", + "stroke2": "#8d4b43", + "stroke3": "#d88c80", "stroke4": "#e4b7af", - "surface1": "#7b2920", + "surface1": "#7d2b22", "surface2": "#900", - "surface3": "#8c2f25", - "surface4": "#913127", - "surface5": "#97352a", + "surface3": "#8e3127", + "surface4": "#923228", + "surface5": "#9b392d", "surface6": "#a63e32", }, "warnings": undefined, @@ -6623,25 +6424,23 @@ exports[`buildRamps > background ramp snapshots at lightness 30 1`] = ` "ramp": { "bgFill1": "#3e3e00", "bgFill2": "#313000", - "bgFillDark": "#000", "bgFillInverted1": "#1d1c00", "bgFillInverted2": "#000", "fgFill": "#f1f2df", - "fgFillDark": "#f1f2df", "fgFillInverted": "#f1f2df", - "fgSurface1": "#63635b", "fgSurface2": "#494943", - "fgSurface3": "#2e2f2a", - "fgSurface4": "#000", + "fgSurface3": "#2e2e2a", + "fgSurface4": "#22231f", + "fgSurface5": "#000", "stroke1": "#949665", - "stroke2": "#86875b", - "stroke3": "#4a4b30", + "stroke2": "#868758", + "stroke3": "#494a2f", "stroke4": "#2e2e1c", "surface1": "#98994d", "surface2": "#a0a117", "surface3": "#a5a654", - "surface4": "#94954b", - "surface5": "#8e904a", + "surface4": "#95964c", + "surface5": "#8b8d47", "surface6": "#838446", }, "warnings": undefined, @@ -6658,25 +6457,23 @@ exports[`buildRamps > background ramp snapshots at lightness 30 1`] = ` "ramp": { "bgFill1": "#004600", "bgFill2": "#003700", - "bgFillDark": "#000", "bgFillInverted1": "#002100", "bgFillInverted2": "#000", "fgFill": "#ebf3eb", - "fgFillDark": "#ebf3eb", "fgFillInverted": "#ebf3eb", - "fgSurface1": "#5b655a", "fgSurface2": "#444b43", - "fgSurface3": "#2b302a", - "fgSurface4": "#000", + "fgSurface3": "#292e29", + "fgSurface4": "#1f231f", + "fgSurface5": "#000", "stroke1": "#6b9f66", - "stroke2": "#60905c", - "stroke3": "#335031", + "stroke2": "#5d9159", + "stroke3": "#314e2f", "stroke4": "#1e321d", - "surface1": "#55a750", + "surface1": "#53a54e", "surface2": "#33b22e", - "surface3": "#5db557", + "surface3": "#5bb355", "surface4": "#53a24e", - "surface5": "#509c4b", + "surface5": "#4d9948", "surface6": "#4c8f47", }, "warnings": undefined, @@ -6693,25 +6490,23 @@ exports[`buildRamps > background ramp snapshots at lightness 30 1`] = ` "ramp": { "bgFill1": "#004344", "bgFill2": "#003535", - "bgFillDark": "#000", "bgFillInverted1": "#001f1f", "bgFillInverted2": "#000", "fgFill": "#e8f3f2", - "fgFillDark": "#e8f3f2", "fgFillInverted": "#e8f3f2", - "fgSurface1": "#5c6564", "fgSurface2": "#444a4a", - "fgSurface3": "#2b2f2f", - "fgSurface4": "#000", + "fgSurface3": "#2a2e2e", + "fgSurface4": "#212424", + "fgSurface5": "#000", "stroke1": "#6b9b9a", - "stroke2": "#608c8b", - "stroke3": "#334e4d", + "stroke2": "#5e8d8c", + "stroke3": "#314c4b", "stroke4": "#1e3030", - "surface1": "#55a1a1", + "surface1": "#54a0a0", "surface2": "#2dacac", - "surface3": "#5dafaf", - "surface4": "#539d9c", - "surface5": "#519797", + "surface3": "#5caeae", + "surface4": "#549e9d", + "surface5": "#4f9595", "surface6": "#4d8b8a", }, "warnings": undefined, @@ -6728,25 +6523,23 @@ exports[`buildRamps > background ramp snapshots at lightness 30 1`] = ` "ramp": { "bgFill1": "#558dff", "bgFill2": "#6ea0ff", - "bgFillDark": "#000", "bgFillInverted1": "#c4ddff", "bgFillInverted2": "#eff0f2", "fgFill": "#0b1b42", - "fgFillDark": "#eff0f2", "fgFillInverted": "#0b1b42", - "fgSurface1": "#515d76", - "fgSurface2": "#70798a", - "fgSurface3": "#b0b4be", - "fgSurface4": "#eff0f2", + "fgSurface2": "#747d8e", + "fgSurface3": "#959ca8", + "fgSurface4": "#e5e6ea", + "fgSurface5": "#fdfefe", "stroke1": "#152e6a", - "stroke2": "#1c3a81", - "stroke3": "#5777bc", + "stroke2": "#223e7e", + "stroke3": "#5b7bc1", "stroke4": "#7f98cb", - "surface1": "#031b65", + "surface1": "#05206a", "surface2": "#009", - "surface3": "#052481", - "surface4": "#062788", - "surface5": "#082c91", + "surface3": "#082986", + "surface4": "#07298a", + "surface5": "#0b3095", "surface6": "#0e36a4", }, "warnings": undefined, @@ -6763,25 +6556,23 @@ exports[`buildRamps > background ramp snapshots at lightness 30 1`] = ` "ramp": { "bgFill1": "#ff97ff", "bgFill2": "#ffb8ff", - "bgFillDark": "#000", "bgFillInverted1": "#ffdfff", "bgFillInverted2": "#fff", "fgFill": "#221c21", - "fgFillDark": "#f2eff2", "fgFillInverted": "#221c21", - "fgSurface1": "#9c819a", - "fgSurface2": "#b8a4b7", - "fgSurface3": "#d7ccd6", - "fgSurface4": "#fff", + "fgSurface2": "#bca8bb", + "fgSurface3": "#dbd1da", + "fgSurface4": "#ebe6eb", + "fgSurface5": "#fefdfe", "stroke1": "#7f467d", - "stroke2": "#90518e", - "stroke3": "#dc90d9", + "stroke2": "#8f548d", + "stroke3": "#e094dd", "stroke4": "#edc5ea", - "surface1": "#7d2f7b", + "surface1": "#80327e", "surface2": "#970097", - "surface3": "#8c358a", - "surface4": "#90388f", - "surface5": "#973b95", + "surface3": "#8f388d", + "surface4": "#913990", + "surface5": "#9b3f99", "surface6": "#a444a2", }, "warnings": undefined, @@ -6798,25 +6589,23 @@ exports[`buildRamps > background ramp snapshots at lightness 30 1`] = ` "ramp": { "bgFill1": "#fe8173", "bgFill2": "#ff9e91", - "bgFillDark": "#000", "bgFillInverted1": "#ffd0c7", "bgFillInverted2": "#f2efef", "fgFill": "#241c1a", - "fgFillDark": "#f2efef", "fgFillInverted": "#241c1a", - "fgSurface1": "#8c716d", - "fgSurface2": "#a6908d", - "fgSurface3": "#c2b4b2", - "fgSurface4": "#f2efef", + "fgSurface2": "#a8928f", + "fgSurface3": "#c4b7b5", + "fgSurface4": "#ebe7e6", + "fgSurface5": "#fefefd", "stroke1": "#733a34", - "stroke2": "#85443d", - "stroke3": "#d08075", + "stroke2": "#834741", + "stroke3": "#d28277", "stroke4": "#dfada6", - "surface1": "#702620", + "surface1": "#722822", "surface2": "#8a0f0f", - "surface3": "#802c26", + "surface3": "#822e28", "surface4": "#862f28", - "surface5": "#8d332b", + "surface5": "#90352d", "surface6": "#9a3b33", }, "warnings": undefined, @@ -6833,25 +6622,23 @@ exports[`buildRamps > background ramp snapshots at lightness 30 1`] = ` "ramp": { "bgFill1": "#3e3e00", "bgFill2": "#313000", - "bgFillDark": "#000", "bgFillInverted1": "#1d1c00", "bgFillInverted2": "#000", "fgFill": "#f1f2e0", - "fgFillDark": "#f1f2e0", "fgFillInverted": "#f1f2e0", - "fgSurface1": "#63635b", "fgSurface2": "#494943", - "fgSurface3": "#2e2f2b", - "fgSurface4": "#000", + "fgSurface3": "#2e2f2a", + "fgSurface4": "#23231f", + "fgSurface5": "#000", "stroke1": "#949667", - "stroke2": "#86875d", + "stroke2": "#86875a", "stroke3": "#4a4a32", "stroke4": "#2e2e1d", "surface1": "#989952", "surface2": "#a0a032", "surface3": "#a5a659", - "surface4": "#949550", - "surface5": "#8e904e", + "surface4": "#959651", + "surface5": "#8b8d4b", "surface6": "#83844a", }, "warnings": undefined, @@ -6868,25 +6655,23 @@ exports[`buildRamps > background ramp snapshots at lightness 30 1`] = ` "ramp": { "bgFill1": "#004600", "bgFill2": "#003700", - "bgFillDark": "#000", "bgFillInverted1": "#002100", "bgFillInverted2": "#000", "fgFill": "#ecf2eb", - "fgFillDark": "#ecf2eb", "fgFillInverted": "#ecf2eb", - "fgSurface1": "#5b655a", "fgSurface2": "#444a43", - "fgSurface3": "#2b2f2a", - "fgSurface4": "#000", + "fgSurface3": "#2a2e29", + "fgSurface4": "#202420", + "fgSurface5": "#000", "stroke1": "#6d9e69", - "stroke2": "#628f5e", - "stroke3": "#344f32", + "stroke2": "#60905c", + "stroke3": "#334e31", "stroke4": "#1f311d", - "surface1": "#59a654", + "surface1": "#58a553", "surface2": "#45b141", - "surface3": "#61b45c", - "surface4": "#57a152", - "surface5": "#549b50", + "surface3": "#60b35b", + "surface4": "#58a253", + "surface5": "#52994e", "surface6": "#508f4b", }, "warnings": undefined, @@ -6903,25 +6688,23 @@ exports[`buildRamps > background ramp snapshots at lightness 30 1`] = ` "ramp": { "bgFill1": "#004344", "bgFill2": "#003435", - "bgFillDark": "#000", "bgFillInverted1": "#001f1f", "bgFillInverted2": "#000", "fgFill": "#e8f3f2", - "fgFillDark": "#e8f3f2", "fgFillInverted": "#e8f3f2", - "fgSurface1": "#5c6464", "fgSurface2": "#444a4a", "fgSurface3": "#2b2f2f", - "fgSurface4": "#000", + "fgSurface4": "#202323", + "fgSurface5": "#000", "stroke1": "#6d9b9a", - "stroke2": "#628c8b", + "stroke2": "#608d8c", "stroke3": "#354d4d", "stroke4": "#1f3030", "surface1": "#5aa1a0", "surface2": "#41abaa", "surface3": "#62aeae", - "surface4": "#579c9c", - "surface5": "#559796", + "surface4": "#589d9d", + "surface5": "#529493", "surface6": "#508a8a", }, "warnings": undefined, @@ -6938,25 +6721,23 @@ exports[`buildRamps > background ramp snapshots at lightness 30 1`] = ` "ramp": { "bgFill1": "#648bff", "bgFill2": "#799eff", - "bgFillDark": "#000", "bgFillInverted1": "#c8dcff", "bgFillInverted2": "#eff0f2", "fgFill": "#0f1a41", - "fgFillDark": "#eff0f2", "fgFillInverted": "#0f1a41", - "fgSurface1": "#535d74", - "fgSurface2": "#717889", - "fgSurface3": "#b0b4bd", - "fgSurface4": "#eff0f2", + "fgSurface2": "#747b8c", + "fgSurface3": "#9499a6", + "fgSurface4": "#e4e5e8", + "fgSurface5": "#fdfefe", "stroke1": "#1d2d65", - "stroke2": "#26397c", - "stroke3": "#5e76b9", + "stroke2": "#2b3d79", + "stroke3": "#6179bc", "stroke4": "#8497c9", - "surface1": "#0e1b60", + "surface1": "#101e63", "surface2": "#0f0f8a", - "surface3": "#14257a", + "surface3": "#16287d", "surface4": "#172882", - "surface5": "#192c8b", + "surface5": "#1b2f8e", "surface6": "#20369c", }, "warnings": undefined, @@ -6973,25 +6754,23 @@ exports[`buildRamps > background ramp snapshots at lightness 30 1`] = ` "ramp": { "bgFill1": "#fd88fa", "bgFill2": "#ffa9ff", - "bgFillDark": "#000", "bgFillInverted1": "#ffceff", "bgFillInverted2": "#f5f2f5", "fgFill": "#221c21", - "fgFillDark": "#f2eff2", "fgFillInverted": "#221c21", - "fgSurface1": "#937b91", - "fgSurface2": "#b19baf", - "fgSurface3": "#cfc2ce", - "fgSurface4": "#f5f2f5", + "fgSurface2": "#b59fb3", + "fgSurface3": "#d3c7d2", + "fgSurface4": "#ebe6eb", + "fgSurface5": "#fefdfe", "stroke1": "#764274", - "stroke2": "#864c84", - "stroke3": "#d785d4", + "stroke2": "#854f83", + "stroke3": "#dc89d9", "stroke4": "#e8b9e5", - "surface1": "#722c71", + "surface1": "#752f74", "surface2": "#8a0f8a", - "surface3": "#813380", - "surface4": "#863584", - "surface5": "#8c398b", + "surface3": "#843683", + "surface4": "#873685", + "surface5": "#8f3c8e", "surface6": "#994197", }, "warnings": undefined, @@ -7008,25 +6787,23 @@ exports[`buildRamps > background ramp snapshots at lightness 30 1`] = ` "ramp": { "bgFill1": "#eb857e", "bgFill2": "#ff978e", - "bgFillDark": "#000", "bgFillInverted1": "#ffd0c9", "bgFillInverted2": "#f2efef", "fgFill": "#251b1a", - "fgFillDark": "#f2efef", "fgFillInverted": "#251b1a", - "fgSurface1": "#856e6c", - "fgSurface2": "#a28d8a", - "fgSurface3": "#beb1af", - "fgSurface4": "#f2efef", + "fgSurface2": "#a38e8b", + "fgSurface3": "#bfb2b1", + "fgSurface4": "#ebe7e6", + "fgSurface5": "#fefefd", "stroke1": "#6c3936", - "stroke2": "#7d433f", - "stroke3": "#cb7c76", + "stroke2": "#7b4542", + "stroke3": "#cd7e78", "stroke4": "#daa9a4", - "surface1": "#682624", + "surface1": "#692725", "surface2": "#7a1f1f", - "surface3": "#782e2b", - "surface4": "#7d302d", - "surface5": "#833430", + "surface3": "#792f2c", + "surface4": "#7c2f2c", + "surface5": "#863732", "surface6": "#903d38", }, "warnings": undefined, @@ -7043,25 +6820,23 @@ exports[`buildRamps > background ramp snapshots at lightness 30 1`] = ` "ramp": { "bgFill1": "#3f3d00", "bgFill2": "#313000", - "bgFillDark": "#000", "bgFillInverted1": "#1d1c00", "bgFillInverted2": "#000", "fgFill": "#f1f2e1", - "fgFillDark": "#f1f2e1", "fgFillInverted": "#f1f2e1", - "fgSurface1": "#63635c", "fgSurface2": "#494944", "fgSurface3": "#2e2f2b", - "fgSurface4": "#000", + "fgSurface4": "#232320", + "fgSurface5": "#000", "stroke1": "#94956b", - "stroke2": "#868760", + "stroke2": "#86875e", "stroke3": "#4a4a34", "stroke4": "#2e2e1f", "surface1": "#989958", "surface2": "#9fa049", "surface3": "#a4a660", - "surface4": "#939556", - "surface5": "#8e8f53", + "surface4": "#949657", + "surface5": "#8c8d51", "surface6": "#83844f", }, "warnings": undefined, @@ -7078,25 +6853,23 @@ exports[`buildRamps > background ramp snapshots at lightness 30 1`] = ` "ramp": { "bgFill1": "#7dcf77", "bgFill2": "#90e389", - "bgFillDark": "#000", "bgFillInverted1": "#a9fda2", "bgFillInverted2": "#fff", "fgFill": "#1b1f1b", - "fgFillDark": "#ebf3ea", "fgFillInverted": "#1b1f1b", - "fgSurface1": "#808d7f", "fgSurface2": "#9fae9d", - "fgSurface3": "#c2d4c0", - "fgSurface4": "#fff", + "fgSurface3": "#c1d4bf", + "fgSurface4": "#dfebde", + "fgSurface5": "#fcfefc", "stroke1": "#3f643c", - "stroke2": "#487144", + "stroke2": "#4b7147", "stroke3": "#79b973", "stroke4": "#96e48f", - "surface1": "#275924", + "surface1": "#265823", "surface2": "#006200", - "surface3": "#2d6529", - "surface4": "#2f682c", - "surface5": "#326d2f", + "surface3": "#2c6428", + "surface4": "#2d662b", + "surface5": "#346f31", "surface6": "#3b7837", }, "warnings": undefined, @@ -7113,25 +6886,23 @@ exports[`buildRamps > background ramp snapshots at lightness 30 1`] = ` "ramp": { "bgFill1": "#7fcac9", "bgFill2": "#92dddc", - "bgFillDark": "#000", "bgFillInverted1": "#aaf6f5", "bgFillInverted2": "#fff", "fgFill": "#1c1f1f", - "fgFillDark": "#e8f3f3", "fgFillInverted": "#1c1f1f", - "fgSurface1": "#818c8c", "fgSurface2": "#9fadac", - "fgSurface3": "#c2d3d2", - "fgSurface4": "#fff", + "fgSurface3": "#c1d1d1", + "fgSurface4": "#daebeb", + "fgSurface5": "#fafefe", "stroke1": "#406161", - "stroke2": "#496e6e", + "stroke2": "#4c6e6e", "stroke3": "#7ab4b4", "stroke4": "#98dddd", - "surface1": "#285756", + "surface1": "#275655", "surface2": "#005e5f", - "surface3": "#2e6161", - "surface4": "#306564", - "surface5": "#336969", + "surface3": "#2d6060", + "surface4": "#2e6362", + "surface5": "#366c6c", "surface6": "#3c7474", }, "warnings": undefined, @@ -7148,25 +6919,23 @@ exports[`buildRamps > background ramp snapshots at lightness 30 1`] = ` "ramp": { "bgFill1": "#798cf0", "bgFill2": "#899dff", - "bgFillDark": "#000", "bgFillInverted1": "#cddaff", "bgFillInverted2": "#eff0f2", "fgFill": "#151b3a", - "fgFillDark": "#eff0f2", "fgFillInverted": "#151b3a", - "fgSurface1": "#5a6074", - "fgSurface2": "#777b8b", - "fgSurface3": "#b1b4bc", - "fgSurface4": "#eff0f2", + "fgSurface2": "#787c8c", + "fgSurface3": "#989ba7", + "fgSurface4": "#e4e5e8", + "fgSurface5": "#fdfdfe", "stroke1": "#283064", - "stroke2": "#323c79", - "stroke3": "#6b78b5", + "stroke2": "#353f76", + "stroke3": "#6d7ab7", "stroke4": "#8f9ac7", - "surface1": "#1b2061", + "surface1": "#1c2162", "surface2": "#1f1f7a", - "surface3": "#232977", + "surface3": "#242a78", "surface4": "#262c7d", - "surface5": "#2a3185", + "surface5": "#2c3387", "surface6": "#313a95", }, "warnings": undefined, @@ -7183,25 +6952,23 @@ exports[`buildRamps > background ramp snapshots at lightness 30 1`] = ` "ramp": { "bgFill1": "#ea8ae7", "bgFill2": "#fd9cfb", - "bgFillDark": "#000", "bgFillInverted1": "#ffc9ff", "bgFillInverted2": "#f2eff2", "fgFill": "#211c21", - "fgFillDark": "#f2eff2", "fgFillInverted": "#211c21", - "fgSurface1": "#8b7689", - "fgSurface2": "#a895a7", - "fgSurface3": "#c7bac6", - "fgSurface4": "#f2eff2", + "fgSurface2": "#aa97a9", + "fgSurface3": "#cabcc9", + "fgSurface4": "#ebe6eb", + "fgSurface5": "#fefdfe", "stroke1": "#6d3f6b", - "stroke2": "#7d497b", - "stroke3": "#ce7ecc", + "stroke2": "#7c4c7a", + "stroke3": "#d181cf", "stroke4": "#e2afdf", - "surface1": "#682b67", + "surface1": "#6a2d69", "surface2": "#7a1f7a", - "surface3": "#763275", + "surface3": "#783477", "surface4": "#7b347a", - "surface5": "#813880", + "surface5": "#843b83", "surface6": "#8e418d", }, "warnings": undefined, @@ -7218,25 +6985,23 @@ exports[`buildRamps > background ramp snapshots at lightness 30 1`] = ` "ramp": { "bgFill1": "#d7908d", "bgFill2": "#eaa19e", - "bgFillDark": "#000", "bgFillInverted1": "#ffcfcc", "bgFillInverted2": "#f2efef", "fgFill": "#241c1b", - "fgFillDark": "#f2efef", "fgFillInverted": "#241c1b", - "fgSurface1": "#82706f", "fgSurface2": "#9f8e8c", "fgSurface3": "#bcb1b0", - "fgSurface4": "#f2efef", + "fgSurface4": "#eae7e7", + "fgSurface5": "#fefefd", "stroke1": "#673c3b", - "stroke2": "#774745", + "stroke2": "#754846", "stroke3": "#c3807d", "stroke4": "#d5aba8", "surface1": "#622b2b", "surface2": "#6b2e2e", "surface3": "#703333", - "surface4": "#753636", - "surface5": "#7b3a39", + "surface4": "#743535", + "surface5": "#7e3c3b", "surface6": "#884342", }, "warnings": undefined, @@ -7253,25 +7018,23 @@ exports[`buildRamps > background ramp snapshots at lightness 30 1`] = ` "ramp": { "bgFill1": "#bfc181", "bgFill2": "#d3d494", - "bgFillDark": "#000", "bgFillInverted1": "#ebedac", "bgFillInverted2": "#fff", "fgFill": "#1e1e1b", - "fgFillDark": "#f1f2e0", "fgFillInverted": "#1e1e1b", - "fgSurface1": "#8a8a7f", "fgSurface2": "#aaab9e", - "fgSurface3": "#d0d0c1", - "fgSurface4": "#fff", + "fgSurface3": "#cfd0c1", + "fgSurface4": "#e8e9d8", + "fgSurface5": "#fefff2", "stroke1": "#5c5d3f", - "stroke2": "#696948", + "stroke2": "#68694a", "stroke3": "#acad79", "stroke4": "#d4d595", "surface1": "#515129", "surface2": "#575717", "surface3": "#5b5b2e", - "surface4": "#5f5f31", - "surface5": "#636334", + "surface4": "#5e5e30", + "surface5": "#666637", "surface6": "#6d6e3c", }, "warnings": undefined, @@ -7288,25 +7051,23 @@ exports[`buildRamps > background ramp snapshots at lightness 30 1`] = ` "ramp": { "bgFill1": "#8ccc8a", "bgFill2": "#9fe09d", - "bgFillDark": "#000", "bgFillInverted1": "#b7f9b4", "bgFillInverted2": "#fff", "fgFill": "#1c1f1c", - "fgFillDark": "#ecf2eb", "fgFillInverted": "#1c1f1c", - "fgSurface1": "#818d80", "fgSurface2": "#a0ae9f", - "fgSurface3": "#c3d4c2", - "fgSurface4": "#fff", + "fgSurface3": "#c2d3c1", + "fgSurface4": "#e0eadf", + "fgSurface5": "#fcfefc", "stroke1": "#426341", - "stroke2": "#4b704a", + "stroke2": "#4e704d", "stroke3": "#7eb87c", "stroke4": "#9ce29a", - "surface1": "#2c592c", + "surface1": "#2b582b", "surface2": "#226023", - "surface3": "#326432", - "surface4": "#356734", - "surface5": "#386c37", + "surface3": "#316331", + "surface4": "#346633", + "surface5": "#3a6f39", "surface6": "#417740", }, "warnings": undefined, @@ -7323,25 +7084,23 @@ exports[`buildRamps > background ramp snapshots at lightness 30 1`] = ` "ramp": { "bgFill1": "#8ac8c7", "bgFill2": "#9ddbdb", - "bgFillDark": "#000", "bgFillInverted1": "#b5f4f3", "bgFillInverted2": "#fff", "fgFill": "#1c1f1f", - "fgFillDark": "#e8f3f2", "fgFillInverted": "#1c1f1f", - "fgSurface1": "#828c8c", "fgSurface2": "#a0acac", - "fgSurface3": "#c4d2d2", - "fgSurface4": "#fff", + "fgSurface3": "#c3d2d2", + "fgSurface4": "#dbebea", + "fgSurface5": "#fbfefe", "stroke1": "#426060", - "stroke2": "#4c6d6d", + "stroke2": "#4e6d6d", "stroke3": "#7fb3b3", "stroke4": "#9ddddc", "surface1": "#2c5655", "surface2": "#1e5d5d", "surface3": "#336060", - "surface4": "#356464", - "surface5": "#386968", + "surface4": "#346363", + "surface5": "#3a6b6a", "surface6": "#417373", }, "warnings": undefined, @@ -7358,25 +7117,23 @@ exports[`buildRamps > background ramp snapshots at lightness 30 1`] = ` "ramp": { "bgFill1": "#888ed5", "bgFill2": "#989fe6", - "bgFillDark": "#000", "bgFillInverted1": "#d2d9ff", "bgFillInverted2": "#f0f0f1", "fgFill": "#1a1c2f", - "fgFillDark": "#f0f0f1", "fgFillInverted": "#1a1c2f", - "fgSurface1": "#656778", "fgSurface2": "#82838f", - "fgSurface3": "#b3b4bb", - "fgSurface4": "#f0f0f1", + "fgSurface3": "#a2a3ac", + "fgSurface4": "#e7e7ea", + "fgSurface5": "#fdfdfe", "stroke1": "#363865", - "stroke2": "#414478", + "stroke2": "#424675", "stroke3": "#7b81b1", "stroke4": "#9fa3c6", "surface1": "#292962", "surface2": "#2e2e6b", "surface3": "#323274", - "surface4": "#36367b", - "surface5": "#393a81", + "surface4": "#343479", + "surface5": "#3b3c83", "surface6": "#42438f", }, "warnings": undefined, @@ -7393,25 +7150,23 @@ exports[`buildRamps > background ramp snapshots at lightness 30 1`] = ` "ramp": { "bgFill1": "#d792d5", "bgFill2": "#e9a4e8", - "bgFillDark": "#000", "bgFillInverted1": "#ffc9ff", "bgFillInverted2": "#f2eff2", "fgFill": "#211c21", - "fgFillDark": "#f2eff2", "fgFillInverted": "#211c21", - "fgSurface1": "#857584", "fgSurface2": "#a493a3", - "fgSurface3": "#c3b7c3", - "fgSurface4": "#f2eff2", + "fgSurface3": "#c4b7c3", + "fgSurface4": "#ebe6ea", + "fgSurface5": "#fefdfe", "stroke1": "#664066", - "stroke2": "#764a75", + "stroke2": "#754c74", "stroke3": "#c581c3", "stroke4": "#dcaeda", "surface1": "#612e60", "surface2": "#6b2e6b", "surface3": "#6e366e", - "surface4": "#733872", - "surface5": "#783c78", + "surface4": "#723771", + "surface5": "#7b3e7b", "surface6": "#844584", }, "warnings": undefined, @@ -7428,25 +7183,23 @@ exports[`buildRamps > background ramp snapshots at lightness 30 1`] = ` "ramp": { "bgFill1": "#c49f9f", "bgFill2": "#d6b1b0", - "bgFillDark": "#000", "bgFillInverted1": "#f8d2d1", "bgFillInverted2": "#f1f0f0", "fgFill": "#221d1c", - "fgFillDark": "#f1f0f0", "fgFillInverted": "#221d1c", - "fgSurface1": "#827675", "fgSurface2": "#a09494", - "fgSurface3": "#bfb7b7", - "fgSurface4": "#f1f0f0", + "fgSurface3": "#beb6b6", + "fgSurface4": "#e9e7e7", + "fgSurface5": "#fefdfd", "stroke1": "#634444", - "stroke2": "#724f4f", + "stroke2": "#715050", "stroke3": "#b98c8b", "stroke4": "#d0b4b4", - "surface1": "#5d3636", + "surface1": "#5c3535", "surface2": "#5c3d3d", - "surface3": "#6a3e3f", - "surface4": "#6e4142", - "surface5": "#744545", + "surface3": "#693d3e", + "surface4": "#6c4041", + "surface5": "#764747", "surface6": "#7f4e4e", }, "warnings": undefined, @@ -7463,25 +7216,23 @@ exports[`buildRamps > background ramp snapshots at lightness 30 1`] = ` "ramp": { "bgFill1": "#bebf9d", "bgFill2": "#d2d2b0", - "bgFillDark": "#000", "bgFillInverted1": "#eaebc8", "bgFillInverted2": "#fff", "fgFill": "#1e1e1c", - "fgFillDark": "#f1f1e4", "fgFillInverted": "#1e1e1c", - "fgSurface1": "#8a8a82", "fgSurface2": "#aaaba1", - "fgSurface3": "#d0d0c5", - "fgSurface4": "#fff", + "fgSurface3": "#d0d1c5", + "fgSurface4": "#e8e9dc", + "fgSurface5": "#fefff6", "stroke1": "#5c5c46", - "stroke2": "#696950", - "stroke3": "#acac86", + "stroke2": "#696952", + "stroke3": "#adad87", "stroke4": "#d4d4a5", "surface1": "#515134", "surface2": "#565637", "surface3": "#5b5b3b", "surface4": "#5e5e3d", - "surface5": "#636341", + "surface5": "#656543", "surface6": "#6d6d49", }, "warnings": undefined, @@ -7498,25 +7249,23 @@ exports[`buildRamps > background ramp snapshots at lightness 30 1`] = ` "ramp": { "bgFill1": "#a4c6a3", "bgFill2": "#b6d9b6", - "bgFillDark": "#000", "bgFillInverted1": "#cef2ce", "bgFillInverted2": "#fff", "fgFill": "#1c1f1c", - "fgFillDark": "#edf2ed", "fgFillInverted": "#1c1f1c", - "fgSurface1": "#848c83", - "fgSurface2": "#a3ada2", - "fgSurface3": "#c7d3c6", - "fgSurface4": "#fff", + "fgSurface2": "#a4aea3", + "fgSurface3": "#c7d4c7", + "fgSurface4": "#e2eae2", + "fgSurface5": "#fcfefc", "stroke1": "#496149", - "stroke2": "#536f53", - "stroke3": "#8ab58a", + "stroke2": "#566f56", + "stroke3": "#8bb68b", "stroke4": "#abdfaa", "surface1": "#375737", "surface2": "#3d5c3d", "surface3": "#3e623e", "surface4": "#416541", - "surface5": "#446a44", + "surface5": "#476d47", "surface6": "#4d754d", }, "warnings": undefined, @@ -7533,25 +7282,23 @@ exports[`buildRamps > background ramp snapshots at lightness 30 1`] = ` "ramp": { "bgFill1": "#a1c3c3", "bgFill2": "#b4d6d6", - "bgFillDark": "#000", "bgFillInverted1": "#cdefef", "bgFillInverted2": "#fff", "fgFill": "#1c1f1f", - "fgFillDark": "#eaf2f2", "fgFillInverted": "#1c1f1f", - "fgSurface1": "#838b8b", "fgSurface2": "#a2acab", "fgSurface3": "#c6d1d1", - "fgSurface4": "#fff", + "fgSurface4": "#dfeaea", + "fgSurface5": "#fbfefe", "stroke1": "#495f5f", - "stroke2": "#536c6c", + "stroke2": "#556c6c", "stroke3": "#8ab1b1", "stroke4": "#abd9d9", "surface1": "#365454", "surface2": "#3b5959", "surface3": "#3e5e5e", - "surface4": "#406262", - "surface5": "#446767", + "surface4": "#3f6161", + "surface5": "#466969", "surface6": "#4d7171", }, "warnings": undefined, @@ -7568,25 +7315,23 @@ exports[`buildRamps > background ramp snapshots at lightness 30 1`] = ` "ramp": { "bgFill1": "#9d9ec2", "bgFill2": "#aeb0d4", - "bgFillDark": "#000", "bgFillInverted1": "#d6d8fe", "bgFillInverted2": "#f0f0f1", "fgFill": "#1d1d25", - "fgFillDark": "#f0f0f1", "fgFillInverted": "#1d1d25", - "fgSurface1": "#71727d", "fgSurface2": "#8f9097", - "fgSurface3": "#b3b4b9", - "fgSurface4": "#f0f0f1", + "fgSurface3": "#b0b1b6", + "fgSurface4": "#e7e7e9", + "fgSurface5": "#fdfdfe", "stroke1": "#446", - "stroke2": "#4f4f76", + "stroke2": "#505074", "stroke3": "#8d8eac", "stroke4": "#b1b2c6", - "surface1": "#383762", + "surface1": "#373661", "surface2": "#3d3d5c", - "surface3": "#414070", - "surface4": "#454375", - "surface5": "#49477b", + "surface3": "#403f6f", + "surface4": "#434173", + "surface5": "#4b497d", "surface6": "#525087", }, "warnings": undefined, @@ -7603,25 +7348,23 @@ exports[`buildRamps > background ramp snapshots at lightness 30 1`] = ` "ramp": { "bgFill1": "#c4a0c3", "bgFill2": "#d6b2d5", - "bgFillDark": "#000", "bgFillInverted1": "#f4cff3", "bgFillInverted2": "#f1eff1", "fgFill": "#211d20", - "fgFillDark": "#f1eff1", "fgFillInverted": "#211d20", - "fgSurface1": "#837883", "fgSurface2": "#a296a2", - "fgSurface3": "#c3bac2", - "fgSurface4": "#f1eff1", + "fgSurface3": "#c2bac2", + "fgSurface4": "#eae7ea", + "fgSurface5": "#fefefe", "stroke1": "#614661", - "stroke2": "#70506f", + "stroke2": "#6f526e", "stroke3": "#ba8aba", "stroke4": "#d5b4d4", "surface1": "#5a365a", "surface2": "#5c3d5c", "surface3": "#663e67", - "surface4": "#6b416b", - "surface5": "#704570", + "surface4": "#69406a", + "surface5": "#724772", "surface6": "#7b4e7b", }, "warnings": undefined, @@ -7638,25 +7381,23 @@ exports[`buildRamps > background ramp snapshots at lightness 30 1`] = ` "ramp": { "bgFill1": "#b2b2b2", "bgFill2": "#c4c4c4", - "bgFillDark": "#000", "bgFillInverted1": "#dcdcdc", "bgFillInverted2": "#f1f1f1", "fgFill": "#1e1e1e", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#1e1e1e", - "fgSurface1": "#818181", - "fgSurface2": "#a0a0a0", - "fgSurface3": "#c4c4c4", - "fgSurface4": "#f1f1f1", + "fgSurface2": "#a1a1a1", + "fgSurface3": "#c5c5c5", + "fgSurface4": "#e8e8e8", + "fgSurface5": "#fefefe", "stroke1": "#545454", "stroke2": "#606060", - "stroke3": "#a0a0a0", + "stroke3": "#a1a1a1", "stroke4": "#c6c6c6", - "surface1": "#484848", + "surface1": "#494949", "surface2": "#4d4d4d", - "surface3": "#515151", - "surface4": "#555", - "surface5": "#595959", + "surface3": "#525252", + "surface4": "#545454", + "surface5": "#5c5c5c", "surface6": "#636363", }, "warnings": undefined, @@ -7673,25 +7414,23 @@ exports[`buildRamps > background ramp snapshots at lightness 30 1`] = ` "ramp": { "bgFill1": "#b2b2b2", "bgFill2": "#c4c4c4", - "bgFillDark": "#000", "bgFillInverted1": "#dcdcdc", "bgFillInverted2": "#f1f1f1", "fgFill": "#1e1e1e", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#1e1e1e", - "fgSurface1": "#818181", - "fgSurface2": "#a0a0a0", - "fgSurface3": "#c4c4c4", - "fgSurface4": "#f1f1f1", + "fgSurface2": "#a1a1a1", + "fgSurface3": "#c5c5c5", + "fgSurface4": "#e8e8e8", + "fgSurface5": "#fefefe", "stroke1": "#545454", "stroke2": "#606060", - "stroke3": "#a0a0a0", + "stroke3": "#a1a1a1", "stroke4": "#c6c6c6", - "surface1": "#484848", + "surface1": "#494949", "surface2": "#4d4d4d", - "surface3": "#515151", - "surface4": "#555", - "surface5": "#595959", + "surface3": "#525252", + "surface4": "#545454", + "surface5": "#5c5c5c", "surface6": "#636363", }, "warnings": undefined, @@ -7708,25 +7447,23 @@ exports[`buildRamps > background ramp snapshots at lightness 30 1`] = ` "ramp": { "bgFill1": "#b2b2b2", "bgFill2": "#c4c4c4", - "bgFillDark": "#000", "bgFillInverted1": "#dcdcdc", "bgFillInverted2": "#f1f1f1", "fgFill": "#1e1e1e", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#1e1e1e", - "fgSurface1": "#818181", - "fgSurface2": "#a0a0a0", - "fgSurface3": "#c4c4c4", - "fgSurface4": "#f1f1f1", + "fgSurface2": "#a1a1a1", + "fgSurface3": "#c5c5c5", + "fgSurface4": "#e8e8e8", + "fgSurface5": "#fefefe", "stroke1": "#545454", "stroke2": "#606060", - "stroke3": "#a0a0a0", + "stroke3": "#a1a1a1", "stroke4": "#c6c6c6", - "surface1": "#484848", + "surface1": "#494949", "surface2": "#4d4d4d", - "surface3": "#515151", - "surface4": "#555", - "surface5": "#595959", + "surface3": "#525252", + "surface4": "#545454", + "surface5": "#5c5c5c", "surface6": "#636363", }, "warnings": undefined, @@ -7743,25 +7480,23 @@ exports[`buildRamps > background ramp snapshots at lightness 30 1`] = ` "ramp": { "bgFill1": "#b2b2b2", "bgFill2": "#c4c4c4", - "bgFillDark": "#000", "bgFillInverted1": "#dcdcdc", "bgFillInverted2": "#f1f1f1", "fgFill": "#1e1e1e", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#1e1e1e", - "fgSurface1": "#818181", - "fgSurface2": "#a0a0a0", - "fgSurface3": "#c4c4c4", - "fgSurface4": "#f1f1f1", + "fgSurface2": "#a1a1a1", + "fgSurface3": "#c5c5c5", + "fgSurface4": "#e8e8e8", + "fgSurface5": "#fefefe", "stroke1": "#545454", "stroke2": "#606060", - "stroke3": "#a0a0a0", + "stroke3": "#a1a1a1", "stroke4": "#c6c6c6", - "surface1": "#484848", + "surface1": "#494949", "surface2": "#4d4d4d", - "surface3": "#515151", - "surface4": "#555", - "surface5": "#595959", + "surface3": "#525252", + "surface4": "#545454", + "surface5": "#5c5c5c", "surface6": "#636363", }, "warnings": undefined, @@ -7778,25 +7513,23 @@ exports[`buildRamps > background ramp snapshots at lightness 30 1`] = ` "ramp": { "bgFill1": "#b2b2b2", "bgFill2": "#c4c4c4", - "bgFillDark": "#000", "bgFillInverted1": "#dcdcdc", "bgFillInverted2": "#f1f1f1", "fgFill": "#1e1e1e", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#1e1e1e", - "fgSurface1": "#818181", - "fgSurface2": "#a0a0a0", - "fgSurface3": "#c4c4c4", - "fgSurface4": "#f1f1f1", + "fgSurface2": "#a1a1a1", + "fgSurface3": "#c5c5c5", + "fgSurface4": "#e8e8e8", + "fgSurface5": "#fefefe", "stroke1": "#545454", "stroke2": "#606060", - "stroke3": "#a0a0a0", + "stroke3": "#a1a1a1", "stroke4": "#c6c6c6", - "surface1": "#484848", + "surface1": "#494949", "surface2": "#4d4d4d", - "surface3": "#515151", - "surface4": "#555", - "surface5": "#595959", + "surface3": "#525252", + "surface4": "#545454", + "surface5": "#5c5c5c", "surface6": "#636363", }, "warnings": undefined, @@ -7813,25 +7546,23 @@ exports[`buildRamps > background ramp snapshots at lightness 30 1`] = ` "ramp": { "bgFill1": "#b2b2b2", "bgFill2": "#c4c4c4", - "bgFillDark": "#000", "bgFillInverted1": "#dcdcdc", "bgFillInverted2": "#f1f1f1", "fgFill": "#1e1e1e", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#1e1e1e", - "fgSurface1": "#818181", - "fgSurface2": "#a0a0a0", - "fgSurface3": "#c4c4c4", - "fgSurface4": "#f1f1f1", + "fgSurface2": "#a1a1a1", + "fgSurface3": "#c5c5c5", + "fgSurface4": "#e8e8e8", + "fgSurface5": "#fefefe", "stroke1": "#545454", "stroke2": "#606060", - "stroke3": "#a0a0a0", + "stroke3": "#a1a1a1", "stroke4": "#c6c6c6", - "surface1": "#484848", + "surface1": "#494949", "surface2": "#4d4d4d", - "surface3": "#515151", - "surface4": "#555", - "surface5": "#595959", + "surface3": "#525252", + "surface4": "#545454", + "surface5": "#5c5c5c", "surface6": "#636363", }, "warnings": undefined, @@ -7853,25 +7584,23 @@ exports[`buildRamps > background ramp snapshots at lightness 40 1`] = ` "ramp": { "bgFill1": "#ffa697", "bgFill2": "#ffc2b6", - "bgFillDark": "#000", "bgFillInverted1": "#ffe3dc", "bgFillInverted2": "#fff", "fgFill": "#241c1b", - "fgFillDark": "#f2efef", "fgFillInverted": "#241c1b", - "fgSurface1": "#a1837d", - "fgSurface2": "#b7a6a3", - "fgSurface3": "#d6cdcc", - "fgSurface4": "#fff", + "fgSurface2": "#baa9a6", + "fgSurface3": "#d9d1cf", + "fgSurface4": "#ebe7e6", + "fgSurface5": "#fefefd", "stroke1": "#8c473d", - "stroke2": "#9f5146", - "stroke3": "#da9a90", + "stroke2": "#9d554a", + "stroke3": "#de9d93", "stroke4": "#ebcac4", - "surface1": "#8d3026", + "surface1": "#8f3228", "surface2": "#ad0000", - "surface3": "#9d362b", - "surface4": "#a2382d", - "surface5": "#a93c30", + "surface3": "#9f382d", + "surface4": "#a3392e", + "surface5": "#ad3f33", "surface6": "#b74538", }, "warnings": undefined, @@ -7888,25 +7617,23 @@ exports[`buildRamps > background ramp snapshots at lightness 40 1`] = ` "ramp": { "bgFill1": "#5a5a00", "bgFill2": "#4d4d00", - "bgFillDark": "#1e1e1b", "bgFillInverted1": "#302f00", "bgFillInverted2": "#1e1e1b", "fgFill": "#f2f4cf", - "fgFillDark": "#f2f4cf", "fgFillInverted": "#f2f4cf", - "fgSurface1": "#84857a", "fgSurface2": "#68685f", - "fgSurface3": "#4d4e47", - "fgSurface4": "#1e1e1b", + "fgSurface3": "#4d4d46", + "fgSurface4": "#44443e", + "fgSurface5": "#000", "stroke1": "#bdbf81", - "stroke2": "#acae75", - "stroke3": "#696a45", + "stroke2": "#acae72", + "stroke3": "#686944", "stroke4": "#4d4e32", - "surface1": "#c2c362", + "surface1": "#c1c261", "surface2": "#cc0", - "surface3": "#d1d36a", - "surface4": "#bdbe60", - "surface5": "#b6b85e", + "surface3": "#d0d269", + "surface4": "#bebf61", + "surface5": "#b3b55b", "surface6": "#a9aa5c", }, "warnings": undefined, @@ -7923,25 +7650,23 @@ exports[`buildRamps > background ramp snapshots at lightness 40 1`] = ` "ramp": { "bgFill1": "#005800", "bgFill2": "#004900", - "bgFillDark": "#1b1f1b", "bgFillInverted1": "#003700", "bgFillInverted2": "#1b1f1b", "fgFill": "#ebf3ea", - "fgFillDark": "#ebf3ea", "fgFillInverted": "#ebf3ea", - "fgSurface1": "#6a7669", "fgSurface2": "#525a51", - "fgSurface3": "#3a4039", - "fgSurface4": "#1b1f1b", + "fgSurface3": "#383f38", + "fgSurface4": "#30352f", + "fgSurface5": "#000", "stroke1": "#76b571", - "stroke2": "#6ba566", - "stroke3": "#3d613a", + "stroke2": "#68a563", + "stroke3": "#3b5f38", "stroke4": "#294427", - "surface1": "#5bbe55", + "surface1": "#59bc53", "surface2": "#0c0", - "surface3": "#63cd5d", + "surface3": "#61cb5b", "surface4": "#59b953", - "surface5": "#57b251", + "surface5": "#54af4e", "surface6": "#53a44e", }, "warnings": undefined, @@ -7958,25 +7683,23 @@ exports[`buildRamps > background ramp snapshots at lightness 40 1`] = ` "ramp": { "bgFill1": "#005a5b", "bgFill2": "#004c4d", - "bgFillDark": "#1b1f1f", "bgFillInverted1": "#003434", "bgFillInverted2": "#1b1f1f", "fgFill": "#e7f3f3", - "fgFillDark": "#e7f3f3", "fgFillInverted": "#e7f3f3", - "fgSurface1": "#717b7a", "fgSurface2": "#575f5f", - "fgSurface3": "#3f4545", - "fgSurface4": "#1b1f1f", + "fgSurface3": "#3e4444", + "fgSurface4": "#363b3b", + "fgSurface5": "#000", "stroke1": "#7db8b7", - "stroke2": "#71a7a6", - "stroke3": "#416363", + "stroke2": "#6ea8a7", + "stroke3": "#406262", "stroke4": "#2d4747", - "surface1": "#61bfbe", + "surface1": "#60bebd", "surface2": "#0cc", - "surface3": "#69cecd", - "surface4": "#5fbab9", - "surface5": "#5db3b3", + "surface3": "#68cdcc", + "surface4": "#60bbba", + "surface5": "#5ab1b1", "surface6": "#59a6a5", }, "warnings": undefined, @@ -7993,25 +7716,23 @@ exports[`buildRamps > background ramp snapshots at lightness 40 1`] = ` "ramp": { "bgFill1": "#6499ff", "bgFill2": "#7eacff", - "bgFillDark": "#000", "bgFillInverted1": "#c4ddff", "bgFillInverted2": "#eff0f2", "fgFill": "#161e2e", - "fgFillDark": "#eff0f2", "fgFillInverted": "#161e2e", - "fgSurface1": "#606e89", - "fgSurface2": "#838b9a", - "fgSurface3": "#b0b4be", - "fgSurface4": "#eff0f2", + "fgSurface2": "#8991a0", + "fgSurface3": "#adb2bc", + "fgSurface4": "#e6e8eb", + "fgSurface5": "#fdfefe", "stroke1": "#1b3d8f", - "stroke2": "#25499f", - "stroke3": "#6e8ac5", + "stroke2": "#264a9d", + "stroke3": "#7490cc", "stroke4": "#99add6", - "surface1": "#072a8e", + "surface1": "#0b3094", "surface2": "#00c", - "surface3": "#0932a7", - "surface4": "#0b35ae", - "surface5": "#0d39b7", + "surface3": "#0e38ad", + "surface4": "#0d38b1", + "surface5": "#113ebc", "surface6": "#1444c5", }, "warnings": undefined, @@ -8028,25 +7749,23 @@ exports[`buildRamps > background ramp snapshots at lightness 40 1`] = ` "ramp": { "bgFill1": "#ff97ff", "bgFill2": "#ffb8ff", - "bgFillDark": "#000", "bgFillInverted1": "#ffdfff", "bgFillInverted2": "#fff", "fgFill": "#221c21", - "fgFillDark": "#f2eff2", "fgFillInverted": "#221c21", - "fgSurface1": "#9c819a", - "fgSurface2": "#b8a4b7", - "fgSurface3": "#d7ccd6", - "fgSurface4": "#fff", + "fgSurface2": "#bca8bb", + "fgSurface3": "#dbd1da", + "fgSurface4": "#ebe6eb", + "fgSurface5": "#fefdfe", "stroke1": "#7f477e", - "stroke2": "#90518e", - "stroke3": "#dc90d9", + "stroke2": "#8f548d", + "stroke3": "#e094dd", "stroke4": "#ecc5ea", - "surface1": "#7c2f7c", + "surface1": "#80327f", "surface2": "#970098", - "surface3": "#8b358a", - "surface4": "#90388f", - "surface5": "#963b95", + "surface3": "#8f388d", + "surface4": "#913990", + "surface5": "#9a3f99", "surface6": "#a444a2", }, "warnings": undefined, @@ -8063,25 +7782,23 @@ exports[`buildRamps > background ramp snapshots at lightness 40 1`] = ` "ramp": { "bgFill1": "#ffa698", "bgFill2": "#ffc2b6", - "bgFillDark": "#000", "bgFillInverted1": "#ffe3dc", "bgFillInverted2": "#fff", "fgFill": "#241c1b", - "fgFillDark": "#f2efef", "fgFillInverted": "#241c1b", - "fgSurface1": "#a1827e", - "fgSurface2": "#b7a6a3", - "fgSurface3": "#d6cdcc", - "fgSurface4": "#fff", + "fgSurface2": "#bbaaa7", + "fgSurface3": "#dad2d1", + "fgSurface4": "#ebe7e6", + "fgSurface5": "#fefefd", "stroke1": "#8c473e", - "stroke2": "#9e5147", - "stroke3": "#da9a91", + "stroke2": "#9c544b", + "stroke3": "#df9e95", "stroke4": "#ebcac4", - "surface1": "#8c2f27", + "surface1": "#8f3229", "surface2": "#ad0005", - "surface3": "#9d362c", - "surface4": "#a2382e", - "surface5": "#a93c32", + "surface3": "#a0392e", + "surface4": "#a3392f", + "surface5": "#ad3f35", "surface6": "#b7453a", }, "warnings": undefined, @@ -8098,25 +7815,23 @@ exports[`buildRamps > background ramp snapshots at lightness 40 1`] = ` "ramp": { "bgFill1": "#504f00", "bgFill2": "#424100", - "bgFillDark": "#1e1e1b", "bgFillInverted1": "#302f00", "bgFillInverted2": "#1e1e1b", "fgFill": "#f1f2df", - "fgFillDark": "#f1f2df", "fgFillInverted": "#f1f2df", - "fgSurface1": "#75756c", "fgSurface2": "#5a5a53", "fgSurface3": "#40403b", - "fgSurface4": "#1e1e1b", + "fgSurface4": "#363732", + "fgSurface5": "#000", "stroke1": "#aaac74", - "stroke2": "#9a9c69", - "stroke3": "#5b5c3c", + "stroke2": "#9a9c66", + "stroke3": "#5a5b3b", "stroke4": "#404029", "surface1": "#aeb059", "surface2": "#b8b814", "surface3": "#bcbe60", - "surface4": "#a9ab57", - "surface5": "#a4a555", + "surface4": "#abad59", + "surface5": "#a2a353", "surface6": "#979952", }, "warnings": undefined, @@ -8133,25 +7848,23 @@ exports[`buildRamps > background ramp snapshots at lightness 40 1`] = ` "ramp": { "bgFill1": "#004900", "bgFill2": "#003a00", - "bgFillDark": "#050605", "bgFillInverted1": "#002500", "bgFillInverted2": "#050605", "fgFill": "#ebf3ea", - "fgFillDark": "#ebf3ea", "fgFillInverted": "#ebf3ea", - "fgSurface1": "#5d685c", "fgSurface2": "#464d45", - "fgSurface3": "#2d332d", - "fgSurface4": "#050605", + "fgSurface3": "#2b312b", + "fgSurface4": "#222622", + "fgSurface5": "#000", "stroke1": "#6aa366", - "stroke2": "#60935c", - "stroke3": "#345331", + "stroke2": "#5c9358", + "stroke3": "#32512f", "stroke4": "#1f351e", - "surface1": "#52ab4d", + "surface1": "#50a94b", "surface2": "#14b814", - "surface3": "#5ab954", - "surface4": "#50a64b", - "surface5": "#4ea049", + "surface3": "#58b752", + "surface4": "#51a74c", + "surface5": "#4b9d46", "surface6": "#4a9346", }, "warnings": undefined, @@ -8168,25 +7881,23 @@ exports[`buildRamps > background ramp snapshots at lightness 40 1`] = ` "ramp": { "bgFill1": "#004c4c", "bgFill2": "#003d3e", - "bgFillDark": "#111313", "bgFillInverted1": "#002b2c", "bgFillInverted2": "#111313", "fgFill": "#e8f3f2", - "fgFillDark": "#e8f3f2", "fgFillInverted": "#e8f3f2", - "fgSurface1": "#636d6c", "fgSurface2": "#4b5252", "fgSurface3": "#323737", - "fgSurface4": "#111313", + "fgSurface4": "#282c2c", + "fgSurface5": "#000", "stroke1": "#71a6a5", - "stroke2": "#659695", - "stroke3": "#385655", + "stroke2": "#639796", + "stroke3": "#375554", "stroke4": "#243939", - "surface1": "#58acac", + "surface1": "#57abab", "surface2": "#14b8b8", - "surface3": "#60bab9", - "surface4": "#55a7a6", - "surface5": "#53a1a1", + "surface3": "#5fb9b8", + "surface4": "#56a8a7", + "surface5": "#509e9e", "surface6": "#509494", }, "warnings": undefined, @@ -8203,25 +7914,23 @@ exports[`buildRamps > background ramp snapshots at lightness 40 1`] = ` "ramp": { "bgFill1": "#6a94ff", "bgFill2": "#82a8ff", - "bgFillDark": "#000", "bgFillInverted1": "#c7dcff", "bgFillInverted2": "#eff0f2", "fgFill": "#161d30", - "fgFillDark": "#eff0f2", "fgFillInverted": "#161d30", - "fgSurface1": "#616c85", - "fgSurface2": "#828997", - "fgSurface3": "#b0b4bd", - "fgSurface4": "#eff0f2", + "fgSurface2": "#868d9b", + "fgSurface3": "#a9aeb8", + "fgSurface4": "#e6e8ea", + "fgSurface5": "#fdfefe", "stroke1": "#253c82", - "stroke2": "#2d4797", - "stroke3": "#7087c2", + "stroke2": "#324b94", + "stroke3": "#748bc7", "stroke4": "#99aad3", - "surface1": "#152985", + "surface1": "#182d89", "surface2": "#1414b8", - "surface3": "#1a319d", - "surface4": "#1c34a3", - "surface5": "#1f38ac", + "surface3": "#1d35a1", + "surface4": "#1d36a5", + "surface5": "#223cb0", "surface6": "#2542bd", }, "warnings": undefined, @@ -8238,25 +7947,23 @@ exports[`buildRamps > background ramp snapshots at lightness 40 1`] = ` "ramp": { "bgFill1": "#ff98ff", "bgFill2": "#ffb9ff", - "bgFillDark": "#000", "bgFillInverted1": "#ffdfff", "bgFillInverted2": "#fff", "fgFill": "#221c21", - "fgFillDark": "#f2eff2", "fgFillInverted": "#221c21", - "fgSurface1": "#9c829a", - "fgSurface2": "#b8a4b7", - "fgSurface3": "#d7cdd6", - "fgSurface4": "#fff", + "fgSurface2": "#bca8bb", + "fgSurface3": "#dbd2db", + "fgSurface4": "#ece7ec", + "fgSurface5": "#fffeff", "stroke1": "#80477e", - "stroke2": "#90518e", - "stroke3": "#dc91d9", + "stroke2": "#8f548d", + "stroke3": "#e195dd", "stroke4": "#edc6ea", - "surface1": "#7d2f7c", + "surface1": "#80327f", "surface2": "#970098", - "surface3": "#8c368b", - "surface4": "#903890", - "surface5": "#973c96", + "surface3": "#8f398e", + "surface4": "#913991", + "surface5": "#9b3f99", "surface6": "#a445a3", }, "warnings": undefined, @@ -8273,25 +7980,23 @@ exports[`buildRamps > background ramp snapshots at lightness 40 1`] = ` "ramp": { "bgFill1": "#ffa69d", "bgFill2": "#ffc1ba", - "bgFillDark": "#000", "bgFillInverted1": "#ffe3de", "bgFillInverted2": "#fff", "fgFill": "#231c1b", - "fgFillDark": "#f2efef", "fgFillInverted": "#231c1b", - "fgSurface1": "#9e8380", - "fgSurface2": "#b6a7a5", - "fgSurface3": "#d6cecd", - "fgSurface4": "#fff", + "fgSurface2": "#b8a9a7", + "fgSurface3": "#d8d1d0", + "fgSurface4": "#ebe7e6", + "fgSurface5": "#fefefd", "stroke1": "#894944", - "stroke2": "#9a534e", - "stroke3": "#d79b95", + "stroke2": "#985550", + "stroke3": "#da9e98", "stroke4": "#e9cac7", - "surface1": "#883330", + "surface1": "#8a3532", "surface2": "#a02526", - "surface3": "#983a36", + "surface3": "#9a3c38", "surface4": "#9d3d38", - "surface5": "#a4413c", + "surface5": "#a7443f", "surface6": "#b24a45", }, "warnings": undefined, @@ -8308,25 +8013,23 @@ exports[`buildRamps > background ramp snapshots at lightness 40 1`] = ` "ramp": { "bgFill1": "#414000", "bgFill2": "#333200", - "bgFillDark": "#050504", "bgFillInverted1": "#201f00", "bgFillInverted2": "#050504", "fgFill": "#f1f2e0", - "fgFillDark": "#f1f2e0", "fgFillInverted": "#f1f2e0", - "fgSurface1": "#65665d", "fgSurface2": "#4b4b45", - "fgSurface3": "#31312d", - "fgSurface4": "#050504", + "fgSurface3": "#30302c", + "fgSurface4": "#262622", + "fgSurface5": "#000", "stroke1": "#979868", - "stroke2": "#888a5e", - "stroke3": "#4c4d32", + "stroke2": "#898b5c", + "stroke3": "#4b4c31", "stroke4": "#30311f", - "surface1": "#9b9c51", + "surface1": "#9a9b50", "surface2": "#a3a329", - "surface3": "#a8a959", - "surface4": "#96984f", - "surface5": "#91924d", + "surface3": "#a7a858", + "surface4": "#979950", + "surface5": "#8f904b", "surface6": "#86874a", }, "warnings": undefined, @@ -8343,25 +8046,23 @@ exports[`buildRamps > background ramp snapshots at lightness 40 1`] = ` "ramp": { "bgFill1": "#004600", "bgFill2": "#003700", - "bgFillDark": "#000", "bgFillInverted1": "#002100", "bgFillInverted2": "#000", "fgFill": "#ebf2eb", - "fgFillDark": "#ebf2eb", "fgFillInverted": "#ebf2eb", - "fgSurface1": "#5b655a", "fgSurface2": "#444b43", - "fgSurface3": "#2b302b", - "fgSurface4": "#000", + "fgSurface3": "#2a2f2a", + "fgSurface4": "#20231f", + "fgSurface5": "#000", "stroke1": "#6b9f68", - "stroke2": "#61905d", - "stroke3": "#345032", + "stroke2": "#5f915b", + "stroke3": "#334f31", "stroke4": "#1f321d", - "surface1": "#57a753", + "surface1": "#56a652", "surface2": "#3bb239", - "surface3": "#5fb55b", - "surface4": "#54a150", - "surface5": "#529c4e", + "surface3": "#5eb45a", + "surface4": "#56a351", + "surface5": "#4f994b", "surface6": "#4d8f4a", }, "warnings": undefined, @@ -8378,25 +8079,23 @@ exports[`buildRamps > background ramp snapshots at lightness 40 1`] = ` "ramp": { "bgFill1": "#004344", "bgFill2": "#003435", - "bgFillDark": "#000", "bgFillInverted1": "#001f20", "bgFillInverted2": "#000", "fgFill": "#e8f3f2", - "fgFillDark": "#e8f3f2", "fgFillInverted": "#e8f3f2", - "fgSurface1": "#5c6564", "fgSurface2": "#444a4a", - "fgSurface3": "#2b2f2f", - "fgSurface4": "#000", + "fgSurface3": "#2a2e2e", + "fgSurface4": "#212424", + "fgSurface5": "#000", "stroke1": "#6c9b9a", - "stroke2": "#618c8b", - "stroke3": "#344d4d", + "stroke2": "#5f8d8c", + "stroke3": "#334c4c", "stroke4": "#1f3030", - "surface1": "#57a1a1", + "surface1": "#56a0a0", "surface2": "#35acab", - "surface3": "#5fafaf", - "surface4": "#549d9c", - "surface5": "#529796", + "surface3": "#5eaeae", + "surface4": "#559e9d", + "surface5": "#509594", "surface6": "#4e8b8a", }, "warnings": undefined, @@ -8413,25 +8112,23 @@ exports[`buildRamps > background ramp snapshots at lightness 40 1`] = ` "ramp": { "bgFill1": "#8097ff", "bgFill2": "#94abff", - "bgFillDark": "#000", "bgFillInverted1": "#ccdaff", "bgFillInverted2": "#eff0f2", "fgFill": "#191d2c", - "fgFillDark": "#eff0f2", "fgFillInverted": "#191d2c", - "fgSurface1": "#696f85", - "fgSurface2": "#898d9a", - "fgSurface3": "#b1b4bc", - "fgSurface4": "#eff0f2", + "fgSurface2": "#8b8f9c", + "fgSurface3": "#aeb0b9", + "fgSurface4": "#e7e8ea", + "fgSurface5": "#fdfefe", "stroke1": "#333f7f", - "stroke2": "#3d4a94", - "stroke3": "#7d8bc0", + "stroke2": "#404d90", + "stroke3": "#7f8dc2", "stroke4": "#a4aed3", - "surface1": "#262e83", + "surface1": "#273085", "surface2": "#2929a3", - "surface3": "#2d3698", + "surface3": "#2e389a", "surface4": "#30399e", - "surface5": "#333da6", + "surface5": "#3540a9", "surface6": "#3b47b5", }, "warnings": undefined, @@ -8448,25 +8145,23 @@ exports[`buildRamps > background ramp snapshots at lightness 40 1`] = ` "ramp": { "bgFill1": "#ff97ff", "bgFill2": "#ffb8ff", - "bgFillDark": "#000", "bgFillInverted1": "#ffdfff", "bgFillInverted2": "#fff", "fgFill": "#221c21", - "fgFillDark": "#f2eff2", "fgFillInverted": "#221c21", - "fgSurface1": "#9b8299", - "fgSurface2": "#b8a4b6", - "fgSurface3": "#d7ccd6", - "fgSurface4": "#fff", + "fgSurface2": "#bca8ba", + "fgSurface3": "#dad1da", + "fgSurface4": "#ebe6eb", + "fgSurface5": "#fefdfe", "stroke1": "#7e477d", - "stroke2": "#8f518d", - "stroke3": "#db91d8", + "stroke2": "#8e548c", + "stroke3": "#df95dc", "stroke4": "#ecc5e9", - "surface1": "#7b317b", + "surface1": "#7e347e", "surface2": "#941394", - "surface3": "#8a3789", - "surface4": "#8f398e", - "surface5": "#953d94", + "surface3": "#8d3a8c", + "surface4": "#903a8f", + "surface5": "#994198", "surface6": "#a246a1", }, "warnings": undefined, @@ -8483,25 +8178,23 @@ exports[`buildRamps > background ramp snapshots at lightness 40 1`] = ` "ramp": { "bgFill1": "#ffa4a1", "bgFill2": "#ffc0bc", - "bgFillDark": "#000", "bgFillInverted1": "#ffe3e0", "bgFillInverted2": "#fff", "fgFill": "#221c1c", - "fgFillDark": "#f2efef", "fgFillInverted": "#221c1c", - "fgSurface1": "#9a8583", "fgSurface2": "#b4a7a6", "fgSurface3": "#d5cecd", - "fgSurface4": "#fff", + "fgSurface4": "#eae7e7", + "fgSurface5": "#fefefd", "stroke1": "#824c4b", - "stroke2": "#935755", + "stroke2": "#915957", "stroke3": "#d19e9b", "stroke4": "#e5cbc9", "surface1": "#803a39", "surface2": "#8b3a3a", "surface3": "#8e4141", - "surface4": "#934443", - "surface5": "#9a4847", + "surface4": "#924342", + "surface5": "#9d4a49", "surface6": "#a75150", }, "warnings": undefined, @@ -8518,25 +8211,23 @@ exports[`buildRamps > background ramp snapshots at lightness 40 1`] = ` "ramp": { "bgFill1": "#3f3d00", "bgFill2": "#312f00", - "bgFillDark": "#000", "bgFillInverted1": "#1d1c00", "bgFillInverted2": "#000", "fgFill": "#f1f2e2", - "fgFillDark": "#f1f2e2", "fgFillInverted": "#f1f2e2", - "fgSurface1": "#63635c", "fgSurface2": "#494944", - "fgSurface3": "#2e2f2b", - "fgSurface4": "#000", + "fgSurface3": "#2f2f2b", + "fgSurface4": "#232320", + "fgSurface5": "#000", "stroke1": "#94956c", - "stroke2": "#868761", + "stroke2": "#86875f", "stroke3": "#4a4a34", "stroke4": "#2e2e1f", "surface1": "#98995a", "surface2": "#9fa04e", "surface3": "#a5a662", - "surface4": "#949458", - "surface5": "#8e8f55", + "surface4": "#96965a", + "surface5": "#8c8d53", "surface6": "#838450", }, "warnings": undefined, @@ -8553,25 +8244,23 @@ exports[`buildRamps > background ramp snapshots at lightness 40 1`] = ` "ramp": { "bgFill1": "#004700", "bgFill2": "#003700", - "bgFillDark": "#000", "bgFillInverted1": "#002100", "bgFillInverted2": "#000", "fgFill": "#ecf2ec", - "fgFillDark": "#ecf2ec", "fgFillInverted": "#ecf2ec", - "fgSurface1": "#5c655c", "fgSurface2": "#454b44", - "fgSurface3": "#2b302b", - "fgSurface4": "#000", + "fgSurface3": "#2b2f2a", + "fgSurface4": "#212420", + "fgSurface5": "#000", "stroke1": "#719e6f", - "stroke2": "#658e63", - "stroke3": "#364f35", + "stroke2": "#638f61", + "stroke3": "#354e34", "stroke4": "#213120", - "surface1": "#60a55e", + "surface1": "#5fa45d", "surface2": "#5cad5a", - "surface3": "#69b367", - "surface4": "#5ea05c", - "surface5": "#5b9a59", + "surface3": "#68b266", + "surface4": "#5fa15d", + "surface5": "#599857", "surface6": "#558e54", }, "warnings": undefined, @@ -8588,25 +8277,23 @@ exports[`buildRamps > background ramp snapshots at lightness 40 1`] = ` "ramp": { "bgFill1": "#004344", "bgFill2": "#003435", - "bgFillDark": "#000", "bgFillInverted1": "#001f20", "bgFillInverted2": "#000", "fgFill": "#e9f2f2", - "fgFillDark": "#e9f2f2", "fgFillInverted": "#e9f2f2", - "fgSurface1": "#5d6464", "fgSurface2": "#454a4a", - "fgSurface3": "#2b2f2f", - "fgSurface4": "#000", + "fgSurface3": "#2b2e2e", + "fgSurface4": "#212424", + "fgSurface5": "#000", "stroke1": "#719a9a", - "stroke2": "#668b8b", - "stroke3": "#374d4d", + "stroke2": "#648c8c", + "stroke3": "#364c4c", "stroke4": "#213030", - "surface1": "#60a0a0", + "surface1": "#609f9f", "surface2": "#58a8a8", - "surface3": "#69adad", - "surface4": "#5e9b9b", - "surface5": "#5b9695", + "surface3": "#68acac", + "surface4": "#5f9c9c", + "surface5": "#599493", "surface6": "#568a89", }, "warnings": undefined, @@ -8623,25 +8310,23 @@ exports[`buildRamps > background ramp snapshots at lightness 40 1`] = ` "ramp": { "bgFill1": "#9ba4ff", "bgFill2": "#afb8ff", - "bgFillDark": "#000", "bgFillInverted1": "#d2d9ff", "bgFillInverted2": "#f0f0f1", "fgFill": "#1c1d25", - "fgFillDark": "#f0f0f1", "fgFillInverted": "#1c1d25", - "fgSurface1": "#787a8a", "fgSurface2": "#9799a3", - "fgSurface3": "#bbbcc2", - "fgSurface4": "#f0f0f1", + "fgSurface3": "#bbbcc3", + "fgSurface4": "#e7e7ea", + "fgSurface5": "#fdfdfe", "stroke1": "#454982", - "stroke2": "#505594", + "stroke2": "#515691", "stroke3": "#9297bf", "stroke4": "#b9bdd6", "surface1": "#393985", "surface2": "#3d3d8f", "surface3": "#424296", - "surface4": "#45459c", - "surface5": "#4849a3", + "surface4": "#44449b", + "surface5": "#4a4ba6", "surface6": "#5154ad", }, "warnings": undefined, @@ -8658,25 +8343,23 @@ exports[`buildRamps > background ramp snapshots at lightness 40 1`] = ` "ramp": { "bgFill1": "#f69ef4", "bgFill2": "#ffb8ff", - "bgFillDark": "#000", "bgFillInverted1": "#ffdfff", "bgFillInverted2": "#fff", "fgFill": "#211c21", - "fgFillDark": "#f2eff2", "fgFillInverted": "#211c21", - "fgSurface1": "#978496", - "fgSurface2": "#b6a5b5", - "fgSurface3": "#d6cdd5", - "fgSurface4": "#fff", + "fgSurface2": "#b7a6b6", + "fgSurface3": "#d7ced6", + "fgSurface4": "#ebe6eb", + "fgSurface5": "#fefdfe", "stroke1": "#794b79", - "stroke2": "#895588", - "stroke3": "#d495d2", + "stroke2": "#885787", + "stroke3": "#d596d3", "stroke4": "#e8c7e6", - "surface1": "#753775", + "surface1": "#763876", "surface2": "#833284", - "surface3": "#833e83", - "surface4": "#884188", - "surface5": "#8e458d", + "surface3": "#843f84", + "surface4": "#874087", + "surface5": "#914790", "surface6": "#9a4e9a", }, "warnings": undefined, @@ -8693,25 +8376,23 @@ exports[`buildRamps > background ramp snapshots at lightness 40 1`] = ` "ramp": { "bgFill1": "#e0b1b0", "bgFill2": "#f4c4c3", - "bgFillDark": "#000", "bgFillInverted1": "#ffe2e2", "bgFillInverted2": "#fff", "fgFill": "#211d1d", - "fgFillDark": "#f1f0ef", "fgFillInverted": "#211d1d", - "fgSurface1": "#958686", "fgSurface2": "#b2a8a8", - "fgSurface3": "#d3cece", - "fgSurface4": "#fff", + "fgSurface3": "#d2cdcd", + "fgSurface4": "#eae7e7", + "fgSurface5": "#fefefe", "stroke1": "#775151", - "stroke2": "#875d5c", + "stroke2": "#855e5d", "stroke3": "#c6a1a1", "stroke4": "#e0cdcc", - "surface1": "#724142", + "surface1": "#714041", "surface2": "#714949", - "surface3": "#804a4b", - "surface4": "#844d4e", - "surface5": "#8a5152", + "surface3": "#7f494a", + "surface4": "#824b4c", + "surface5": "#8c5354", "surface6": "#965a5b", }, "warnings": undefined, @@ -8728,25 +8409,23 @@ exports[`buildRamps > background ramp snapshots at lightness 40 1`] = ` "ramp": { "bgFill1": "#3e3d15", "bgFill2": "#302f06", - "bgFillDark": "#000", "bgFillInverted1": "#1d1c00", "bgFillInverted2": "#000", "fgFill": "#f1f1e5", - "fgFillDark": "#f1f1e5", "fgFillInverted": "#f1f1e5", - "fgSurface1": "#63635e", "fgSurface2": "#494945", "fgSurface3": "#2e2f2c", - "fgSurface4": "#000", + "fgSurface4": "#232321", + "fgSurface5": "#000", "stroke1": "#949576", - "stroke2": "#86866b", + "stroke2": "#878769", "stroke3": "#4a4a3a", "stroke4": "#2e2e23", "surface1": "#98986a", "surface2": "#9e9e74", "surface3": "#a4a574", - "surface4": "#939468", - "surface5": "#8e8e64", + "surface4": "#949569", + "surface5": "#8c8c62", "surface6": "#82835d", }, "warnings": undefined, @@ -8763,25 +8442,23 @@ exports[`buildRamps > background ramp snapshots at lightness 40 1`] = ` "ramp": { "bgFill1": "#9cc89b", "bgFill2": "#afdbae", - "bgFillDark": "#000", "bgFillInverted1": "#c7f4c6", "bgFillInverted2": "#fff", "fgFill": "#1c1f1c", - "fgFillDark": "#ecf2ec", "fgFillInverted": "#1c1f1c", - "fgSurface1": "#828c82", "fgSurface2": "#a1ada1", - "fgSurface3": "#c5d3c4", - "fgSurface4": "#fff", + "fgSurface3": "#c5d3c5", + "fgSurface4": "#e1eae1", + "fgSurface5": "#fcfefc", "stroke1": "#466246", - "stroke2": "#506f50", + "stroke2": "#526f52", "stroke3": "#86b685", "stroke4": "#a5dfa5", "surface1": "#335733", "surface2": "#355d36", "surface3": "#39623a", - "surface4": "#3c663d", - "surface5": "#406b40", + "surface4": "#3b653c", + "surface5": "#426d42", "surface6": "#487549", }, "warnings": undefined, @@ -8798,25 +8475,23 @@ exports[`buildRamps > background ramp snapshots at lightness 40 1`] = ` "ramp": { "bgFill1": "#9ac5c4", "bgFill2": "#acd8d8", - "bgFillDark": "#000", "bgFillInverted1": "#c5f1f1", "bgFillInverted2": "#fff", "fgFill": "#1c1f1f", - "fgFillDark": "#eaf2f2", "fgFillInverted": "#1c1f1f", - "fgSurface1": "#838b8b", "fgSurface2": "#a1acac", - "fgSurface3": "#c5d2d1", - "fgSurface4": "#fff", + "fgSurface3": "#c4d0d0", + "fgSurface4": "#ddeaea", + "fgSurface5": "#fbfefe", "stroke1": "#465f5f", - "stroke2": "#506c6c", + "stroke2": "#526c6c", "stroke3": "#86b2b2", "stroke4": "#a6dbda", - "surface1": "#355", + "surface1": "#325454", "surface2": "#325a5b", - "surface3": "#3a5f5f", - "surface4": "#3c6363", - "surface5": "#406767", + "surface3": "#395e5e", + "surface4": "#3b6262", + "surface5": "#436a6a", "surface6": "#497272", }, "warnings": undefined, @@ -8833,25 +8508,23 @@ exports[`buildRamps > background ramp snapshots at lightness 40 1`] = ` "ramp": { "bgFill1": "#b8b9e8", "bgFill2": "#cbccfc", - "bgFillDark": "#000", "bgFillInverted1": "#e5e7ff", "bgFillInverted2": "#fff", "fgFill": "#1d1e22", - "fgFillDark": "#f0f0f1", "fgFillInverted": "#1d1e22", - "fgSurface1": "#888992", "fgSurface2": "#a9aab0", - "fgSurface3": "#cfcfd2", - "fgSurface4": "#fff", + "fgSurface3": "#cdced1", + "fgSurface4": "#e7e7e9", + "fgSurface5": "#fdfdfe", "stroke1": "#575782", "stroke2": "#63638f", "stroke3": "#a7a8c0", "stroke4": "#d0d0dd", - "surface1": "#4b4981", + "surface1": "#4a4880", "surface2": "#505079", - "surface3": "#545290", - "surface4": "#575594", - "surface5": "#5c5a99", + "surface3": "#53518f", + "surface4": "#565493", + "surface5": "#5e5c9b", "surface6": "#6665a0", }, "warnings": undefined, @@ -8868,25 +8541,23 @@ exports[`buildRamps > background ramp snapshots at lightness 40 1`] = ` "ramp": { "bgFill1": "#dcaedb", "bgFill2": "#f0c1ef", - "bgFillDark": "#000", "bgFillInverted1": "#ffdfff", "bgFillInverted2": "#fff", "fgFill": "#201d20", - "fgFillDark": "#f1eff1", "fgFillInverted": "#201d20", - "fgSurface1": "#938692", - "fgSurface2": "#b2a6b2", - "fgSurface3": "#d4cdd4", - "fgSurface4": "#fff", + "fgSurface2": "#b3a7b3", + "fgSurface3": "#d4ced4", + "fgSurface4": "#eae7ea", + "fgSurface5": "#fefefe", "stroke1": "#715071", - "stroke2": "#805b80", - "stroke3": "#c99bc9", + "stroke2": "#7f5d7f", + "stroke3": "#ca9cca", "stroke4": "#e2cae1", "surface1": "#6b3f6b", "surface2": "#6e466e", "surface3": "#784879", - "surface4": "#7c4a7d", - "surface5": "#824e82", + "surface4": "#7b497c", + "surface5": "#845084", "surface6": "#8d588e", }, "warnings": undefined, @@ -8903,16 +8574,14 @@ exports[`buildRamps > background ramp snapshots at lightness 40 1`] = ` "ramp": { "bgFill1": "#bdbdbd", "bgFill2": "#d0d0d0", - "bgFillDark": "#000", "bgFillInverted1": "#e8e8e8", "bgFillInverted2": "#fff", "fgFill": "#1e1e1e", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#1e1e1e", - "fgSurface1": "#898989", "fgSurface2": "#aaa", - "fgSurface3": "#cfcfcf", - "fgSurface4": "#fff", + "fgSurface3": "#d0d0d0", + "fgSurface4": "#e8e8e8", + "fgSurface5": "#fefefe", "stroke1": "#5b5b5b", "stroke2": "#676767", "stroke3": "#aaa", @@ -8921,7 +8590,7 @@ exports[`buildRamps > background ramp snapshots at lightness 40 1`] = ` "surface2": "#545454", "surface3": "#595959", "surface4": "#5c5c5c", - "surface5": "#616161", + "surface5": "#636363", "surface6": "#6b6b6b", }, "warnings": undefined, @@ -8938,16 +8607,14 @@ exports[`buildRamps > background ramp snapshots at lightness 40 1`] = ` "ramp": { "bgFill1": "#bdbdbd", "bgFill2": "#d0d0d0", - "bgFillDark": "#000", "bgFillInverted1": "#e8e8e8", "bgFillInverted2": "#fff", "fgFill": "#1e1e1e", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#1e1e1e", - "fgSurface1": "#898989", "fgSurface2": "#aaa", - "fgSurface3": "#cfcfcf", - "fgSurface4": "#fff", + "fgSurface3": "#d0d0d0", + "fgSurface4": "#e8e8e8", + "fgSurface5": "#fefefe", "stroke1": "#5b5b5b", "stroke2": "#676767", "stroke3": "#aaa", @@ -8956,7 +8623,7 @@ exports[`buildRamps > background ramp snapshots at lightness 40 1`] = ` "surface2": "#545454", "surface3": "#595959", "surface4": "#5c5c5c", - "surface5": "#616161", + "surface5": "#636363", "surface6": "#6b6b6b", }, "warnings": undefined, @@ -8973,16 +8640,14 @@ exports[`buildRamps > background ramp snapshots at lightness 40 1`] = ` "ramp": { "bgFill1": "#bdbdbd", "bgFill2": "#d0d0d0", - "bgFillDark": "#000", "bgFillInverted1": "#e8e8e8", "bgFillInverted2": "#fff", "fgFill": "#1e1e1e", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#1e1e1e", - "fgSurface1": "#898989", "fgSurface2": "#aaa", - "fgSurface3": "#cfcfcf", - "fgSurface4": "#fff", + "fgSurface3": "#d0d0d0", + "fgSurface4": "#e8e8e8", + "fgSurface5": "#fefefe", "stroke1": "#5b5b5b", "stroke2": "#676767", "stroke3": "#aaa", @@ -8991,7 +8656,7 @@ exports[`buildRamps > background ramp snapshots at lightness 40 1`] = ` "surface2": "#545454", "surface3": "#595959", "surface4": "#5c5c5c", - "surface5": "#616161", + "surface5": "#636363", "surface6": "#6b6b6b", }, "warnings": undefined, @@ -9008,16 +8673,14 @@ exports[`buildRamps > background ramp snapshots at lightness 40 1`] = ` "ramp": { "bgFill1": "#bdbdbd", "bgFill2": "#d0d0d0", - "bgFillDark": "#000", "bgFillInverted1": "#e8e8e8", "bgFillInverted2": "#fff", "fgFill": "#1e1e1e", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#1e1e1e", - "fgSurface1": "#898989", "fgSurface2": "#aaa", - "fgSurface3": "#cfcfcf", - "fgSurface4": "#fff", + "fgSurface3": "#d0d0d0", + "fgSurface4": "#e8e8e8", + "fgSurface5": "#fefefe", "stroke1": "#5b5b5b", "stroke2": "#676767", "stroke3": "#aaa", @@ -9026,7 +8689,7 @@ exports[`buildRamps > background ramp snapshots at lightness 40 1`] = ` "surface2": "#545454", "surface3": "#595959", "surface4": "#5c5c5c", - "surface5": "#616161", + "surface5": "#636363", "surface6": "#6b6b6b", }, "warnings": undefined, @@ -9043,16 +8706,14 @@ exports[`buildRamps > background ramp snapshots at lightness 40 1`] = ` "ramp": { "bgFill1": "#bdbdbd", "bgFill2": "#d0d0d0", - "bgFillDark": "#000", "bgFillInverted1": "#e8e8e8", "bgFillInverted2": "#fff", "fgFill": "#1e1e1e", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#1e1e1e", - "fgSurface1": "#898989", "fgSurface2": "#aaa", - "fgSurface3": "#cfcfcf", - "fgSurface4": "#fff", + "fgSurface3": "#d0d0d0", + "fgSurface4": "#e8e8e8", + "fgSurface5": "#fefefe", "stroke1": "#5b5b5b", "stroke2": "#676767", "stroke3": "#aaa", @@ -9061,7 +8722,7 @@ exports[`buildRamps > background ramp snapshots at lightness 40 1`] = ` "surface2": "#545454", "surface3": "#595959", "surface4": "#5c5c5c", - "surface5": "#616161", + "surface5": "#636363", "surface6": "#6b6b6b", }, "warnings": undefined, @@ -9078,16 +8739,14 @@ exports[`buildRamps > background ramp snapshots at lightness 40 1`] = ` "ramp": { "bgFill1": "#bdbdbd", "bgFill2": "#d0d0d0", - "bgFillDark": "#000", "bgFillInverted1": "#e8e8e8", "bgFillInverted2": "#fff", "fgFill": "#1e1e1e", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#1e1e1e", - "fgSurface1": "#898989", "fgSurface2": "#aaa", - "fgSurface3": "#cfcfcf", - "fgSurface4": "#fff", + "fgSurface3": "#d0d0d0", + "fgSurface4": "#e8e8e8", + "fgSurface5": "#fefefe", "stroke1": "#5b5b5b", "stroke2": "#676767", "stroke3": "#aaa", @@ -9096,7 +8755,7 @@ exports[`buildRamps > background ramp snapshots at lightness 40 1`] = ` "surface2": "#545454", "surface3": "#595959", "surface4": "#5c5c5c", - "surface5": "#616161", + "surface5": "#636363", "surface6": "#6b6b6b", }, "warnings": undefined, @@ -9118,25 +8777,23 @@ exports[`buildRamps > background ramp snapshots at lightness 50 1`] = ` "ramp": { "bgFill1": "#7d0000", "bgFill2": "#640000", - "bgFillDark": "#000", "bgFillInverted1": "#400000", "bgFillInverted2": "#000", "fgFill": "#f2efef", - "fgFillDark": "#f2efef", "fgFillInverted": "#f2efef", - "fgSurface1": "#715f5b", "fgSurface2": "#544643", - "fgSurface3": "#362c2a", - "fgSurface4": "#000", + "fgSurface3": "#382e2c", + "fgSurface4": "#292120", + "fgSurface5": "#000", "stroke1": "#d07c70", - "stroke2": "#c66a5d", + "stroke2": "#c06c60", "stroke3": "#713931", "stroke4": "#49221c", - "surface1": "#e77363", + "surface1": "#e97564", "surface2": "#ff6c5a", - "surface3": "#e98677", - "surface4": "#e56c5c", - "surface5": "#e36353", + "surface3": "#eb8878", + "surface4": "#e86e5e", + "surface5": "#e16151", "surface6": "#d65848", }, "warnings": undefined, @@ -9153,25 +8810,23 @@ exports[`buildRamps > background ramp snapshots at lightness 50 1`] = ` "ramp": { "bgFill1": "#5a5a00", "bgFill2": "#4d4d00", - "bgFillDark": "#1e1e1b", "bgFillInverted1": "#302f00", "bgFillInverted2": "#1e1e1b", "fgFill": "#f5f893", - "fgFillDark": "#f5f893", "fgFillInverted": "#f5f893", - "fgSurface1": "#a9aa9d", "fgSurface2": "#898a7f", - "fgSurface3": "#6c6d64", - "fgSurface4": "#1e1e1b", + "fgSurface3": "#6b6c63", + "fgSurface4": "#494a43", + "fgSurface5": "#141412", "stroke1": "#ecefa2", - "stroke2": "#d9db94", - "stroke3": "#8a8c5d", + "stroke2": "#d9db90", + "stroke3": "#898b5c", "stroke4": "#6c6d48", - "surface1": "#f2f47c", + "surface1": "#f1f37b", "surface2": "#ff0", - "surface3": "#fff", - "surface4": "#ecee7b", - "surface5": "#e5e779", + "surface3": "#fefefe", + "surface4": "#eef07d", + "surface5": "#e2e476", "surface6": "#d5d777", }, "warnings": undefined, @@ -9188,25 +8843,23 @@ exports[`buildRamps > background ramp snapshots at lightness 50 1`] = ` "ramp": { "bgFill1": "#006b00", "bgFill2": "#005c00", - "bgFillDark": "#1b1f1b", "bgFillInverted1": "#003700", "bgFillInverted2": "#1b1f1b", "fgFill": "#dff9dc", - "fgFillDark": "#dff9dc", "fgFillInverted": "#dff9dc", - "fgSurface1": "#8b998a", "fgSurface2": "#6f7a6e", - "fgSurface3": "#555f54", - "fgSurface4": "#1b1f1b", + "fgSurface3": "#545c53", + "fgSurface4": "#4b544b", + "fgSurface5": "#121512", "stroke1": "#95e48f", - "stroke2": "#88d082", - "stroke3": "#53824f", + "stroke2": "#84d07d", + "stroke3": "#51804d", "stroke4": "#3f643c", - "surface1": "#73ee6c", + "surface1": "#70eb69", "surface2": "#0f0", - "surface3": "#98fb90", - "surface4": "#71e769", - "surface5": "#6fe068", + "surface3": "#95f88d", + "surface4": "#72e86a", + "surface5": "#6bdc64", "surface6": "#6ccf65", }, "warnings": undefined, @@ -9223,25 +8876,23 @@ exports[`buildRamps > background ramp snapshots at lightness 50 1`] = ` "ramp": { "bgFill1": "#006565", "bgFill2": "#005757", - "bgFillDark": "#1b1f1f", "bgFillInverted1": "#003434", "bgFillInverted2": "#1b1f1f", "fgFill": "#c8fcfb", - "fgFillDark": "#c8fcfb", "fgFillInverted": "#c8fcfb", - "fgSurface1": "#929f9e", "fgSurface2": "#757f7f", - "fgSurface3": "#5b6363", - "fgSurface4": "#1b1f1f", + "fgSurface3": "#5a6262", + "fgSurface4": "#515959", + "fgSurface5": "#121515", "stroke1": "#9de7e6", - "stroke2": "#8fd3d2", - "stroke3": "#598585", + "stroke2": "#8bd4d3", + "stroke3": "#578383", "stroke4": "#446767", - "surface1": "#7aefef", + "surface1": "#78eded", "surface2": "#0ff", - "surface3": "#9efefd", - "surface4": "#78e9e8", - "surface5": "#76e2e1", + "surface3": "#9cfcfb", + "surface4": "#79eae9", + "surface5": "#72dedd", "surface6": "#73d1d0", }, "warnings": undefined, @@ -9258,25 +8909,23 @@ exports[`buildRamps > background ramp snapshots at lightness 50 1`] = ` "ramp": { "bgFill1": "#87b2ff", "bgFill2": "#a2c5ff", - "bgFillDark": "#000", "bgFillInverted1": "#c6deff", "bgFillInverted2": "#f0f1f3", "fgFill": "#191e28", - "fgFillDark": "#eff0f2", "fgFillInverted": "#191e28", - "fgSurface1": "#75819b", - "fgSurface2": "#9aa1ad", - "fgSurface3": "#c0c4cc", - "fgSurface4": "#f0f1f3", + "fgSurface2": "#a1a8b4", + "fgSurface3": "#c9cdd3", + "fgSurface4": "#e6e8eb", + "fgSurface5": "#fdfefe", "stroke1": "#1f4aba", - "stroke2": "#375cb1", - "stroke3": "#89a1d0", + "stroke2": "#3b68d1", + "stroke3": "#90a8d8", "stroke4": "#b8c6e3", - "surface1": "#0b38b7", + "surface1": "#113fbe", "surface2": "#00f", - "surface3": "#1041cc", - "surface4": "#1345d0", - "surface5": "#194bd2", + "surface3": "#1648d3", + "surface4": "#1749d4", + "surface5": "#1d50d7", "surface6": "#2959d2", }, "warnings": undefined, @@ -9293,25 +8942,23 @@ exports[`buildRamps > background ramp snapshots at lightness 50 1`] = ` "ramp": { "bgFill1": "#6e006f", "bgFill2": "#570059", - "bgFillDark": "#000", "bgFillInverted1": "#370038", "bgFillInverted2": "#000", "fgFill": "#f2eff2", - "fgFillDark": "#f2eff2", "fgFillInverted": "#f2eff2", - "fgSurface1": "#6c5e6c", "fgSurface2": "#504550", - "fgSurface3": "#342c33", - "fgSurface4": "#000", + "fgSurface3": "#372f37", + "fgSurface4": "#282228", + "fgSurface5": "#000", "stroke1": "#c874c5", - "stroke2": "#b569b3", + "stroke2": "#b766b5", "stroke3": "#663865", "stroke4": "#412240", - "surface1": "#e162df", + "surface1": "#e566e3", "surface2": "#ff47ff", - "surface3": "#eb74e9", - "surface4": "#dc5fda", - "surface5": "#d35cd1", + "surface3": "#ef78ed", + "surface4": "#df62dd", + "surface5": "#d15acf", "surface6": "#bf58be", }, "warnings": undefined, @@ -9328,25 +8975,23 @@ exports[`buildRamps > background ramp snapshots at lightness 50 1`] = ` "ramp": { "bgFill1": "#ffa697", "bgFill2": "#ffc2b6", - "bgFillDark": "#000", "bgFillInverted1": "#ffe3dc", "bgFillInverted2": "#fff", "fgFill": "#241c1b", - "fgFillDark": "#f2efef", "fgFillInverted": "#241c1b", - "fgSurface1": "#a1837d", - "fgSurface2": "#b7a6a3", - "fgSurface3": "#d6cdcc", - "fgSurface4": "#fff", + "fgSurface2": "#baa9a6", + "fgSurface3": "#d9d1cf", + "fgSurface4": "#ebe7e6", + "fgSurface5": "#fefefd", "stroke1": "#8c473d", - "stroke2": "#9f5146", - "stroke3": "#da9a90", + "stroke2": "#9d554a", + "stroke3": "#de9d93", "stroke4": "#ebcac4", - "surface1": "#8d3026", + "surface1": "#8f3228", "surface2": "#ad0000", - "surface3": "#9d362b", - "surface4": "#a2382d", - "surface5": "#a93c30", + "surface3": "#9f382d", + "surface4": "#a3392e", + "surface5": "#ad3f33", "surface6": "#b74538", }, "warnings": undefined, @@ -9363,25 +9008,23 @@ exports[`buildRamps > background ramp snapshots at lightness 50 1`] = ` "ramp": { "bgFill1": "#5a5900", "bgFill2": "#4d4d00", - "bgFillDark": "#1e1e1b", "bgFillInverted1": "#302f00", "bgFillInverted2": "#1e1e1b", "fgFill": "#f4f7a5", - "fgFillDark": "#f4f7a5", "fgFillInverted": "#f4f7a5", - "fgSurface1": "#97988c", "fgSurface2": "#79796f", "fgSurface3": "#5d5e56", - "fgSurface4": "#1e1e1b", + "fgSurface4": "#54544d", + "fgSurface5": "#141412", "stroke1": "#d5d792", - "stroke2": "#c3c585", + "stroke2": "#c3c582", "stroke3": "#7a7b52", "stroke4": "#5d5f3e", "surface1": "#dadc71", "surface2": "#e6e61a", "surface3": "#ebed7a", - "surface4": "#d5d66f", - "surface5": "#cecf6d", + "surface4": "#d7d871", + "surface5": "#cccd6b", "surface6": "#bfc16a", }, "warnings": undefined, @@ -9398,25 +9041,23 @@ exports[`buildRamps > background ramp snapshots at lightness 50 1`] = ` "ramp": { "bgFill1": "#006a00", "bgFill2": "#005b00", - "bgFillDark": "#1c1f1b", "bgFillInverted1": "#003700", "bgFillInverted2": "#1c1f1b", "fgFill": "#e9f4e7", - "fgFillDark": "#e9f4e7", "fgFillInverted": "#e9f4e7", - "fgSurface1": "#7c887a", "fgSurface2": "#616b60", - "fgSurface3": "#495048", - "fgSurface4": "#1c1f1b", + "fgSurface3": "#474f46", + "fgSurface4": "#3f463e", + "fgSurface5": "#000", "stroke1": "#87cd81", - "stroke2": "#7abb75", - "stroke3": "#497246", + "stroke2": "#77bb72", + "stroke3": "#477044", "stroke4": "#355532", - "surface1": "#69d662", + "surface1": "#67d460", "surface2": "#1ae61a", - "surface3": "#72e66b", + "surface3": "#70e469", "surface4": "#67d160", - "surface5": "#64c95e", + "surface5": "#61c65b", "surface6": "#61ba5b", }, "warnings": undefined, @@ -9433,25 +9074,23 @@ exports[`buildRamps > background ramp snapshots at lightness 50 1`] = ` "ramp": { "bgFill1": "#006566", "bgFill2": "#005757", - "bgFillDark": "#1c1f1f", "bgFillInverted1": "#003434", "bgFillInverted2": "#1c1f1f", "fgFill": "#dbf7f6", - "fgFillDark": "#dbf7f6", "fgFillInverted": "#dbf7f6", - "fgSurface1": "#828d8d", "fgSurface2": "#67706f", - "fgSurface3": "#4e5555", - "fgSurface4": "#1c1f1f", + "fgSurface3": "#4d5454", + "fgSurface4": "#454b4b", + "fgSurface5": "#121515", "stroke1": "#8ecfce", - "stroke2": "#81bdbc", - "stroke3": "#4e7574", + "stroke2": "#7ebebd", + "stroke3": "#4c7372", "stroke4": "#395857", - "surface1": "#6fd7d7", + "surface1": "#6ed6d6", "surface2": "#1ae6e6", - "surface3": "#79e8e7", - "surface4": "#6dd2d1", - "surface5": "#6bcbca", + "surface3": "#78e7e6", + "surface4": "#6ed3d2", + "surface5": "#68c8c7", "surface6": "#67bcbb", }, "warnings": undefined, @@ -9468,25 +9107,23 @@ exports[`buildRamps > background ramp snapshots at lightness 50 1`] = ` "ramp": { "bgFill1": "#86acff", "bgFill2": "#9fbfff", - "bgFillDark": "#000", "bgFillInverted1": "#c7dcff", "bgFillInverted2": "#eff0f2", "fgFill": "#1a1e27", - "fgFillDark": "#eff0f2", "fgFillInverted": "#1a1e27", - "fgSurface1": "#727d95", - "fgSurface2": "#969ca8", - "fgSurface3": "#bbbfc7", - "fgSurface4": "#eff0f2", + "fgSurface2": "#9ca2ae", + "fgSurface3": "#c3c6cd", + "fgSurface4": "#e6e8eb", + "fgSurface5": "#fdfefe", "stroke1": "#2b49a7", - "stroke2": "#3a57ac", - "stroke3": "#879bcc", + "stroke2": "#3958b6", + "stroke3": "#8ea2d3", "stroke4": "#b3c0df", - "surface1": "#1b36a9", + "surface1": "#1f3caf", "surface2": "#1a1ae6", - "surface3": "#203ec0", - "surface4": "#2342c5", - "surface5": "#2647c9", + "surface3": "#2444c6", + "surface4": "#2545c8", + "surface5": "#294cce", "surface6": "#3255cc", }, "warnings": undefined, @@ -9503,25 +9140,23 @@ exports[`buildRamps > background ramp snapshots at lightness 50 1`] = ` "ramp": { "bgFill1": "#6e006f", "bgFill2": "#580059", - "bgFillDark": "#000", "bgFillInverted1": "#370038", "bgFillInverted2": "#000", "fgFill": "#f2eff2", - "fgFillDark": "#f2eff2", "fgFillInverted": "#f2eff2", - "fgSurface1": "#6d5e6c", "fgSurface2": "#504550", - "fgSurface3": "#342c33", - "fgSurface4": "#000", + "fgSurface3": "#372f37", + "fgSurface4": "#282228", + "fgSurface5": "#000", "stroke1": "#c874c6", - "stroke2": "#b569b3", + "stroke2": "#b665b4", "stroke3": "#663865", "stroke4": "#412240", - "surface1": "#e162e0", + "surface1": "#e566e4", "surface2": "#ff47ff", - "surface3": "#eb75e9", - "surface4": "#dc5fda", - "surface5": "#d35cd1", + "surface3": "#ef78ed", + "surface4": "#df62dd", + "surface5": "#d15acf", "surface6": "#bf58be", }, "warnings": undefined, @@ -9538,25 +9173,23 @@ exports[`buildRamps > background ramp snapshots at lightness 50 1`] = ` "ramp": { "bgFill1": "#ffa69c", "bgFill2": "#ffc1b9", - "bgFillDark": "#000", "bgFillInverted1": "#ffe3de", "bgFillInverted2": "#fff", "fgFill": "#241c1b", - "fgFillDark": "#f2efef", "fgFillInverted": "#241c1b", - "fgSurface1": "#a1827f", - "fgSurface2": "#b7a6a4", - "fgSurface3": "#d6cdcc", - "fgSurface4": "#fff", + "fgSurface2": "#baa9a7", + "fgSurface3": "#d9d1d0", + "fgSurface4": "#ebe7e6", + "fgSurface5": "#fefefd", "stroke1": "#8c4741", - "stroke2": "#9e514b", - "stroke3": "#da9a93", + "stroke2": "#9c544e", + "stroke3": "#de9d96", "stroke4": "#ebcac6", - "surface1": "#8c2f2c", + "surface1": "#8e312e", "surface2": "#ac0116", - "surface3": "#9d3632", - "surface4": "#a23834", - "surface5": "#a93c37", + "surface3": "#9f3834", + "surface4": "#a33935", + "surface5": "#ad3f3a", "surface6": "#b74540", }, "warnings": undefined, @@ -9573,25 +9206,23 @@ exports[`buildRamps > background ramp snapshots at lightness 50 1`] = ` "ramp": { "bgFill1": "#5b5900", "bgFill2": "#4e4c00", - "bgFillDark": "#1e1e1b", "bgFillInverted1": "#302f00", "bgFillInverted2": "#1e1e1b", "fgFill": "#f2f3d0", - "fgFillDark": "#f2f3d0", "fgFillInverted": "#f2f3d0", - "fgSurface1": "#84857a", "fgSurface2": "#686860", - "fgSurface3": "#4e4e48", - "fgSurface4": "#1e1e1b", + "fgSurface3": "#4e4e47", + "fgSurface4": "#44443e", + "fgSurface5": "#000", "stroke1": "#bdbf84", - "stroke2": "#acae77", - "stroke3": "#696a47", + "stroke2": "#acae74", + "stroke3": "#686946", "stroke4": "#4e4e34", "surface1": "#c2c367", "surface2": "#cc3", "surface3": "#d1d370", - "surface4": "#bdbe65", - "surface5": "#b7b863", + "surface4": "#bfc067", + "surface5": "#b4b560", "surface6": "#a9ab60", }, "warnings": undefined, @@ -9608,25 +9239,23 @@ exports[`buildRamps > background ramp snapshots at lightness 50 1`] = ` "ramp": { "bgFill1": "#005900", "bgFill2": "#004b00", - "bgFillDark": "#1c1f1b", "bgFillInverted1": "#003700", "bgFillInverted2": "#1c1f1b", "fgFill": "#ebf3eb", - "fgFillDark": "#ebf3eb", "fgFillInverted": "#ebf3eb", - "fgSurface1": "#6c786b", "fgSurface2": "#535c53", - "fgSurface3": "#3b413b", - "fgSurface4": "#1c1f1b", + "fgSurface3": "#3a4039", + "fgSurface4": "#313731", + "fgSurface5": "#000", "stroke1": "#79b775", - "stroke2": "#6ea66a", - "stroke3": "#3f623d", + "stroke2": "#6aa666", + "stroke3": "#3d603b", "stroke4": "#2b4529", - "surface1": "#60bf5c", + "surface1": "#5ebd5a", "surface2": "#3c3", - "surface3": "#69ce64", - "surface4": "#5eb959", - "surface5": "#5bb357", + "surface3": "#67cc62", + "surface4": "#5fba5a", + "surface5": "#58b054", "surface6": "#57a553", }, "warnings": undefined, @@ -9643,25 +9272,23 @@ exports[`buildRamps > background ramp snapshots at lightness 50 1`] = ` "ramp": { "bgFill1": "#005b5c", "bgFill2": "#004d4e", - "bgFillDark": "#1c1f1f", "bgFillInverted1": "#003435", "bgFillInverted2": "#1c1f1f", "fgFill": "#e8f3f3", - "fgFillDark": "#e8f3f3", "fgFillInverted": "#e8f3f3", - "fgSurface1": "#727c7b", "fgSurface2": "#586060", - "fgSurface3": "#404646", - "fgSurface4": "#1c1f1f", + "fgSurface3": "#3f4545", + "fgSurface4": "#363b3b", + "fgSurface5": "#000", "stroke1": "#80b9b8", - "stroke2": "#74a8a7", - "stroke3": "#436464", + "stroke2": "#71a9a8", + "stroke3": "#426363", "stroke4": "#2f4848", - "surface1": "#66c0bf", + "surface1": "#65bfbe", "surface2": "#3cc", - "surface3": "#6fcfcf", - "surface4": "#64bbba", - "surface5": "#61b4b4", + "surface3": "#6ecece", + "surface4": "#65bcbb", + "surface5": "#5eb1b1", "surface6": "#5da6a6", }, "warnings": undefined, @@ -9678,25 +9305,23 @@ exports[`buildRamps > background ramp snapshots at lightness 50 1`] = ` "ramp": { "bgFill1": "#99b0ff", "bgFill2": "#b0c4ff", - "bgFillDark": "#000", "bgFillInverted1": "#ceddff", "bgFillInverted2": "#f2f2f4", "fgFill": "#1b1e26", - "fgFillDark": "#eff0f2", "fgFillInverted": "#1b1e26", - "fgSurface1": "#7b8195", - "fgSurface2": "#9da1ac", - "fgSurface3": "#c2c5cb", - "fgSurface4": "#f2f2f4", + "fgSurface2": "#a0a4af", + "fgSurface3": "#c7c9cf", + "fgSurface4": "#e7e8ea", + "fgSurface5": "#fdfefe", "stroke1": "#3f4e9c", - "stroke2": "#4b5ba8", - "stroke3": "#939fcb", + "stroke2": "#4b5baa", + "stroke3": "#97a3cf", "stroke4": "#bec6e0", - "surface1": "#313ba5", + "surface1": "#333ea8", "surface2": "#33c", - "surface3": "#3843ba", + "surface3": "#3a46bd", "surface4": "#3b47be", - "surface5": "#3e4dc2", + "surface5": "#4050c5", "surface6": "#485ac4", }, "warnings": undefined, @@ -9713,25 +9338,23 @@ exports[`buildRamps > background ramp snapshots at lightness 50 1`] = ` "ramp": { "bgFill1": "#6e006f", "bgFill2": "#570059", - "bgFillDark": "#000", "bgFillInverted1": "#370038", "bgFillInverted2": "#000", "fgFill": "#f2eff2", - "fgFillDark": "#f2eff2", "fgFillInverted": "#f2eff2", - "fgSurface1": "#6c5f6b", "fgSurface2": "#50464f", - "fgSurface3": "#332c33", - "fgSurface4": "#000", + "fgSurface3": "#352e34", + "fgSurface4": "#272127", + "fgSurface5": "#000", "stroke1": "#c478c1", - "stroke2": "#b16cae", + "stroke2": "#b269af", "stroke3": "#643a62", "stroke4": "#40233f", - "surface1": "#db68da", + "surface1": "#dd6adc", "surface2": "#f25cf1", - "surface3": "#e679e3", - "surface4": "#d665d4", - "surface5": "#cd62cb", + "surface3": "#e87be5", + "surface4": "#d867d6", + "surface5": "#cb60c9", "surface6": "#ba5db9", }, "warnings": undefined, @@ -9748,25 +9371,23 @@ exports[`buildRamps > background ramp snapshots at lightness 50 1`] = ` "ramp": { "bgFill1": "#ffa4a0", "bgFill2": "#ffc0bc", - "bgFillDark": "#000", "bgFillInverted1": "#ffe3e0", "bgFillInverted2": "#fff", "fgFill": "#231c1c", - "fgFillDark": "#f2efef", "fgFillInverted": "#231c1c", - "fgSurface1": "#9c8482", - "fgSurface2": "#b5a7a5", - "fgSurface3": "#d5cdcd", - "fgSurface4": "#fff", + "fgSurface2": "#b6a8a6", + "fgSurface3": "#d6cfce", + "fgSurface4": "#ebe7e6", + "fgSurface5": "#fefefe", "stroke1": "#854a49", - "stroke2": "#965553", - "stroke3": "#d49c99", + "stroke2": "#945755", + "stroke3": "#d59d9a", "stroke4": "#e7cac8", "surface1": "#833636", "surface2": "#943134", - "surface3": "#933e3e", - "surface4": "#984040", - "surface5": "#9e4444", + "surface3": "#943e3e", + "surface4": "#973f3f", + "surface5": "#a14646", "surface6": "#ab4d4d", }, "warnings": undefined, @@ -9783,25 +9404,23 @@ exports[`buildRamps > background ramp snapshots at lightness 50 1`] = ` "ramp": { "bgFill1": "#4d4c00", "bgFill2": "#403e00", - "bgFillDark": "#1d1d1a", "bgFillInverted1": "#2f2e00", "bgFillInverted2": "#1d1d1a", "fgFill": "#f1f2e1", - "fgFillDark": "#f1f2e1", "fgFillInverted": "#f1f2e1", - "fgSurface1": "#71726a", "fgSurface2": "#575751", "fgSurface3": "#3d3d38", - "fgSurface4": "#1d1d1a", + "fgSurface4": "#33332f", + "fgSurface5": "#000", "stroke1": "#a6a777", - "stroke2": "#97986c", + "stroke2": "#979869", "stroke3": "#58583d", "stroke4": "#3d3d29", "surface1": "#aaab61", "surface2": "#b3b34d", "surface3": "#b8b96a", - "surface4": "#a6a65f", - "surface5": "#a0a15d", + "surface4": "#a8a861", + "surface5": "#9d9e5a", "surface6": "#949458", }, "warnings": undefined, @@ -9818,25 +9437,23 @@ exports[`buildRamps > background ramp snapshots at lightness 50 1`] = ` "ramp": { "bgFill1": "#004900", "bgFill2": "#003900", - "bgFillDark": "#040504", "bgFillInverted1": "#002400", "bgFillInverted2": "#040504", "fgFill": "#ecf2eb", - "fgFillDark": "#ecf2eb", "fgFillInverted": "#ecf2eb", - "fgSurface1": "#5e675d", "fgSurface2": "#464c45", - "fgSurface3": "#2d322d", - "fgSurface4": "#040504", + "fgSurface3": "#2d312c", + "fgSurface4": "#222622", + "fgSurface5": "#000", "stroke1": "#6fa16d", - "stroke2": "#649162", + "stroke2": "#61915f", "stroke3": "#365135", "stroke4": "#213420", "surface1": "#5ca85a", "surface2": "#4db34d", "surface3": "#64b662", - "surface4": "#59a458", - "surface5": "#579e55", + "surface4": "#5aa559", + "surface5": "#549b53", "surface6": "#529150", }, "warnings": undefined, @@ -9853,25 +9470,23 @@ exports[`buildRamps > background ramp snapshots at lightness 50 1`] = ` "ramp": { "bgFill1": "#004a4b", "bgFill2": "#003c3c", - "bgFillDark": "#0e1010", "bgFillInverted1": "#002929", "bgFillInverted2": "#0e1010", "fgFill": "#e9f2f2", - "fgFillDark": "#e9f2f2", "fgFillInverted": "#e9f2f2", - "fgSurface1": "#636b6b", "fgSurface2": "#4a5050", - "fgSurface3": "#313635", - "fgSurface4": "#0e1010", + "fgSurface3": "#313636", + "fgSurface4": "#282b2b", + "fgSurface5": "#000", "stroke1": "#74a3a2", - "stroke2": "#699393", - "stroke3": "#3a5453", + "stroke2": "#679494", + "stroke3": "#395352", "stroke4": "#253737", "surface1": "#60a9a8", "surface2": "#4db3b3", "surface3": "#69b7b6", - "surface4": "#5ea4a4", - "surface5": "#5c9e9e", + "surface4": "#60a6a6", + "surface5": "#5a9c9c", "surface6": "#579291", }, "warnings": undefined, @@ -9888,25 +9503,23 @@ exports[`buildRamps > background ramp snapshots at lightness 50 1`] = ` "ramp": { "bgFill1": "#afb8ff", "bgFill2": "#c5cdff", - "bgFillDark": "#000", "bgFillInverted1": "#e2e8ff", "bgFillInverted2": "#fff", "fgFill": "#1c1e25", - "fgFillDark": "#f0f0f1", "fgFillInverted": "#1c1e25", - "fgSurface1": "#878997", "fgSurface2": "#a8aab2", "fgSurface3": "#cecfd4", - "fgSurface4": "#fff", + "fgSurface4": "#e7e7ea", + "fgSurface5": "#fdfdfe", "stroke1": "#505597", - "stroke2": "#5d62a2", + "stroke2": "#5d61a4", "stroke3": "#a3a8ca", "stroke4": "#cdd0e2", "surface1": "#44449f", "surface2": "#4847ac", "surface3": "#4c4daf", - "surface4": "#5051b3", - "surface5": "#5456b7", + "surface4": "#4f50b2", + "surface5": "#5658b9", "surface6": "#5e62b9", }, "warnings": undefined, @@ -9923,25 +9536,23 @@ exports[`buildRamps > background ramp snapshots at lightness 50 1`] = ` "ramp": { "bgFill1": "#6d006f", "bgFill2": "#570059", - "bgFillDark": "#000", "bgFillInverted1": "#370038", "bgFillInverted2": "#000", "fgFill": "#f2eff2", - "fgFillDark": "#f2eff2", "fgFillInverted": "#f2eff2", - "fgSurface1": "#6a5f6a", "fgSurface2": "#4f464f", - "fgSurface3": "#332d32", - "fgSurface4": "#000", + "fgSurface3": "#332d33", + "fgSurface4": "#262126", + "fgSurface5": "#000", "stroke1": "#bc7dbb", - "stroke2": "#aa70a9", + "stroke2": "#ab6eaa", "stroke3": "#603d5f", "stroke4": "#3d253c", - "surface1": "#d172d0", + "surface1": "#d273d1", "surface2": "#dc74db", - "surface3": "#dc80db", - "surface4": "#cc6ecb", - "surface5": "#c46bc3", + "surface3": "#dd81dc", + "surface4": "#cd6fcc", + "surface5": "#c168c0", "surface6": "#b264b1", }, "warnings": undefined, @@ -9958,25 +9569,23 @@ exports[`buildRamps > background ramp snapshots at lightness 50 1`] = ` "ramp": { "bgFill1": "#e7afae", "bgFill2": "#fbc2c1", - "bgFillDark": "#000", "bgFillInverted1": "#ffe2e2", "bgFillInverted2": "#fff", "fgFill": "#211d1d", - "fgFillDark": "#f1f0ef", "fgFillInverted": "#211d1d", - "fgSurface1": "#968685", - "fgSurface2": "#b2a7a7", - "fgSurface3": "#d3cece", - "fgSurface4": "#fff", + "fgSurface2": "#b3a7a7", + "fgSurface3": "#d4cece", + "fgSurface4": "#eae7e7", + "fgSurface5": "#fefefe", "stroke1": "#795050", - "stroke2": "#895b5b", + "stroke2": "#875d5d", "stroke3": "#c9a09f", "stroke4": "#e1cccb", "surface1": "#753f40", "surface2": "#774747", "surface3": "#834849", - "surface4": "#884b4c", - "surface5": "#8d4f50", + "surface4": "#874a4b", + "surface5": "#905152", "surface6": "#9a5859", }, "warnings": undefined, @@ -9993,25 +9602,23 @@ exports[`buildRamps > background ramp snapshots at lightness 50 1`] = ` "ramp": { "bgFill1": "#3e3d08", "bgFill2": "#312f00", - "bgFillDark": "#000", "bgFillInverted1": "#1e1c00", "bgFillInverted2": "#000", "fgFill": "#f1f1e4", - "fgFillDark": "#f1f1e4", "fgFillInverted": "#f1f1e4", - "fgSurface1": "#63635d", "fgSurface2": "#494945", - "fgSurface3": "#2e2f2b", - "fgSurface4": "#000", + "fgSurface3": "#2e2e2b", + "fgSurface4": "#232321", + "fgSurface5": "#000", "stroke1": "#949573", - "stroke2": "#868668", + "stroke2": "#878766", "stroke3": "#4a4a38", "stroke4": "#2e2e22", "surface1": "#989866", "surface2": "#9e9e6b", "surface3": "#a5a56f", - "surface4": "#949463", - "surface5": "#8e8e60", + "surface4": "#959564", + "surface5": "#8c8c5e", "surface6": "#83835a", }, "warnings": undefined, @@ -10028,25 +9635,23 @@ exports[`buildRamps > background ramp snapshots at lightness 50 1`] = ` "ramp": { "bgFill1": "#124516", "bgFill2": "#003707", - "bgFillDark": "#000", "bgFillInverted1": "#002100", "bgFillInverted2": "#000", "fgFill": "#edf2ed", - "fgFillDark": "#edf2ed", "fgFillInverted": "#edf2ed", - "fgSurface1": "#5e655e", "fgSurface2": "#454a45", "fgSurface3": "#2c2f2c", - "fgSurface4": "#000", + "fgSurface4": "#212320", + "fgSurface5": "#000", "stroke1": "#779c76", - "stroke2": "#6b8d6b", - "stroke3": "#3a4e3a", + "stroke2": "#698e69", + "stroke3": "#394d39", "stroke4": "#233123", "surface1": "#6ba26b", "surface2": "#74a874", "surface3": "#74b074", - "surface4": "#689e68", - "surface5": "#659865", + "surface4": "#699f69", + "surface5": "#639563", "surface6": "#5e8b5e", }, "warnings": undefined, @@ -10063,25 +9668,23 @@ exports[`buildRamps > background ramp snapshots at lightness 50 1`] = ` "ramp": { "bgFill1": "#0a4243", "bgFill2": "#003435", - "bgFillDark": "#000", "bgFillInverted1": "#001f20", "bgFillInverted2": "#000", "fgFill": "#eaf2f2", - "fgFillDark": "#eaf2f2", "fgFillInverted": "#eaf2f2", - "fgSurface1": "#5e6464", "fgSurface2": "#454a4a", "fgSurface3": "#2c2f2f", - "fgSurface4": "#000", + "fgSurface4": "#212424", + "fgSurface5": "#000", "stroke1": "#799", - "stroke2": "#6c8a8a", + "stroke2": "#6a8b8b", "stroke3": "#3a4c4c", "stroke4": "#23302f", "surface1": "#6b9e9e", "surface2": "#71a4a4", "surface3": "#74abab", - "surface4": "#689a9a", - "surface5": "#659494", + "surface4": "#699b9b", + "surface5": "#639292", "surface6": "#5e8888", }, "warnings": undefined, @@ -10098,25 +9701,23 @@ exports[`buildRamps > background ramp snapshots at lightness 50 1`] = ` "ramp": { "bgFill1": "#b6b8f0", "bgFill2": "#caccff", - "bgFillDark": "#000", "bgFillInverted1": "#e5e7ff", "bgFillInverted2": "#fff", "fgFill": "#1d1e23", - "fgFillDark": "#f0f0f1", "fgFillInverted": "#1d1e23", - "fgSurface1": "#888993", "fgSurface2": "#a9a9b0", - "fgSurface3": "#cfcfd3", - "fgSurface4": "#fff", + "fgSurface3": "#cdced1", + "fgSurface4": "#e7e7e9", + "fgSurface5": "#fdfdfe", "stroke1": "#575685", "stroke2": "#636393", "stroke3": "#a7a8c2", "stroke4": "#cfd0de", - "surface1": "#4b4886", + "surface1": "#4a4785", "surface2": "#504f80", - "surface3": "#545195", - "surface4": "#57549a", - "surface5": "#5b599e", + "surface3": "#535094", + "surface4": "#565399", + "surface5": "#5d5ba0", "surface6": "#6564a4", }, "warnings": undefined, @@ -10133,25 +9734,23 @@ exports[`buildRamps > background ramp snapshots at lightness 50 1`] = ` "ramp": { "bgFill1": "#582959", "bgFill2": "#491b4a", - "bgFillDark": "#000", "bgFillInverted1": "#340535", "bgFillInverted2": "#000", "fgFill": "#f1eff1", - "fgFillDark": "#f1eff1", "fgFillInverted": "#f1eff1", - "fgSurface1": "#686068", "fgSurface2": "#4d474d", - "fgSurface3": "#312d31", - "fgSurface4": "#000", + "fgSurface3": "#302c30", + "fgSurface4": "#242124", + "fgSurface5": "#000", "stroke1": "#b184b1", - "stroke2": "#a077a0", - "stroke3": "#5a4159", + "stroke2": "#a176a1", + "stroke3": "#594058", "stroke4": "#392839", - "surface1": "#c17ec2", + "surface1": "#c07dc1", "surface2": "#bf8abf", - "surface3": "#cd8bcd", - "surface4": "#bb7abc", - "surface5": "#b476b4", + "surface3": "#cc8acc", + "surface4": "#bc7bbd", + "surface5": "#b173b1", "surface6": "#a56da5", }, "warnings": undefined, @@ -10168,25 +9767,23 @@ exports[`buildRamps > background ramp snapshots at lightness 50 1`] = ` "ramp": { "bgFill1": "#3b3b3b", "bgFill2": "#2e2e2e", - "bgFillDark": "#000", "bgFillInverted1": "#1b1b1b", "bgFillInverted2": "#000", "fgFill": "#f0f0f0", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", - "fgSurface1": "#636363", "fgSurface2": "#494949", "fgSurface3": "#2e2e2e", - "fgSurface4": "#000", + "fgSurface4": "#222", + "fgSurface5": "#000", "stroke1": "#939393", "stroke2": "#848484", - "stroke3": "#494949", + "stroke3": "#484848", "stroke4": "#2d2d2d", "surface1": "#959595", "surface2": "#9b9b9b", "surface3": "#a2a2a2", - "surface4": "#919191", - "surface5": "#8c8c8c", + "surface4": "#929292", + "surface5": "#898989", "surface6": "#808080", }, "warnings": undefined, @@ -10203,25 +9800,23 @@ exports[`buildRamps > background ramp snapshots at lightness 50 1`] = ` "ramp": { "bgFill1": "#3b3b3b", "bgFill2": "#2e2e2e", - "bgFillDark": "#000", "bgFillInverted1": "#1b1b1b", "bgFillInverted2": "#000", "fgFill": "#f0f0f0", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", - "fgSurface1": "#636363", "fgSurface2": "#494949", "fgSurface3": "#2e2e2e", - "fgSurface4": "#000", + "fgSurface4": "#222", + "fgSurface5": "#000", "stroke1": "#939393", "stroke2": "#848484", - "stroke3": "#494949", + "stroke3": "#484848", "stroke4": "#2d2d2d", "surface1": "#959595", "surface2": "#9b9b9b", "surface3": "#a2a2a2", - "surface4": "#919191", - "surface5": "#8c8c8c", + "surface4": "#929292", + "surface5": "#898989", "surface6": "#808080", }, "warnings": undefined, @@ -10238,25 +9833,23 @@ exports[`buildRamps > background ramp snapshots at lightness 50 1`] = ` "ramp": { "bgFill1": "#3b3b3b", "bgFill2": "#2e2e2e", - "bgFillDark": "#000", "bgFillInverted1": "#1b1b1b", "bgFillInverted2": "#000", "fgFill": "#f0f0f0", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", - "fgSurface1": "#636363", "fgSurface2": "#494949", "fgSurface3": "#2e2e2e", - "fgSurface4": "#000", + "fgSurface4": "#222", + "fgSurface5": "#000", "stroke1": "#939393", "stroke2": "#848484", - "stroke3": "#494949", + "stroke3": "#484848", "stroke4": "#2d2d2d", "surface1": "#959595", "surface2": "#9b9b9b", "surface3": "#a2a2a2", - "surface4": "#919191", - "surface5": "#8c8c8c", + "surface4": "#929292", + "surface5": "#898989", "surface6": "#808080", }, "warnings": undefined, @@ -10273,25 +9866,23 @@ exports[`buildRamps > background ramp snapshots at lightness 50 1`] = ` "ramp": { "bgFill1": "#3b3b3b", "bgFill2": "#2e2e2e", - "bgFillDark": "#000", "bgFillInverted1": "#1b1b1b", "bgFillInverted2": "#000", "fgFill": "#f0f0f0", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", - "fgSurface1": "#636363", "fgSurface2": "#494949", "fgSurface3": "#2e2e2e", - "fgSurface4": "#000", + "fgSurface4": "#222", + "fgSurface5": "#000", "stroke1": "#939393", "stroke2": "#848484", - "stroke3": "#494949", + "stroke3": "#484848", "stroke4": "#2d2d2d", "surface1": "#959595", "surface2": "#9b9b9b", "surface3": "#a2a2a2", - "surface4": "#919191", - "surface5": "#8c8c8c", + "surface4": "#929292", + "surface5": "#898989", "surface6": "#808080", }, "warnings": undefined, @@ -10308,25 +9899,23 @@ exports[`buildRamps > background ramp snapshots at lightness 50 1`] = ` "ramp": { "bgFill1": "#3b3b3b", "bgFill2": "#2e2e2e", - "bgFillDark": "#000", "bgFillInverted1": "#1b1b1b", "bgFillInverted2": "#000", "fgFill": "#f0f0f0", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", - "fgSurface1": "#636363", "fgSurface2": "#494949", "fgSurface3": "#2e2e2e", - "fgSurface4": "#000", + "fgSurface4": "#222", + "fgSurface5": "#000", "stroke1": "#939393", "stroke2": "#848484", - "stroke3": "#494949", + "stroke3": "#484848", "stroke4": "#2d2d2d", "surface1": "#959595", "surface2": "#9b9b9b", "surface3": "#a2a2a2", - "surface4": "#919191", - "surface5": "#8c8c8c", + "surface4": "#929292", + "surface5": "#898989", "surface6": "#808080", }, "warnings": undefined, @@ -10343,25 +9932,23 @@ exports[`buildRamps > background ramp snapshots at lightness 50 1`] = ` "ramp": { "bgFill1": "#3b3b3b", "bgFill2": "#2e2e2e", - "bgFillDark": "#000", "bgFillInverted1": "#1b1b1b", "bgFillInverted2": "#000", "fgFill": "#f0f0f0", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", - "fgSurface1": "#636363", "fgSurface2": "#494949", "fgSurface3": "#2e2e2e", - "fgSurface4": "#000", + "fgSurface4": "#222", + "fgSurface5": "#000", "stroke1": "#939393", "stroke2": "#848484", - "stroke3": "#494949", + "stroke3": "#484848", "stroke4": "#2d2d2d", "surface1": "#959595", "surface2": "#9b9b9b", "surface3": "#a2a2a2", - "surface4": "#919191", - "surface5": "#8c8c8c", + "surface4": "#929292", + "surface5": "#898989", "surface6": "#808080", }, "warnings": undefined, @@ -10383,25 +9970,23 @@ exports[`buildRamps > background ramp snapshots at lightness 60 1`] = ` "ramp": { "bgFill1": "#7e0000", "bgFill2": "#640000", - "bgFillDark": "#000", "bgFillInverted1": "#400000", "bgFillInverted2": "#000", "fgFill": "#f2efef", - "fgFillDark": "#f2efef", "fgFillInverted": "#f2efef", - "fgSurface1": "#715f5c", "fgSurface2": "#544543", - "fgSurface3": "#362c2b", - "fgSurface4": "#000", + "fgSurface3": "#382e2d", + "fgSurface4": "#2a2120", + "fgSurface5": "#000", "stroke1": "#d07c73", - "stroke2": "#c66a61", + "stroke2": "#c06c63", "stroke3": "#713833", "stroke4": "#49221e", - "surface1": "#e77368", + "surface1": "#e9756a", "surface2": "#ff6c61", - "surface3": "#e9867b", - "surface4": "#e56c61", - "surface5": "#e36359", + "surface3": "#eb887d", + "surface4": "#e76e63", + "surface5": "#e16157", "surface6": "#d6574e", }, "warnings": undefined, @@ -10418,25 +10003,23 @@ exports[`buildRamps > background ramp snapshots at lightness 60 1`] = ` "ramp": { "bgFill1": "#5b5900", "bgFill2": "#4e4c00", - "bgFillDark": "#1e1e1b", "bgFillInverted1": "#302f00", "bgFillInverted2": "#1e1e1b", "fgFill": "#f5f896", - "fgFillDark": "#f5f896", "fgFillInverted": "#f5f896", - "fgSurface1": "#aaaa9d", "fgSurface2": "#898a7f", - "fgSurface3": "#6c6d64", - "fgSurface4": "#1e1e1b", + "fgSurface3": "#6c6c64", + "fgSurface4": "#494a44", + "fgSurface5": "#141412", "stroke1": "#edefa4", - "stroke2": "#d9db96", - "stroke3": "#8b8c5e", + "stroke2": "#d9db92", + "stroke3": "#8a8b5d", "stroke4": "#6c6e49", - "surface1": "#f3f580", + "surface1": "#f2f47f", "surface2": "#ff3", - "surface3": "#fff", - "surface4": "#edef7e", - "surface5": "#e5e77d", + "surface3": "#fefefe", + "surface4": "#eef07f", + "surface5": "#e2e47a", "surface6": "#d5d77a", }, "warnings": undefined, @@ -10453,25 +10036,23 @@ exports[`buildRamps > background ramp snapshots at lightness 60 1`] = ` "ramp": { "bgFill1": "#006b00", "bgFill2": "#005c00", - "bgFillDark": "#1c1f1b", "bgFillInverted1": "#003700", "bgFillInverted2": "#1c1f1b", "fgFill": "#dff9dc", - "fgFillDark": "#dff9dc", "fgFillInverted": "#dff9dc", - "fgSurface1": "#8d9a8b", "fgSurface2": "#707b6f", - "fgSurface3": "#565f55", - "fgSurface4": "#1c1f1b", + "fgSurface3": "#555d54", + "fgSurface4": "#4c554c", + "fgSurface5": "#121512", "stroke1": "#97e592", - "stroke2": "#8ad185", - "stroke3": "#558351", + "stroke2": "#86d180", + "stroke3": "#53814f", "stroke4": "#40653d", - "surface1": "#77ef71", + "surface1": "#74ec6e", "surface2": "#3f3", - "surface3": "#a1fb9b", - "surface4": "#74e86e", - "surface5": "#72e16c", + "surface3": "#9ef898", + "surface4": "#75e96f", + "surface5": "#6edd68", "surface6": "#6fd069", }, "warnings": undefined, @@ -10488,25 +10069,23 @@ exports[`buildRamps > background ramp snapshots at lightness 60 1`] = ` "ramp": { "bgFill1": "#006566", "bgFill2": "#005758", - "bgFillDark": "#1c1f1f", "bgFillInverted1": "#003435", "bgFillInverted2": "#1c1f1f", "fgFill": "#c8fcfb", - "fgFillDark": "#c8fcfb", "fgFillInverted": "#c8fcfb", - "fgSurface1": "#939f9f", "fgSurface2": "#768080", - "fgSurface3": "#5c6464", - "fgSurface4": "#1c1f1f", + "fgSurface3": "#5a6362", + "fgSurface4": "#525a5a", + "fgSurface5": "#121515", "stroke1": "#9fe7e6", - "stroke2": "#91d3d3", - "stroke3": "#5a8685", + "stroke2": "#8ed4d4", + "stroke3": "#588483", "stroke4": "#456867", - "surface1": "#7ef0ef", + "surface1": "#7ceeed", "surface2": "#3ff", - "surface3": "#a6fefd", - "surface4": "#7ceae9", - "surface5": "#7ae2e2", + "surface3": "#a4fcfb", + "surface4": "#7debea", + "surface5": "#77dfdf", "surface6": "#76d2d1", }, "warnings": undefined, @@ -10523,25 +10102,23 @@ exports[`buildRamps > background ramp snapshots at lightness 60 1`] = ` "ramp": { "bgFill1": "#a1bbff", "bgFill2": "#bacfff", - "bgFillDark": "#000", "bgFillInverted1": "#dde9ff", "bgFillInverted2": "#fff", "fgFill": "#1a1e27", - "fgFillDark": "#eff0f2", "fgFillInverted": "#1a1e27", - "fgSurface1": "#82899f", - "fgSurface2": "#a6aab5", - "fgSurface3": "#cdcfd5", - "fgSurface4": "#fff", + "fgSurface2": "#acb0bb", + "fgSurface3": "#d4d6db", + "fgSurface4": "#e0e1e5", + "fgSurface5": "#fdfdfe", "stroke1": "#3c53b5", - "stroke2": "#4d63b2", - "stroke3": "#9ba9d4", + "stroke2": "#5169c8", + "stroke3": "#a1afdb", "stroke4": "#c9d1e8", - "surface1": "#2d3ec0", + "surface1": "#3043c5", "surface2": "#3027f7", - "surface3": "#3549ce", - "surface4": "#384dd0", - "surface5": "#3c54d0", + "surface3": "#384ed3", + "surface4": "#3a50d3", + "surface5": "#3f58d5", "surface6": "#4862d1", }, "warnings": undefined, @@ -10558,25 +10135,23 @@ exports[`buildRamps > background ramp snapshots at lightness 60 1`] = ` "ramp": { "bgFill1": "#6e006f", "bgFill2": "#570059", - "bgFillDark": "#000", "bgFillInverted1": "#370038", "bgFillInverted2": "#000", "fgFill": "#f2eff2", - "fgFillDark": "#f2eff2", "fgFillInverted": "#f2eff2", - "fgSurface1": "#6c5e6c", "fgSurface2": "#504550", - "fgSurface3": "#342c33", - "fgSurface4": "#000", + "fgSurface3": "#372f37", + "fgSurface4": "#282228", + "fgSurface5": "#000", "stroke1": "#c874c5", - "stroke2": "#b569b3", + "stroke2": "#b766b5", "stroke3": "#663865", "stroke4": "#412240", - "surface1": "#e162df", + "surface1": "#e566e3", "surface2": "#ff47ff", - "surface3": "#eb75e9", - "surface4": "#dc5fda", - "surface5": "#d35cd1", + "surface3": "#ef79ed", + "surface4": "#df62dd", + "surface5": "#d15acf", "surface6": "#bf58be", }, "warnings": undefined, @@ -10593,25 +10168,23 @@ exports[`buildRamps > background ramp snapshots at lightness 60 1`] = ` "ramp": { "bgFill1": "#7d0004", "bgFill2": "#640001", - "bgFillDark": "#000", "bgFillInverted1": "#400000", "bgFillInverted2": "#000", "fgFill": "#f2efef", - "fgFillDark": "#f2efef", "fgFillInverted": "#f2efef", - "fgSurface1": "#715e5d", "fgSurface2": "#544544", - "fgSurface3": "#362c2b", - "fgSurface4": "#000", + "fgSurface3": "#382e2d", + "fgSurface4": "#292120", + "fgSurface5": "#000", "stroke1": "#d07c75", - "stroke2": "#c66a64", + "stroke2": "#c06c66", "stroke3": "#713835", "stroke4": "#49221f", - "surface1": "#e7726c", + "surface1": "#e9746e", "surface2": "#ff6b66", - "surface3": "#e9857e", - "surface4": "#e56b65", - "surface5": "#e3625d", + "surface3": "#eb8780", + "surface4": "#e86d67", + "surface5": "#e1605b", "surface6": "#d65753", }, "warnings": undefined, @@ -10628,25 +10201,23 @@ exports[`buildRamps > background ramp snapshots at lightness 60 1`] = ` "ramp": { "bgFill1": "#5b5900", "bgFill2": "#4e4c00", - "bgFillDark": "#1e1e1b", "bgFillInverted1": "#302f00", "bgFillInverted2": "#1e1e1b", "fgFill": "#f5f7a2", - "fgFillDark": "#f5f7a2", "fgFillInverted": "#f5f7a2", - "fgSurface1": "#9b9c90", "fgSurface2": "#7c7d73", - "fgSurface3": "#616159", - "fgSurface4": "#1e1e1b", + "fgSurface3": "#61615a", + "fgSurface4": "#575750", + "fgSurface5": "#141412", "stroke1": "#dadc9a", - "stroke2": "#c8c98c", - "stroke3": "#7e7f57", + "stroke2": "#c9c989", + "stroke3": "#7d7e56", "stroke4": "#616242", "surface1": "#e0e17a", "surface2": "#ebeb47", "surface3": "#f1f284", - "surface4": "#dadb78", - "surface5": "#d3d476", + "surface4": "#dcdd7a", + "surface5": "#d0d173", "surface6": "#c4c572", }, "warnings": undefined, @@ -10663,25 +10234,23 @@ exports[`buildRamps > background ramp snapshots at lightness 60 1`] = ` "ramp": { "bgFill1": "#006b00", "bgFill2": "#005c00", - "bgFillDark": "#1c1f1b", "bgFillInverted1": "#003700", "bgFillInverted2": "#1c1f1b", "fgFill": "#e6f5e5", - "fgFillDark": "#e6f5e5", "fgFillInverted": "#e6f5e5", - "fgSurface1": "#818d80", "fgSurface2": "#656f65", - "fgSurface3": "#4d544c", - "fgSurface4": "#1c1f1b", + "fgSurface3": "#4c534b", + "fgSurface4": "#434a43", + "fgSurface5": "#111310", "stroke1": "#8ed28a", - "stroke2": "#81c07d", - "stroke3": "#4e764b", + "stroke2": "#7ec07a", + "stroke3": "#4d754a", "stroke4": "#395937", - "surface1": "#72dc6e", + "surface1": "#71db6d", "surface2": "#47eb47", - "surface3": "#7ced77", - "surface4": "#70d66b", - "surface5": "#6dcf69", + "surface3": "#7bec76", + "surface4": "#71d76c", + "surface5": "#6acc66", "surface6": "#69c065", }, "warnings": undefined, @@ -10698,25 +10267,23 @@ exports[`buildRamps > background ramp snapshots at lightness 60 1`] = ` "ramp": { "bgFill1": "#006566", "bgFill2": "#005758", - "bgFillDark": "#1c1f1f", "bgFillInverted1": "#003435", "bgFillInverted2": "#1c1f1f", "fgFill": "#d7f8f7", - "fgFillDark": "#d7f8f7", "fgFillInverted": "#d7f8f7", - "fgSurface1": "#879292", "fgSurface2": "#6b7474", - "fgSurface3": "#525959", - "fgSurface4": "#1c1f1f", + "fgSurface3": "#515858", + "fgSurface4": "#484f4f", + "fgSurface5": "#121515", "stroke1": "#95d5d4", - "stroke2": "#88c3c2", - "stroke3": "#537979", + "stroke2": "#85c4c3", + "stroke3": "#527878", "stroke4": "#3e5c5b", - "surface1": "#79dddd", + "surface1": "#78dcdc", "surface2": "#47ebeb", - "surface3": "#83eeee", - "surface4": "#76d8d7", - "surface5": "#74d0d0", + "surface3": "#82eded", + "surface4": "#77d9d8", + "surface5": "#71cdcd", "surface6": "#70c1c0", }, "warnings": undefined, @@ -10733,25 +10300,23 @@ exports[`buildRamps > background ramp snapshots at lightness 60 1`] = ` "ramp": { "bgFill1": "#a8baff", "bgFill2": "#c0ceff", - "bgFillDark": "#000", "bgFillInverted1": "#dfe9ff", "bgFillInverted2": "#fff", "fgFill": "#1b1e26", - "fgFillDark": "#eff0f2", "fgFillInverted": "#1b1e26", - "fgSurface1": "#84899c", - "fgSurface2": "#a7aab4", - "fgSurface3": "#cecfd5", - "fgSurface4": "#fff", + "fgSurface2": "#abaeb8", + "fgSurface3": "#d2d4d9", + "fgSurface4": "#e7e7ea", + "fgSurface5": "#fdfdfe", "stroke1": "#4854a4", - "stroke2": "#5562ad", - "stroke3": "#9fa9d1", + "stroke2": "#5562b3", + "stroke3": "#a3add5", "stroke4": "#cbd1e6", - "surface1": "#3a40b4", + "surface1": "#3c43b7", "surface2": "#3e37db", - "surface3": "#424ac3", - "surface4": "#454dc7", - "surface5": "#4954c8", + "surface3": "#444dc6", + "surface4": "#464ec8", + "surface5": "#4b57cb", "surface6": "#5461c9", }, "warnings": undefined, @@ -10768,25 +10333,23 @@ exports[`buildRamps > background ramp snapshots at lightness 60 1`] = ` "ramp": { "bgFill1": "#6d006f", "bgFill2": "#570058", - "bgFillDark": "#000", "bgFillInverted1": "#370038", "bgFillInverted2": "#000", "fgFill": "#f2eff2", - "fgFillDark": "#f2eff2", "fgFillInverted": "#f2eff2", - "fgSurface1": "#6c5f6b", "fgSurface2": "#50464f", - "fgSurface3": "#342c33", - "fgSurface4": "#000", + "fgSurface3": "#362f36", + "fgSurface4": "#272127", + "fgSurface5": "#000", "stroke1": "#c577c3", - "stroke2": "#b26bb0", + "stroke2": "#b368b1", "stroke3": "#653a63", "stroke4": "#402340", - "surface1": "#de66dc", + "surface1": "#e169df", "surface2": "#f754f6", - "surface3": "#e777e5", - "surface4": "#d863d6", - "surface5": "#cf60cd", + "surface3": "#ea7ae8", + "surface4": "#db65d8", + "surface5": "#cd5ecb", "surface6": "#bc5bba", }, "warnings": undefined, @@ -10803,25 +10366,23 @@ exports[`buildRamps > background ramp snapshots at lightness 60 1`] = ` "ramp": { "bgFill1": "#7d0013", "bgFill2": "#640006", - "bgFillDark": "#000", "bgFillInverted1": "#400001", "bgFillInverted2": "#000", "fgFill": "#f2efef", - "fgFillDark": "#f2efef", "fgFillInverted": "#f2efef", - "fgSurface1": "#705f5e", "fgSurface2": "#534645", - "fgSurface3": "#352c2c", - "fgSurface4": "#000", + "fgSurface3": "#372d2d", + "fgSurface4": "#282120", + "fgSurface5": "#000", "stroke1": "#cc7e7a", - "stroke2": "#c16c6a", + "stroke2": "#bd6e6c", "stroke3": "#6e3a39", "stroke4": "#472322", - "surface1": "#e17673", + "surface1": "#e27774", "surface2": "#f27573", - "surface3": "#e58885", - "surface4": "#e06f6d", - "surface5": "#de6766", + "surface3": "#e68986", + "surface4": "#e2706e", + "surface5": "#db6463", "surface6": "#d05b5b", }, "warnings": undefined, @@ -10838,25 +10399,23 @@ exports[`buildRamps > background ramp snapshots at lightness 60 1`] = ` "ramp": { "bgFill1": "#5b5900", "bgFill2": "#4e4c00", - "bgFillDark": "#1e1e1b", "bgFillInverted1": "#312f00", "bgFillInverted2": "#1e1e1b", "fgFill": "#f3f4c3", - "fgFillDark": "#f3f4c3", "fgFillInverted": "#f3f4c3", - "fgSurface1": "#8c8d83", "fgSurface2": "#6f7068", "fgSurface3": "#55554f", - "fgSurface4": "#1e1e1b", + "fgSurface4": "#4b4b46", + "fgSurface5": "#141412", "stroke1": "#c8c990", - "stroke2": "#b6b783", + "stroke2": "#b6b780", "stroke3": "#717150", "stroke4": "#55553b", "surface1": "#cdce76", "surface2": "#d6d65c", "surface3": "#dcdd7f", - "surface4": "#c7c874", - "surface5": "#c1c271", + "surface4": "#c9ca76", + "surface5": "#bebf6e", "surface6": "#b3b46d", }, "warnings": undefined, @@ -10873,25 +10432,23 @@ exports[`buildRamps > background ramp snapshots at lightness 60 1`] = ` "ramp": { "bgFill1": "#006300", "bgFill2": "#050", - "bgFillDark": "#1c1f1c", "bgFillInverted1": "#003700", "bgFillInverted2": "#1c1f1c", "fgFill": "#ebf3ea", - "fgFillDark": "#ebf3ea", "fgFillInverted": "#ebf3ea", - "fgSurface1": "#768175", "fgSurface2": "#5c645b", - "fgSurface3": "#434a43", - "fgSurface4": "#1c1f1c", + "fgSurface3": "#424942", + "fgSurface4": "#3a3f39", + "fgSurface5": "#000", "stroke1": "#86c284", - "stroke2": "#7ab077", - "stroke3": "#486a46", + "stroke2": "#77b174", + "stroke3": "#476945", "stroke4": "#334e32", - "surface1": "#6fca6d", + "surface1": "#6ec96c", "surface2": "#5cd65c", - "surface3": "#79da76", - "surface4": "#6dc56b", - "surface5": "#6abe68", + "surface3": "#78d975", + "surface4": "#6ec66c", + "surface5": "#68bb66", "surface6": "#65b063", }, "warnings": undefined, @@ -10908,25 +10465,23 @@ exports[`buildRamps > background ramp snapshots at lightness 60 1`] = ` "ramp": { "bgFill1": "#006465", "bgFill2": "#005657", - "bgFillDark": "#1c1f1f", "bgFillInverted1": "#003435", "bgFillInverted2": "#1c1f1f", "fgFill": "#e4f4f4", - "fgFillDark": "#e4f4f4", "fgFillInverted": "#e4f4f4", - "fgSurface1": "#7b8585", "fgSurface2": "#616868", - "fgSurface3": "#484e4e", - "fgSurface4": "#1c1f1f", + "fgSurface3": "#474d4d", + "fgSurface4": "#3e4444", + "fgSurface5": "#000", "stroke1": "#8dc4c3", - "stroke2": "#80b2b2", - "stroke3": "#4c6d6c", + "stroke2": "#7eb3b3", + "stroke3": "#4b6c6b", "stroke4": "#375050", - "surface1": "#75cbcb", + "surface1": "#74caca", "surface2": "#5cd6d6", - "surface3": "#7fdbdb", - "surface4": "#72c6c5", - "surface5": "#70bfbf", + "surface3": "#7edada", + "surface4": "#73c7c6", + "surface5": "#6dbcbc", "surface6": "#6bb1b0", }, "warnings": undefined, @@ -10943,25 +10498,23 @@ exports[`buildRamps > background ramp snapshots at lightness 60 1`] = ` "ramp": { "bgFill1": "#aeb8ff", "bgFill2": "#c4cdff", - "bgFillDark": "#000", "bgFillInverted1": "#e2e8ff", "bgFillInverted2": "#fff", "fgFill": "#1c1d25", - "fgFillDark": "#f0f0f2", "fgFillInverted": "#1c1d25", - "fgSurface1": "#868998", - "fgSurface2": "#a8aab3", - "fgSurface3": "#cecfd4", - "fgSurface4": "#fff", + "fgSurface2": "#a9abb4", + "fgSurface3": "#d0d1d6", + "fgSurface4": "#e7e7ea", + "fgSurface5": "#fdfdfe", "stroke1": "#50549b", - "stroke2": "#5c62a6", - "stroke3": "#a3a8cd", + "stroke2": "#5c61a9", + "stroke3": "#a5aacf", "stroke4": "#cdd0e4", - "surface1": "#4342a4", + "surface1": "#4443a5", "surface2": "#4742bb", - "surface3": "#4c4bb6", - "surface4": "#4f4fb9", - "surface5": "#5354bd", + "surface3": "#4d4cb7", + "surface4": "#4f4eb8", + "surface5": "#5557c0", "surface6": "#5d61bf", }, "warnings": undefined, @@ -10978,25 +10531,23 @@ exports[`buildRamps > background ramp snapshots at lightness 60 1`] = ` "ramp": { "bgFill1": "#6d006f", "bgFill2": "#570059", - "bgFillDark": "#000", "bgFillInverted1": "#370038", "bgFillInverted2": "#000", "fgFill": "#f2eff2", - "fgFillDark": "#f2eff2", "fgFillInverted": "#f2eff2", - "fgSurface1": "#6b5f6a", "fgSurface2": "#4f464f", - "fgSurface3": "#332c33", - "fgSurface4": "#000", + "fgSurface3": "#342d34", + "fgSurface4": "#272126", + "fgSurface5": "#000", "stroke1": "#bf7bbe", - "stroke2": "#ad6eab", + "stroke2": "#af6cad", "stroke3": "#613c61", "stroke4": "#3e243e", - "surface1": "#d66ed5", + "surface1": "#d76fd6", "surface2": "#e66be5", - "surface3": "#e07edf", - "surface4": "#d06acf", - "surface5": "#c867c7", + "surface3": "#e17fe0", + "surface4": "#d26cd1", + "surface5": "#c565c4", "surface6": "#b561b5", }, "warnings": undefined, @@ -11013,25 +10564,23 @@ exports[`buildRamps > background ramp snapshots at lightness 60 1`] = ` "ramp": { "bgFill1": "#6b2227", "bgFill2": "#5b1219", - "bgFillDark": "#000", "bgFillInverted1": "#400005", "bgFillInverted2": "#000", "fgFill": "#f2efef", - "fgFillDark": "#f2efef", "fgFillInverted": "#f2efef", - "fgSurface1": "#6d605f", "fgSurface2": "#514646", - "fgSurface3": "#342d2c", - "fgSurface4": "#000", + "fgSurface3": "#342c2c", + "fgSurface4": "#272120", + "fgSurface5": "#000", "stroke1": "#c28281", - "stroke2": "#b77271", - "stroke3": "#673e3d", + "stroke2": "#b37472", + "stroke3": "#663d3c", "stroke4": "#422525", "surface1": "#d57c7c", "surface2": "#d88484", "surface3": "#d98e8d", - "surface4": "#d37777", - "surface5": "#d06f70", + "surface4": "#d47878", + "surface5": "#cd6c6d", "surface6": "#c16566", }, "warnings": undefined, @@ -11048,25 +10597,23 @@ exports[`buildRamps > background ramp snapshots at lightness 60 1`] = ` "ramp": { "bgFill1": "#595700", "bgFill2": "#4c4a00", - "bgFillDark": "#1e1e1c", "bgFillInverted1": "#312f00", "bgFillInverted2": "#1e1e1c", "fgFill": "#f1f2de", - "fgFillDark": "#f1f2de", "fgFillInverted": "#f1f2de", - "fgSurface1": "#7e7e76", "fgSurface2": "#62625c", - "fgSurface3": "#484844", - "fgSurface4": "#1e1e1c", + "fgSurface3": "#484843", + "fgSurface4": "#3f3f3a", + "fgSurface5": "#000", "stroke1": "#b6b689", - "stroke2": "#a5a67c", - "stroke3": "#636449", + "stroke2": "#a6a77b", + "stroke3": "#626348", "stroke4": "#484834", "surface1": "#baba75", "surface2": "#c2c270", "surface3": "#c9c97f", - "surface4": "#b5b572", - "surface5": "#afaf6f", + "surface4": "#b7b774", + "surface5": "#adad6d", "surface6": "#a2a26a", }, "warnings": undefined, @@ -11083,25 +10630,23 @@ exports[`buildRamps > background ramp snapshots at lightness 60 1`] = ` "ramp": { "bgFill1": "#005800", "bgFill2": "#004900", - "bgFillDark": "#1c1f1c", "bgFillInverted1": "#003700", "bgFillInverted2": "#1c1f1c", "fgFill": "#ecf2ec", - "fgFillDark": "#ecf2ec", "fgFillInverted": "#ecf2ec", - "fgSurface1": "#6d756c", "fgSurface2": "#535a53", - "fgSurface3": "#3b403a", - "fgSurface4": "#1c1f1c", + "fgSurface3": "#3b403b", + "fgSurface4": "#313631", + "fgSurface5": "#000", "stroke1": "#82b280", - "stroke2": "#75a174", + "stroke2": "#73a272", "stroke3": "#435f43", "stroke4": "#2e422d", "surface1": "#70ba70", "surface2": "#70c270", "surface3": "#7ac879", - "surface4": "#6db46d", - "surface5": "#6aae6a", + "surface4": "#6fb66f", + "surface5": "#68ac68", "surface6": "#64a164", }, "warnings": undefined, @@ -11118,25 +10663,23 @@ exports[`buildRamps > background ramp snapshots at lightness 60 1`] = ` "ramp": { "bgFill1": "#005859", "bgFill2": "#004a4b", - "bgFillDark": "#1c1f1f", "bgFillInverted1": "#003435", "bgFillInverted2": "#1c1f1f", "fgFill": "#e9f2f2", - "fgFillDark": "#e9f2f2", "fgFillInverted": "#e9f2f2", - "fgSurface1": "#707878", "fgSurface2": "#575d5d", - "fgSurface3": "#3e4343", - "fgSurface4": "#1c1f1f", + "fgSurface3": "#3e4242", + "fgSurface4": "#353939", + "fgSurface5": "#000", "stroke1": "#86b3b2", - "stroke2": "#7aa2a2", + "stroke2": "#78a3a3", "stroke3": "#476060", "stroke4": "#314444", "surface1": "#74b9b9", "surface2": "#70c2c2", "surface3": "#7ec8c8", - "surface4": "#72b4b4", - "surface5": "#6faeae", + "surface4": "#74b6b6", + "surface5": "#6dacac", "surface6": "#69a1a1", }, "warnings": undefined, @@ -11153,25 +10696,23 @@ exports[`buildRamps > background ramp snapshots at lightness 60 1`] = ` "ramp": { "bgFill1": "#36317c", "bgFill2": "#2a226c", - "bgFillDark": "#000", "bgFillInverted1": "#1b0955", "bgFillInverted2": "#000", "fgFill": "#f0f0f2", - "fgFillDark": "#f0f0f2", "fgFillInverted": "#f0f0f2", - "fgSurface1": "#606175", "fgSurface2": "#474857", "fgSurface3": "#2d2e39", - "fgSurface4": "#000", + "fgSurface4": "#22232c", + "fgSurface5": "#000", "stroke1": "#8c8ec3", - "stroke2": "#7d7fbb", + "stroke2": "#7d7fb4", "stroke3": "#434182", "stroke4": "#292855", "surface1": "#8d8fd8", "surface2": "#9293e8", "surface3": "#9a9cdd", - "surface4": "#888ad7", - "surface5": "#8384d4", + "surface4": "#898bd9", + "surface5": "#8182d2", "surface6": "#7778cd", }, "warnings": undefined, @@ -11188,25 +10729,23 @@ exports[`buildRamps > background ramp snapshots at lightness 60 1`] = ` "ramp": { "bgFill1": "#661768", "bgFill2": "#560158", - "bgFillDark": "#000", "bgFillInverted1": "#360039", "bgFillInverted2": "#000", "fgFill": "#f2eff2", - "fgFillDark": "#f2eff2", "fgFillInverted": "#f2eff2", - "fgSurface1": "#6a6069", "fgSurface2": "#4e474e", - "fgSurface3": "#322d32", - "fgSurface4": "#000", + "fgSurface3": "#322c31", + "fgSurface4": "#262226", + "fgSurface5": "#000", "stroke1": "#b880b7", - "stroke2": "#a773a6", + "stroke2": "#a872a7", "stroke3": "#5d3e5d", "stroke4": "#3b263b", "surface1": "#cb76cb", "surface2": "#d17ed0", "surface3": "#d785d6", - "surface4": "#c673c6", - "surface5": "#be6fbe", + "surface4": "#c774c7", + "surface5": "#bc6dbc", "surface6": "#ae68ad", }, "warnings": undefined, @@ -11223,25 +10762,23 @@ exports[`buildRamps > background ramp snapshots at lightness 60 1`] = ` "ramp": { "bgFill1": "#553233", "bgFill2": "#462526", - "bgFillDark": "#000", "bgFillInverted1": "#301113", "bgFillInverted2": "#000", "fgFill": "#f1f0f0", - "fgFillDark": "#f1f0f0", "fgFillInverted": "#f1f0f0", - "fgSurface1": "#6a6160", "fgSurface2": "#4f4747", - "fgSurface3": "#322d2d", - "fgSurface4": "#000", + "fgSurface3": "#312c2c", + "fgSurface4": "#262221", + "fgSurface5": "#000", "stroke1": "#b58787", - "stroke2": "#a77878", - "stroke3": "#5e4242", + "stroke2": "#a67979", + "stroke3": "#5d4141", "stroke4": "#3c2828", - "surface1": "#c38586", + "surface1": "#c28485", "surface2": "#bb9291", - "surface3": "#ca9595", - "surface4": "#c18081", - "surface5": "#bd797b", + "surface3": "#c99494", + "surface4": "#c28182", + "surface5": "#ba7779", "surface6": "#af6f71", }, "warnings": undefined, @@ -11258,25 +10795,23 @@ exports[`buildRamps > background ramp snapshots at lightness 60 1`] = ` "ramp": { "bgFill1": "#4a4924", "bgFill2": "#3d3c17", - "bgFillDark": "#1a1a18", "bgFillInverted1": "#2d2c05", "bgFillInverted2": "#1a1a18", "fgFill": "#f1f1e6", - "fgFillDark": "#f1f1e6", "fgFillInverted": "#f1f1e6", - "fgSurface1": "#6f706a", "fgSurface2": "#555551", "fgSurface3": "#3b3b38", - "fgSurface4": "#1a1a18", + "fgSurface4": "#30312e", + "fgSurface5": "#000", "stroke1": "#a3a484", - "stroke2": "#949478", - "stroke3": "#565644", + "stroke2": "#959576", + "stroke3": "#555543", "stroke4": "#3b3b2e", "surface1": "#a7a778", "surface2": "#adad85", "surface3": "#b5b582", - "surface4": "#a3a375", - "surface5": "#9d9d72", + "surface4": "#a4a476", + "surface5": "#9a9a70", "surface6": "#91916a", }, "warnings": undefined, @@ -11293,25 +10828,23 @@ exports[`buildRamps > background ramp snapshots at lightness 60 1`] = ` "ramp": { "bgFill1": "#244a26", "bgFill2": "#173c19", - "bgFillDark": "#0f100e", "bgFillInverted1": "#042a08", "bgFillInverted2": "#0f100e", "fgFill": "#edf1ed", - "fgFillDark": "#edf1ed", "fgFillInverted": "#edf1ed", - "fgSurface1": "#656b64", "fgSurface2": "#4c504b", "fgSurface3": "#323632", - "fgSurface4": "#0f100e", + "fgSurface4": "#282b28", + "fgSurface5": "#000", "stroke1": "#81a381", - "stroke2": "#749374", - "stroke3": "#415441", + "stroke2": "#729373", + "stroke3": "#405340", "stroke4": "#2a372a", "surface1": "#76a977", "surface2": "#85ad85", "surface3": "#81b781", - "surface4": "#73a474", - "surface5": "#709f70", + "surface4": "#75a676", + "surface5": "#6d9c6d", "surface6": "#689168", }, "warnings": undefined, @@ -11328,25 +10861,23 @@ exports[`buildRamps > background ramp snapshots at lightness 60 1`] = ` "ramp": { "bgFill1": "#234a4a", "bgFill2": "#153c3d", - "bgFillDark": "#121414", "bgFillInverted1": "#012b2c", "bgFillInverted2": "#121414", "fgFill": "#ebf2f2", - "fgFillDark": "#ebf2f2", "fgFillInverted": "#ebf2f2", - "fgSurface1": "#676c6c", "fgSurface2": "#4e5252", "fgSurface3": "#343737", - "fgSurface4": "#121414", + "fgSurface4": "#2a2d2d", + "fgSurface5": "#000", "stroke1": "#84a2a2", - "stroke2": "#779393", + "stroke2": "#769393", "stroke3": "#435454", "stroke4": "#2c3838", "surface1": "#79a8a8", "surface2": "#85adad", "surface3": "#83b6b6", - "surface4": "#76a3a3", - "surface5": "#729e9e", + "surface4": "#78a5a5", + "surface5": "#6f9b9b", "surface6": "#6b9191", }, "warnings": undefined, @@ -11363,25 +10894,23 @@ exports[`buildRamps > background ramp snapshots at lightness 60 1`] = ` "ramp": { "bgFill1": "#39385b", "bgFill2": "#2c2a4d", - "bgFillDark": "#000", "bgFillInverted1": "#1a1737", "bgFillInverted2": "#000", "fgFill": "#f0f0f1", - "fgFillDark": "#f0f0f1", "fgFillInverted": "#f0f0f1", - "fgSurface1": "#61626f", "fgSurface2": "#484853", - "fgSurface3": "#2e2e35", - "fgSurface4": "#000", + "fgSurface3": "#2d2d35", + "fgSurface4": "#232329", + "fgSurface5": "#000", "stroke1": "#8f8fb5", - "stroke2": "#8181ab", + "stroke2": "#8181a7", "stroke3": "#46446f", "stroke4": "#2b2a48", "surface1": "#9190c5", "surface2": "#9898c2", "surface3": "#9e9ecc", - "surface4": "#8d8cc2", - "surface5": "#8787bf", + "surface4": "#8e8dc3", + "surface5": "#8585bc", "surface6": "#7c7bb7", }, "warnings": undefined, @@ -11398,25 +10927,23 @@ exports[`buildRamps > background ramp snapshots at lightness 60 1`] = ` "ramp": { "bgFill1": "#522e52", "bgFill2": "#432144", - "bgFillDark": "#000", "bgFillInverted1": "#2e0d2f", "bgFillInverted2": "#000", "fgFill": "#f1eff1", - "fgFillDark": "#f1eff1", "fgFillInverted": "#f1eff1", - "fgSurface1": "#686167", "fgSurface2": "#4d474d", - "fgSurface3": "#312d31", - "fgSurface4": "#000", + "fgSurface3": "#302c30", + "fgSurface4": "#242124", + "fgSurface5": "#000", "stroke1": "#ae86ae", - "stroke2": "#9d789d", - "stroke3": "#584258", + "stroke2": "#9e779e", + "stroke3": "#574157", "stroke4": "#372837", - "surface1": "#bc81bd", + "surface1": "#bb80bc", "surface2": "#b78eb7", - "surface3": "#c88ec9", + "surface3": "#c78dc8", "surface4": "#b77db8", - "surface5": "#b079b0", + "surface5": "#ad76ad", "surface6": "#a170a1", }, "warnings": undefined, @@ -11433,25 +10960,23 @@ exports[`buildRamps > background ramp snapshots at lightness 60 1`] = ` "ramp": { "bgFill1": "#3b3b3b", "bgFill2": "#2e2e2e", - "bgFillDark": "#000", "bgFillInverted1": "#1b1b1b", "bgFillInverted2": "#000", "fgFill": "#f0f0f0", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", - "fgSurface1": "#636363", "fgSurface2": "#494949", "fgSurface3": "#2e2e2e", - "fgSurface4": "#000", + "fgSurface4": "#222", + "fgSurface5": "#000", "stroke1": "#929292", "stroke2": "#848484", - "stroke3": "#494949", + "stroke3": "#484848", "stroke4": "#2d2d2d", "surface1": "#959595", "surface2": "#9b9b9b", "surface3": "#a2a2a2", - "surface4": "#919191", - "surface5": "#8c8c8c", + "surface4": "#929292", + "surface5": "#898989", "surface6": "#808080", }, "warnings": undefined, @@ -11468,25 +10993,23 @@ exports[`buildRamps > background ramp snapshots at lightness 60 1`] = ` "ramp": { "bgFill1": "#3b3b3b", "bgFill2": "#2e2e2e", - "bgFillDark": "#000", "bgFillInverted1": "#1b1b1b", "bgFillInverted2": "#000", "fgFill": "#f0f0f0", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", - "fgSurface1": "#636363", "fgSurface2": "#494949", "fgSurface3": "#2e2e2e", - "fgSurface4": "#000", + "fgSurface4": "#222", + "fgSurface5": "#000", "stroke1": "#929292", "stroke2": "#848484", - "stroke3": "#494949", + "stroke3": "#484848", "stroke4": "#2d2d2d", "surface1": "#959595", "surface2": "#9b9b9b", "surface3": "#a2a2a2", - "surface4": "#919191", - "surface5": "#8c8c8c", + "surface4": "#929292", + "surface5": "#898989", "surface6": "#808080", }, "warnings": undefined, @@ -11503,25 +11026,23 @@ exports[`buildRamps > background ramp snapshots at lightness 60 1`] = ` "ramp": { "bgFill1": "#3b3b3b", "bgFill2": "#2e2e2e", - "bgFillDark": "#000", "bgFillInverted1": "#1b1b1b", "bgFillInverted2": "#000", "fgFill": "#f0f0f0", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", - "fgSurface1": "#636363", "fgSurface2": "#494949", "fgSurface3": "#2e2e2e", - "fgSurface4": "#000", + "fgSurface4": "#222", + "fgSurface5": "#000", "stroke1": "#929292", "stroke2": "#848484", - "stroke3": "#494949", + "stroke3": "#484848", "stroke4": "#2d2d2d", "surface1": "#959595", "surface2": "#9b9b9b", "surface3": "#a2a2a2", - "surface4": "#919191", - "surface5": "#8c8c8c", + "surface4": "#929292", + "surface5": "#898989", "surface6": "#808080", }, "warnings": undefined, @@ -11538,25 +11059,23 @@ exports[`buildRamps > background ramp snapshots at lightness 60 1`] = ` "ramp": { "bgFill1": "#3b3b3b", "bgFill2": "#2e2e2e", - "bgFillDark": "#000", "bgFillInverted1": "#1b1b1b", "bgFillInverted2": "#000", "fgFill": "#f0f0f0", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", - "fgSurface1": "#636363", "fgSurface2": "#494949", "fgSurface3": "#2e2e2e", - "fgSurface4": "#000", + "fgSurface4": "#222", + "fgSurface5": "#000", "stroke1": "#929292", "stroke2": "#848484", - "stroke3": "#494949", + "stroke3": "#484848", "stroke4": "#2d2d2d", "surface1": "#959595", "surface2": "#9b9b9b", "surface3": "#a2a2a2", - "surface4": "#919191", - "surface5": "#8c8c8c", + "surface4": "#929292", + "surface5": "#898989", "surface6": "#808080", }, "warnings": undefined, @@ -11573,25 +11092,23 @@ exports[`buildRamps > background ramp snapshots at lightness 60 1`] = ` "ramp": { "bgFill1": "#3b3b3b", "bgFill2": "#2e2e2e", - "bgFillDark": "#000", "bgFillInverted1": "#1b1b1b", "bgFillInverted2": "#000", "fgFill": "#f0f0f0", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", - "fgSurface1": "#636363", "fgSurface2": "#494949", "fgSurface3": "#2e2e2e", - "fgSurface4": "#000", + "fgSurface4": "#222", + "fgSurface5": "#000", "stroke1": "#929292", "stroke2": "#848484", - "stroke3": "#494949", + "stroke3": "#484848", "stroke4": "#2d2d2d", "surface1": "#959595", "surface2": "#9b9b9b", "surface3": "#a2a2a2", - "surface4": "#919191", - "surface5": "#8c8c8c", + "surface4": "#929292", + "surface5": "#898989", "surface6": "#808080", }, "warnings": undefined, @@ -11608,25 +11125,23 @@ exports[`buildRamps > background ramp snapshots at lightness 60 1`] = ` "ramp": { "bgFill1": "#3b3b3b", "bgFill2": "#2e2e2e", - "bgFillDark": "#000", "bgFillInverted1": "#1b1b1b", "bgFillInverted2": "#000", "fgFill": "#f0f0f0", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", - "fgSurface1": "#636363", "fgSurface2": "#494949", "fgSurface3": "#2e2e2e", - "fgSurface4": "#000", + "fgSurface4": "#222", + "fgSurface5": "#000", "stroke1": "#929292", "stroke2": "#848484", - "stroke3": "#494949", + "stroke3": "#484848", "stroke4": "#2d2d2d", "surface1": "#959595", "surface2": "#9b9b9b", "surface3": "#a2a2a2", - "surface4": "#919191", - "surface5": "#8c8c8c", + "surface4": "#929292", + "surface5": "#898989", "surface6": "#808080", }, "warnings": undefined, @@ -11648,25 +11163,23 @@ exports[`buildRamps > background ramp snapshots at lightness 70 1`] = ` "ramp": { "bgFill1": "#7e000b", "bgFill2": "#640005", - "bgFillDark": "#000", "bgFillInverted1": "#400001", "bgFillInverted2": "#000", "fgFill": "#f2efef", - "fgFillDark": "#f2efef", "fgFillInverted": "#f2efef", - "fgSurface1": "#715f5d", "fgSurface2": "#544645", - "fgSurface3": "#362c2c", - "fgSurface4": "#000", + "fgSurface3": "#382e2d", + "fgSurface4": "#292121", + "fgSurface5": "#000", "stroke1": "#d07c78", - "stroke2": "#c66a67", + "stroke2": "#c06c69", "stroke3": "#713837", "stroke4": "#492221", - "surface1": "#e77270", + "surface1": "#e97471", "surface2": "#ff6b6b", - "surface3": "#ea8581", - "surface4": "#e56b69", - "surface5": "#e36261", + "surface3": "#ec8783", + "surface4": "#e86d6b", + "surface5": "#e1605f", "surface6": "#d65757", }, "warnings": undefined, @@ -11683,25 +11196,23 @@ exports[`buildRamps > background ramp snapshots at lightness 70 1`] = ` "ramp": { "bgFill1": "#5b5900", "bgFill2": "#4e4c00", - "bgFillDark": "#1e1e1b", "bgFillInverted1": "#302f00", "bgFillInverted2": "#1e1e1b", "fgFill": "#f5f79e", - "fgFillDark": "#f5f79e", "fgFillInverted": "#f5f79e", - "fgSurface1": "#aaab9f", "fgSurface2": "#8a8a80", "fgSurface3": "#6d6d65", - "fgSurface4": "#1e1e1b", + "fgSurface4": "#4b4b45", + "fgSurface5": "#141412", "stroke1": "#eef0ab", - "stroke2": "#dadc9c", + "stroke2": "#dadc99", "stroke3": "#8b8c62", "stroke4": "#6d6e4c", "surface1": "#f4f58b", "surface2": "#ff6", "surface3": "#fff", - "surface4": "#eeef89", - "surface5": "#e6e787", + "surface4": "#f0f18b", + "surface5": "#e3e484", "surface6": "#d6d782", }, "warnings": undefined, @@ -11718,25 +11229,23 @@ exports[`buildRamps > background ramp snapshots at lightness 70 1`] = ` "ramp": { "bgFill1": "#006b00", "bgFill2": "#005c00", - "bgFillDark": "#1c1f1b", "bgFillInverted1": "#003700", "bgFillInverted2": "#1c1f1b", "fgFill": "#def9dc", - "fgFillDark": "#def9dc", "fgFillInverted": "#def9dc", - "fgSurface1": "#909d8f", "fgSurface2": "#737d72", - "fgSurface3": "#596158", - "fgSurface4": "#1c1f1b", + "fgSurface3": "#576057", + "fgSurface4": "#4f574f", + "fgSurface5": "#121512", "stroke1": "#9de79a", - "stroke2": "#8fd38c", - "stroke3": "#598556", + "stroke2": "#8bd388", + "stroke3": "#578354", "stroke4": "#436742", - "surface1": "#80f17d", + "surface1": "#7eef7b", "surface2": "#6f6", - "surface3": "#b6fab3", - "surface4": "#7deb7a", - "surface5": "#7be478", + "surface3": "#b4f8b1", + "surface4": "#7fed7b", + "surface5": "#77e075", "surface6": "#76d373", }, "warnings": undefined, @@ -11753,25 +11262,23 @@ exports[`buildRamps > background ramp snapshots at lightness 70 1`] = ` "ramp": { "bgFill1": "#006566", "bgFill2": "#005758", - "bgFillDark": "#1c1f1f", "bgFillInverted1": "#003435", "bgFillInverted2": "#1c1f1f", "fgFill": "#c9fcfb", - "fgFillDark": "#c9fcfb", "fgFillInverted": "#c9fcfb", - "fgSurface1": "#96a1a1", "fgSurface2": "#788282", - "fgSurface3": "#5e6565", - "fgSurface4": "#1c1f1f", + "fgSurface3": "#5d6464", + "fgSurface4": "#545b5b", + "fgSurface5": "#121515", "stroke1": "#a6e9e9", - "stroke2": "#98d5d5", - "stroke3": "#5e8787", + "stroke2": "#94d6d6", + "stroke3": "#5c8585", "stroke4": "#486969", - "surface1": "#88f2f1", + "surface1": "#86f0ef", "surface2": "#6ff", - "surface3": "#bafefd", - "surface4": "#86eceb", - "surface5": "#83e4e4", + "surface3": "#b8fcfb", + "surface4": "#87edec", + "surface5": "#7fe0e1", "surface6": "#7fd3d3", }, "warnings": undefined, @@ -11788,25 +11295,23 @@ exports[`buildRamps > background ramp snapshots at lightness 70 1`] = ` "ramp": { "bgFill1": "#312b90", "bgFill2": "#261a7f", - "bgFillDark": "#000", "bgFillInverted1": "#180065", "bgFillInverted2": "#000", "fgFill": "#eff0f2", - "fgFillDark": "#eff0f2", "fgFillInverted": "#eff0f2", - "fgSurface1": "#5e6278", "fgSurface2": "#45485a", - "fgSurface3": "#2c2e3b", - "fgSurface4": "#000", + "fgSurface3": "#2c2f3c", + "fgSurface4": "#21222d", + "fgSurface5": "#000", "stroke1": "#878fc9", - "stroke2": "#777fc3", + "stroke2": "#767db9", "stroke3": "#3d408e", "stroke4": "#25275d", - "surface1": "#858fe2", + "surface1": "#8690e3", "surface2": "#8892ff", - "surface3": "#939de5", - "surface4": "#818ae0", - "surface5": "#7b84dd", + "surface3": "#949ee6", + "surface4": "#838ce2", + "surface5": "#7982db", "surface6": "#6f78d7", }, "warnings": undefined, @@ -11823,25 +11328,23 @@ exports[`buildRamps > background ramp snapshots at lightness 70 1`] = ` "ramp": { "bgFill1": "#7b007d", "bgFill2": "#650067", - "bgFillDark": "#161216", "bgFillInverted1": "#49004b", "bgFillInverted2": "#161216", "fgFill": "#f2eff2", - "fgFillDark": "#f2eff2", "fgFillInverted": "#f2eff2", - "fgSurface1": "#766775", "fgSurface2": "#594d58", - "fgSurface3": "#3d343c", - "fgSurface4": "#161216", + "fgSurface3": "#3f363f", + "fgSurface4": "#312a31", + "fgSurface5": "#000", "stroke1": "#d37ed1", - "stroke2": "#c171c0", + "stroke2": "#c370c2", "stroke3": "#703f70", "stroke4": "#4d294c", - "surface1": "#e970e8", + "surface1": "#ec73eb", "surface2": "#f6f", - "surface3": "#ed87eb", - "surface4": "#e769e6", - "surface5": "#e064df", + "surface3": "#f08aee", + "surface4": "#e96be8", + "surface5": "#dd61dc", "surface6": "#cc5ecc", }, "warnings": undefined, @@ -11858,25 +11361,23 @@ exports[`buildRamps > background ramp snapshots at lightness 70 1`] = ` "ramp": { "bgFill1": "#7c0017", "bgFill2": "#630009", - "bgFillDark": "#000", "bgFillInverted1": "#400002", "bgFillInverted2": "#000", "fgFill": "#f2efef", - "fgFillDark": "#f2efef", "fgFillInverted": "#f2efef", - "fgSurface1": "#6f5f5e", "fgSurface2": "#534645", - "fgSurface3": "#352c2c", - "fgSurface4": "#000", + "fgSurface3": "#362d2c", + "fgSurface4": "#282120", + "fgSurface5": "#000", "stroke1": "#cb7e7b", - "stroke2": "#c06d6b", + "stroke2": "#bc6f6d", "stroke3": "#6d3a39", "stroke4": "#462322", - "surface1": "#e07675", + "surface1": "#e17776", "surface2": "#f07676", - "surface3": "#e48886", - "surface4": "#df6f6e", - "surface5": "#dd6768", + "surface3": "#e58987", + "surface4": "#e17170", + "surface5": "#da6465", "surface6": "#ce5c5d", }, "warnings": undefined, @@ -11893,25 +11394,23 @@ exports[`buildRamps > background ramp snapshots at lightness 70 1`] = ` "ramp": { "bgFill1": "#5b5900", "bgFill2": "#4e4c00", - "bgFillDark": "#1e1e1c", "bgFillInverted1": "#312f00", "bgFillInverted2": "#1e1e1c", "fgFill": "#f5f6a5", - "fgFillDark": "#f5f6a5", "fgFillInverted": "#f5f6a5", - "fgSurface1": "#a0a095", "fgSurface2": "#808178", "fgSurface3": "#64655e", - "fgSurface4": "#1e1e1c", + "fgSurface4": "#464741", + "fgSurface5": "#050504", "stroke1": "#e0e1a5", - "stroke2": "#cdce96", + "stroke2": "#cdce93", "stroke3": "#82825e", "stroke4": "#656548", "surface1": "#e6e689", "surface2": "#f0f075", "surface3": "#f7f894", - "surface4": "#e0e087", - "surface5": "#d9d984", + "surface4": "#e2e289", + "surface5": "#d6d681", "surface6": "#c9ca7f", }, "warnings": undefined, @@ -11928,25 +11427,23 @@ exports[`buildRamps > background ramp snapshots at lightness 70 1`] = ` "ramp": { "bgFill1": "#006b00", "bgFill2": "#005c00", - "bgFillDark": "#1c1f1c", "bgFillInverted1": "#003700", "bgFillInverted2": "#1c1f1c", "fgFill": "#e3f7e2", - "fgFillDark": "#e3f7e2", "fgFillInverted": "#e3f7e2", - "fgSurface1": "#889488", "fgSurface2": "#6c766c", - "fgSurface3": "#535a52", - "fgSurface4": "#1c1f1c", + "fgSurface3": "#525952", + "fgSurface4": "#4a5049", + "fgSurface5": "#121512", "stroke1": "#9bdb98", - "stroke2": "#8dc78b", - "stroke3": "#567c55", + "stroke2": "#89c887", + "stroke3": "#557b54", "stroke4": "#415f3f", - "surface1": "#82e481", + "surface1": "#81e380", "surface2": "#75f075", - "surface3": "#91f48f", - "surface4": "#80de7e", - "surface5": "#7dd77b", + "surface3": "#90f38e", + "surface4": "#81e07f", + "surface5": "#7ad478", "surface6": "#77c775", }, "warnings": undefined, @@ -11963,25 +11460,23 @@ exports[`buildRamps > background ramp snapshots at lightness 70 1`] = ` "ramp": { "bgFill1": "#006566", "bgFill2": "#005758", - "bgFillDark": "#1c1f1f", "bgFillInverted1": "#003435", "bgFillInverted2": "#1c1f1f", "fgFill": "#d3f9f8", - "fgFillDark": "#d3f9f8", "fgFillInverted": "#d3f9f8", - "fgSurface1": "#8e9898", "fgSurface2": "#717979", "fgSurface3": "#575e5e", - "fgSurface4": "#1c1f1f", + "fgSurface4": "#4e5454", + "fgSurface5": "#121515", "stroke1": "#a1dcdc", - "stroke2": "#93c9c9", + "stroke2": "#90caca", "stroke3": "#5b7e7e", "stroke4": "#456160", "surface1": "#88e4e4", "surface2": "#75f0f0", "surface3": "#95f5f5", - "surface4": "#86dede", - "surface5": "#83d7d7", + "surface4": "#88e0e0", + "surface5": "#80d4d4", "surface6": "#7dc7c7", }, "warnings": undefined, @@ -11998,25 +11493,23 @@ exports[`buildRamps > background ramp snapshots at lightness 70 1`] = ` "ramp": { "bgFill1": "#352990", "bgFill2": "#2a187f", - "bgFillDark": "#000", "bgFillInverted1": "#1a0062", "bgFillInverted2": "#000", "fgFill": "#f0f0f2", - "fgFillDark": "#f0f0f2", "fgFillInverted": "#f0f0f2", - "fgSurface1": "#5f6177", "fgSurface2": "#46485a", - "fgSurface3": "#2c2d3a", - "fgSurface4": "#000", + "fgSurface3": "#2d2e3b", + "fgSurface4": "#21222d", + "fgSurface5": "#000", "stroke1": "#898eca", - "stroke2": "#7a7ec3", + "stroke2": "#797dba", "stroke3": "#41408c", "stroke4": "#28275b", - "surface1": "#898ee2", + "surface1": "#8a8fe3", "surface2": "#8d90ff", - "surface3": "#979ce5", - "surface4": "#8589e0", - "surface5": "#8083de", + "surface3": "#989de6", + "surface4": "#878be2", + "surface5": "#7e81db", "surface6": "#7476d7", }, "warnings": undefined, @@ -12033,25 +11526,23 @@ exports[`buildRamps > background ramp snapshots at lightness 70 1`] = ` "ramp": { "bgFill1": "#7a007d", "bgFill2": "#640067", - "bgFillDark": "#151115", "bgFillInverted1": "#48004a", "bgFillInverted2": "#151115", "fgFill": "#f2eff2", - "fgFillDark": "#f2eff2", "fgFillInverted": "#f2eff2", - "fgSurface1": "#746773", "fgSurface2": "#584d57", - "fgSurface3": "#3c343b", - "fgSurface4": "#151115", + "fgSurface3": "#3d353c", + "fgSurface4": "#302930", + "fgSurface5": "#000", "stroke1": "#cd82cc", - "stroke2": "#bb74ba", + "stroke2": "#bc73bb", "stroke3": "#6c416c", "stroke4": "#4a2a49", - "surface1": "#e276e2", + "surface1": "#e377e3", "surface2": "#f075f0", - "surface3": "#e68be5", - "surface4": "#de70de", - "surface5": "#d76bd7", + "surface3": "#e78ce6", + "surface4": "#e072e0", + "surface5": "#d468d4", "surface6": "#c564c5", }, "warnings": undefined, @@ -12068,25 +11559,23 @@ exports[`buildRamps > background ramp snapshots at lightness 70 1`] = ` "ramp": { "bgFill1": "#712127", "bgFill2": "#61101a", - "bgFillDark": "#070505", "bgFillInverted1": "#460005", "bgFillInverted2": "#070505", "fgFill": "#f2efef", - "fgFillDark": "#f2efef", "fgFillInverted": "#f2efef", - "fgSurface1": "#716261", "fgSurface2": "#544848", "fgSurface3": "#372f2f", - "fgSurface4": "#070505", + "fgSurface4": "#2b2424", + "fgSurface5": "#000", "stroke1": "#c68483", - "stroke2": "#bc7373", - "stroke3": "#6c3f3e", + "stroke2": "#b77574", + "stroke3": "#6b3e3d", "stroke4": "#472727", "surface1": "#da7e7f", "surface2": "#e08585", "surface3": "#de908f", - "surface4": "#d87879", - "surface5": "#d57172", + "surface4": "#da797a", + "surface5": "#d26f70", "surface6": "#c96567", }, "warnings": undefined, @@ -12103,25 +11592,23 @@ exports[`buildRamps > background ramp snapshots at lightness 70 1`] = ` "ramp": { "bgFill1": "#5c5900", "bgFill2": "#4f4c00", - "bgFillDark": "#1e1e1c", "bgFillInverted1": "#312f00", "bgFillInverted2": "#1e1e1c", "fgFill": "#f4f5b9", - "fgFillDark": "#f4f5b9", "fgFillInverted": "#f4f5b9", - "fgSurface1": "#95958c", "fgSurface2": "#777770", "fgSurface3": "#5c5c56", - "fgSurface4": "#1e1e1c", + "fgSurface4": "#52524d", + "fgSurface5": "#141412", "stroke1": "#d3d4a0", - "stroke2": "#c0c191", + "stroke2": "#c0c18e", "stroke3": "#78795a", "stroke4": "#5c5c44", "surface1": "#d8d889", "surface2": "#e0e085", "surface3": "#e8e894", - "surface4": "#d2d286", - "surface5": "#cbcc83", + "surface4": "#d4d488", + "surface5": "#c8c980", "surface6": "#bdbd7d", }, "warnings": undefined, @@ -12138,25 +11625,23 @@ exports[`buildRamps > background ramp snapshots at lightness 70 1`] = ` "ramp": { "bgFill1": "#006908", "bgFill2": "#005b00", - "bgFillDark": "#1c1f1c", "bgFillInverted1": "#003700", "bgFillInverted2": "#1c1f1c", "fgFill": "#e8f4e7", - "fgFillDark": "#e8f4e7", "fgFillInverted": "#e8f4e7", - "fgSurface1": "#828b81", "fgSurface2": "#666e66", - "fgSurface3": "#4d534d", - "fgSurface4": "#1c1f1c", + "fgSurface3": "#4d534c", + "fgSurface4": "#444944", + "fgSurface5": "#10120f", "stroke1": "#98cf96", - "stroke2": "#8abc89", + "stroke2": "#87bd86", "stroke3": "#537453", "stroke4": "#3d573d", "surface1": "#84d783", "surface2": "#85e085", "surface3": "#8ee88e", - "surface4": "#81d280", - "surface5": "#7ecb7d", + "surface4": "#82d381", + "surface5": "#7bc87a", "surface6": "#7b7", }, "warnings": undefined, @@ -12173,25 +11658,23 @@ exports[`buildRamps > background ramp snapshots at lightness 70 1`] = ` "ramp": { "bgFill1": "#006465", "bgFill2": "#005657", - "bgFillDark": "#1c1f1f", "bgFillInverted1": "#003435", "bgFillInverted2": "#1c1f1f", "fgFill": "#ddf6f6", - "fgFillDark": "#ddf6f6", "fgFillInverted": "#ddf6f6", - "fgSurface1": "#868f8f", "fgSurface2": "#6a7171", - "fgSurface3": "#515656", - "fgSurface4": "#1c1f1f", + "fgSurface3": "#505656", + "fgSurface4": "#474c4c", + "fgSurface5": "#131515", "stroke1": "#9dd0cf", - "stroke2": "#8fbdbd", - "stroke3": "#577575", + "stroke2": "#8dbebe", + "stroke3": "#567474", "stroke4": "#415858", - "surface1": "#89d7d7", + "surface1": "#88d6d6", "surface2": "#85e0e0", - "surface3": "#94e7e7", - "surface4": "#86d1d1", - "surface5": "#82caca", + "surface3": "#93e6e6", + "surface4": "#87d3d3", + "surface5": "#7fc7c7", "surface6": "#7cbbbb", }, "warnings": undefined, @@ -12208,25 +11691,23 @@ exports[`buildRamps > background ramp snapshots at lightness 70 1`] = ` "ramp": { "bgFill1": "#372e82", "bgFill2": "#2b1f72", - "bgFillDark": "#000", "bgFillInverted1": "#1c045b", "bgFillInverted2": "#000", "fgFill": "#f0f0f2", - "fgFillDark": "#f0f0f2", "fgFillInverted": "#f0f0f2", - "fgSurface1": "#606175", "fgSurface2": "#474858", - "fgSurface3": "#2d2e39", - "fgSurface4": "#000", + "fgSurface3": "#2d2d39", + "fgSurface4": "#21222b", + "fgSurface5": "#000", "stroke1": "#8c8ec5", - "stroke2": "#7d7fbe", - "stroke3": "#434185", + "stroke2": "#7c7eb6", + "stroke3": "#424084", "stroke4": "#292857", "surface1": "#8c8edb", "surface2": "#9192ef", "surface3": "#9a9ce0", - "surface4": "#8889da", - "surface5": "#8384d7", + "surface4": "#8a8bdc", + "surface5": "#8081d4", "surface6": "#7777d0", }, "warnings": undefined, @@ -12243,25 +11724,23 @@ exports[`buildRamps > background ramp snapshots at lightness 70 1`] = ` "ramp": { "bgFill1": "#731b75", "bgFill2": "#640466", - "bgFillDark": "#161216", "bgFillInverted1": "#49004c", "bgFillInverted2": "#161216", "fgFill": "#f2eff2", - "fgFillDark": "#f2eff2", "fgFillInverted": "#f2eff2", - "fgSurface1": "#736873", "fgSurface2": "#574e57", - "fgSurface3": "#3c353b", - "fgSurface4": "#161216", + "fgSurface3": "#3b353b", + "fgSurface4": "#302a30", + "fgSurface5": "#000", "stroke1": "#c887c7", - "stroke2": "#b679b6", - "stroke3": "#6a4469", + "stroke2": "#b778b7", + "stroke3": "#694368", "stroke4": "#482d48", "surface1": "#da7ddb", "surface2": "#e085e0", "surface3": "#e091df", - "surface4": "#d778d7", - "surface5": "#d073d0", + "surface4": "#d879d8", + "surface5": "#cd70cd", "surface6": "#be6bbf", }, "warnings": undefined, @@ -12278,25 +11757,23 @@ exports[`buildRamps > background ramp snapshots at lightness 70 1`] = ` "ramp": { "bgFill1": "#663234", "bgFill2": "#572528", - "bgFillDark": "#130f0f", "bgFillInverted1": "#431317", "bgFillInverted2": "#130f0f", "fgFill": "#f2efef", - "fgFillDark": "#f2efef", "fgFillInverted": "#f2efef", - "fgSurface1": "#736666", "fgSurface2": "#574d4d", "fgSurface3": "#3b3333", - "fgSurface4": "#130f0f", + "fgSurface4": "#2f2929", + "fgSurface5": "#000", "stroke1": "#c28d8d", - "stroke2": "#b97c7c", - "stroke3": "#6c4545", + "stroke2": "#b37d7d", + "stroke3": "#6b4444", "stroke4": "#492c2d", "surface1": "#d28a8a", "surface2": "#d19494", "surface3": "#d89b9a", - "surface4": "#d08485", - "surface5": "#cd7d7f", + "surface4": "#d28586", + "surface5": "#cb7b7d", "surface6": "#c47072", }, "warnings": undefined, @@ -12313,25 +11790,23 @@ exports[`buildRamps > background ramp snapshots at lightness 70 1`] = ` "ramp": { "bgFill1": "#5a581d", "bgFill2": "#4d4b0c", - "bgFillDark": "#1e1e1c", "bgFillInverted1": "#312f00", "bgFillInverted2": "#1e1e1c", "fgFill": "#f2f3d1", - "fgFillDark": "#f2f3d1", "fgFillInverted": "#f2f3d1", - "fgSurface1": "#8a8b83", "fgSurface2": "#6d6e68", - "fgSurface3": "#53534e", - "fgSurface4": "#1e1e1c", + "fgSurface3": "#53534f", + "fgSurface4": "#494945", + "fgSurface5": "#141413", "stroke1": "#c5c59c", - "stroke2": "#b4b48e", + "stroke2": "#b4b48c", "stroke3": "#6f6f56", "stroke4": "#535340", "surface1": "#caca8b", "surface2": "#d1d194", "surface3": "#d9d996", - "surface4": "#c4c488", - "surface5": "#bebe84", + "surface4": "#c6c68a", + "surface5": "#bbbb81", "surface6": "#b0b07d", }, "warnings": undefined, @@ -12348,25 +11823,23 @@ exports[`buildRamps > background ramp snapshots at lightness 70 1`] = ` "ramp": { "bgFill1": "#29642c", "bgFill2": "#1b561f", - "bgFillDark": "#1c1f1c", "bgFillInverted1": "#003700", "bgFillInverted2": "#1c1f1c", "fgFill": "#ebf3eb", - "fgFillDark": "#ebf3eb", "fgFillInverted": "#ebf3eb", - "fgSurface1": "#7c847b", "fgSurface2": "#616761", "fgSurface3": "#484d48", - "fgSurface4": "#1c1f1c", + "fgSurface4": "#3f433f", + "fgSurface5": "#000", "stroke1": "#97c396", - "stroke2": "#89b288", + "stroke2": "#88b287", "stroke3": "#526c52", "stroke4": "#3b4f3b", "surface1": "#88cb88", "surface2": "#94d194", "surface3": "#93db93", - "surface4": "#85c685", - "surface5": "#81bf81", + "surface4": "#87c887", + "surface5": "#7fbd7f", "surface6": "#7ab17a", }, "warnings": undefined, @@ -12383,25 +11856,23 @@ exports[`buildRamps > background ramp snapshots at lightness 70 1`] = ` "ramp": { "bgFill1": "#226061", "bgFill2": "#115353", - "bgFillDark": "#1c1f1f", "bgFillInverted1": "#003435", "bgFillInverted2": "#1c1f1f", "fgFill": "#e5f4f3", - "fgFillDark": "#e5f4f3", "fgFillInverted": "#e5f4f3", - "fgSurface1": "#7f8686", "fgSurface2": "#646969", "fgSurface3": "#4a4f4f", - "fgSurface4": "#1c1f1f", + "fgSurface4": "#414545", + "fgSurface5": "#000", "stroke1": "#9bc3c3", - "stroke2": "#8db2b2", - "stroke3": "#556d6d", + "stroke2": "#8bb2b2", + "stroke3": "#546c6c", "stroke4": "#3e5150", "surface1": "#8ccaca", "surface2": "#94d1d1", "surface3": "#97dada", - "surface4": "#88c5c5", - "surface5": "#85bebe", + "surface4": "#89c7c7", + "surface5": "#82bbbb", "surface6": "#7db0b0", }, "warnings": undefined, @@ -12418,25 +11889,23 @@ exports[`buildRamps > background ramp snapshots at lightness 70 1`] = ` "ramp": { "bgFill1": "#38356a", "bgFill2": "#2b275a", - "bgFillDark": "#000", "bgFillInverted1": "#1a1244", "bgFillInverted2": "#000", "fgFill": "#f0f0f1", - "fgFillDark": "#f0f0f1", "fgFillInverted": "#f0f0f1", - "fgSurface1": "#616172", "fgSurface2": "#474855", "fgSurface3": "#2d2e37", - "fgSurface4": "#000", + "fgSurface4": "#22232a", + "fgSurface5": "#000", "stroke1": "#8e8fbc", - "stroke2": "#7f80b3", - "stroke3": "#454378", + "stroke2": "#7f80ad", + "stroke3": "#454377", "stroke4": "#2b294e", "surface1": "#908fcf", "surface2": "#9696d3", "surface3": "#9c9dd4", - "surface4": "#8b8bcc", - "surface5": "#8685c9", + "surface4": "#8c8cce", + "surface5": "#8483c7", "surface6": "#7b79c1", }, "warnings": undefined, @@ -12453,25 +11922,23 @@ exports[`buildRamps > background ramp snapshots at lightness 70 1`] = ` "ramp": { "bgFill1": "#673168", "bgFill2": "#58235a", - "bgFillDark": "#191619", "bgFillInverted1": "#461047", "bgFillInverted2": "#191619", "fgFill": "#f1eff1", - "fgFillDark": "#f1eff1", "fgFillInverted": "#f1eff1", - "fgSurface1": "#746a74", "fgSurface2": "#585058", - "fgSurface3": "#3d373d", - "fgSurface4": "#191619", + "fgSurface3": "#3c363c", + "fgSurface4": "#322d31", + "fgSurface5": "#000", "stroke1": "#c28dc2", - "stroke2": "#b27fb2", - "stroke3": "#684868", + "stroke2": "#b37fb3", + "stroke3": "#674767", "stroke4": "#473047", - "surface1": "#d288d3", + "surface1": "#d187d2", "surface2": "#d194d1", - "surface3": "#d99ad9", - "surface4": "#cf82d0", - "surface5": "#c97dc9", + "surface3": "#d899d8", + "surface4": "#d083d1", + "surface5": "#c67ac6", "surface6": "#b874b9", }, "warnings": undefined, @@ -12488,25 +11955,23 @@ exports[`buildRamps > background ramp snapshots at lightness 70 1`] = ` "ramp": { "bgFill1": "#5a4041", "bgFill2": "#4d3434", - "bgFillDark": "#1b1818", "bgFillInverted1": "#3b2324", "bgFillInverted2": "#1b1818", "fgFill": "#f1f0f0", - "fgFillDark": "#f1f0f0", "fgFillInverted": "#f1f0f0", - "fgSurface1": "#766c6c", "fgSurface2": "#5a5252", - "fgSurface3": "#3f3939", - "fgSurface4": "#1b1818", + "fgSurface3": "#3e3838", + "fgSurface4": "#342f2f", + "fgSurface5": "#000", "stroke1": "#bc9898", - "stroke2": "#b28787", - "stroke3": "#6b4c4d", + "stroke2": "#ad8888", + "stroke3": "#6a4b4c", "stroke4": "#4a3434", - "surface1": "#c99798", + "surface1": "#c89697", "surface2": "#c2a3a3", - "surface3": "#d0a7a8", - "surface4": "#c69293", - "surface5": "#c38c8c", + "surface3": "#cfa6a7", + "surface4": "#c79394", + "surface5": "#c08989", "surface6": "#bb7e7f", }, "warnings": undefined, @@ -12523,25 +11988,23 @@ exports[`buildRamps > background ramp snapshots at lightness 70 1`] = ` "ramp": { "bgFill1": "#58573c", "bgFill2": "#4b4a30", - "bgFillDark": "#1e1e1d", "bgFillInverted1": "#2f2f15", "bgFillInverted2": "#1e1e1d", "fgFill": "#f1f1e3", - "fgFillDark": "#f1f1e3", "fgFillInverted": "#f1f1e3", - "fgSurface1": "#80807b", "fgSurface2": "#646460", "fgSurface3": "#4a4a47", - "fgSurface4": "#1e1e1d", + "fgSurface4": "#40403d", + "fgSurface5": "#000", "stroke1": "#b8b89b", - "stroke2": "#a7a78d", - "stroke3": "#656554", + "stroke2": "#a7a78b", + "stroke3": "#646453", "stroke4": "#4a4a3d", "surface1": "#bcbb91", "surface2": "#c2c2a3", "surface3": "#caca9c", - "surface4": "#b7b68d", - "surface5": "#b1b089", + "surface4": "#b9b88f", + "surface5": "#aead86", "surface6": "#a3a381", }, "warnings": undefined, @@ -12558,25 +12021,23 @@ exports[`buildRamps > background ramp snapshots at lightness 70 1`] = ` "ramp": { "bgFill1": "#3d593e", "bgFill2": "#314b31", - "bgFillDark": "#1d1f1d", "bgFillInverted1": "#1a331b", "bgFillInverted2": "#1d1f1d", "fgFill": "#edf1ed", - "fgFillDark": "#edf1ed", "fgFillInverted": "#edf1ed", - "fgSurface1": "#777c77", "fgSurface2": "#5c615c", - "fgSurface3": "#434643", - "fgSurface4": "#1d1f1d", + "fgSurface3": "#444744", + "fgSurface4": "#3a3d3a", + "fgSurface5": "#000", "stroke1": "#99b899", - "stroke2": "#8ba78b", + "stroke2": "#89a789", "stroke3": "#526452", "stroke4": "#3a483a", - "surface1": "#90be90", + "surface1": "#91bf91", "surface2": "#a3c2a3", - "surface3": "#9cce9c", - "surface4": "#8cb98d", - "surface5": "#88b389", + "surface3": "#9dcf9d", + "surface4": "#8ebc8f", + "surface5": "#86b187", "surface6": "#7fa57f", }, "warnings": undefined, @@ -12593,25 +12054,23 @@ exports[`buildRamps > background ramp snapshots at lightness 70 1`] = ` "ramp": { "bgFill1": "#3d5858", "bgFill2": "#304b4b", - "bgFillDark": "#1d1e1e", "bgFillInverted1": "#173232", "bgFillInverted2": "#1d1e1e", "fgFill": "#ebf2f2", - "fgFillDark": "#ebf2f2", "fgFillInverted": "#ebf2f2", - "fgSurface1": "#797d7d", "fgSurface2": "#5e6262", "fgSurface3": "#454848", - "fgSurface4": "#1d1e1e", + "fgSurface4": "#3b3e3e", + "fgSurface5": "#000", "stroke1": "#9bb7b7", - "stroke2": "#8da7a7", + "stroke2": "#8ca7a7", "stroke3": "#546464", "stroke4": "#3c4949", "surface1": "#92bdbd", "surface2": "#a3c2c2", "surface3": "#9ecccc", - "surface4": "#8fb8b8", - "surface5": "#8ab2b2", + "surface4": "#91baba", + "surface5": "#87afaf", "surface6": "#81a4a4", }, "warnings": undefined, @@ -12628,25 +12087,23 @@ exports[`buildRamps > background ramp snapshots at lightness 70 1`] = ` "ramp": { "bgFill1": "#42415b", "bgFill2": "#35344d", - "bgFillDark": "#131317", "bgFillInverted1": "#24233b", "bgFillInverted2": "#131317", "fgFill": "#f0f0f1", - "fgFillDark": "#f0f0f1", "fgFillInverted": "#f0f0f1", - "fgSurface1": "#6a6a76", "fgSurface2": "#50505a", "fgSurface3": "#36363e", - "fgSurface4": "#131317", + "fgSurface4": "#2b2b32", + "fgSurface5": "#000", "stroke1": "#9a9ab8", - "stroke2": "#8b8bae", + "stroke2": "#8b8ba9", "stroke3": "#4e4c76", "stroke4": "#343351", "surface1": "#9c9bc6", "surface2": "#a3a3c2", "surface3": "#a9a9ce", - "surface4": "#9897c3", - "surface5": "#9291c0", + "surface4": "#9998c4", + "surface5": "#8f8ebd", "surface6": "#8685b7", }, "warnings": undefined, @@ -12663,25 +12120,23 @@ exports[`buildRamps > background ramp snapshots at lightness 70 1`] = ` "ramp": { "bgFill1": "#5a405b", "bgFill2": "#4d334d", - "bgFillDark": "#1d1b1d", "bgFillInverted1": "#3b233c", "bgFillInverted2": "#1d1b1d", "fgFill": "#f1eff1", - "fgFillDark": "#f1eff1", "fgFillInverted": "#f1eff1", - "fgSurface1": "#756e75", "fgSurface2": "#5a545a", - "fgSurface3": "#3f3b3f", - "fgSurface4": "#1d1b1d", + "fgSurface3": "#3e3a3e", + "fgSurface4": "#343034", + "fgSurface5": "#000", "stroke1": "#bc97bc", "stroke2": "#ad88ad", - "stroke3": "#654e66", + "stroke3": "#644d65", "stroke4": "#463547", - "surface1": "#c895c9", + "surface1": "#c794c8", "surface2": "#c2a3c2", - "surface3": "#d0a6d1", - "surface4": "#c58fc6", - "surface5": "#c08ac1", + "surface3": "#cfa5d0", + "surface4": "#c690c7", + "surface5": "#bc87bd", "surface6": "#b17fb2", }, "warnings": undefined, @@ -12698,25 +12153,23 @@ exports[`buildRamps > background ramp snapshots at lightness 70 1`] = ` "ramp": { "bgFill1": "#4d4d4d", "bgFill2": "#404040", - "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", "fgFill": "#f0f0f0", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", - "fgSurface1": "#757575", "fgSurface2": "#5a5a5a", - "fgSurface3": "#404040", - "fgSurface4": "#1e1e1e", + "fgSurface3": "#414141", + "fgSurface4": "#363636", + "fgSurface5": "#000", "stroke1": "#a9a9a9", "stroke2": "#999", "stroke3": "#5a5a5a", "stroke4": "#3f3f3f", - "surface1": "#acacac", + "surface1": "#adadad", "surface2": "#b3b3b3", - "surface3": "#b9b9b9", - "surface4": "#a7a7a7", - "surface5": "#a1a1a1", + "surface3": "#bababa", + "surface4": "#a9a9a9", + "surface5": "#9f9f9f", "surface6": "#959595", }, "warnings": undefined, @@ -12733,25 +12186,23 @@ exports[`buildRamps > background ramp snapshots at lightness 70 1`] = ` "ramp": { "bgFill1": "#4d4d4d", "bgFill2": "#404040", - "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", "fgFill": "#f0f0f0", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", - "fgSurface1": "#757575", "fgSurface2": "#5a5a5a", - "fgSurface3": "#404040", - "fgSurface4": "#1e1e1e", + "fgSurface3": "#414141", + "fgSurface4": "#363636", + "fgSurface5": "#000", "stroke1": "#a9a9a9", "stroke2": "#999", "stroke3": "#5a5a5a", "stroke4": "#3f3f3f", - "surface1": "#acacac", + "surface1": "#adadad", "surface2": "#b3b3b3", - "surface3": "#b9b9b9", - "surface4": "#a7a7a7", - "surface5": "#a1a1a1", + "surface3": "#bababa", + "surface4": "#a9a9a9", + "surface5": "#9f9f9f", "surface6": "#959595", }, "warnings": undefined, @@ -12768,25 +12219,23 @@ exports[`buildRamps > background ramp snapshots at lightness 70 1`] = ` "ramp": { "bgFill1": "#4d4d4d", "bgFill2": "#404040", - "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", "fgFill": "#f0f0f0", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", - "fgSurface1": "#757575", "fgSurface2": "#5a5a5a", - "fgSurface3": "#404040", - "fgSurface4": "#1e1e1e", + "fgSurface3": "#414141", + "fgSurface4": "#363636", + "fgSurface5": "#000", "stroke1": "#a9a9a9", "stroke2": "#999", "stroke3": "#5a5a5a", "stroke4": "#3f3f3f", - "surface1": "#acacac", + "surface1": "#adadad", "surface2": "#b3b3b3", - "surface3": "#b9b9b9", - "surface4": "#a7a7a7", - "surface5": "#a1a1a1", + "surface3": "#bababa", + "surface4": "#a9a9a9", + "surface5": "#9f9f9f", "surface6": "#959595", }, "warnings": undefined, @@ -12803,25 +12252,23 @@ exports[`buildRamps > background ramp snapshots at lightness 70 1`] = ` "ramp": { "bgFill1": "#4d4d4d", "bgFill2": "#404040", - "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", "fgFill": "#f0f0f0", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", - "fgSurface1": "#757575", "fgSurface2": "#5a5a5a", - "fgSurface3": "#404040", - "fgSurface4": "#1e1e1e", + "fgSurface3": "#414141", + "fgSurface4": "#363636", + "fgSurface5": "#000", "stroke1": "#a9a9a9", "stroke2": "#999", "stroke3": "#5a5a5a", "stroke4": "#3f3f3f", - "surface1": "#acacac", + "surface1": "#adadad", "surface2": "#b3b3b3", - "surface3": "#b9b9b9", - "surface4": "#a7a7a7", - "surface5": "#a1a1a1", + "surface3": "#bababa", + "surface4": "#a9a9a9", + "surface5": "#9f9f9f", "surface6": "#959595", }, "warnings": undefined, @@ -12838,25 +12285,23 @@ exports[`buildRamps > background ramp snapshots at lightness 70 1`] = ` "ramp": { "bgFill1": "#4d4d4d", "bgFill2": "#404040", - "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", "fgFill": "#f0f0f0", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", - "fgSurface1": "#757575", "fgSurface2": "#5a5a5a", - "fgSurface3": "#404040", - "fgSurface4": "#1e1e1e", + "fgSurface3": "#414141", + "fgSurface4": "#363636", + "fgSurface5": "#000", "stroke1": "#a9a9a9", "stroke2": "#999", "stroke3": "#5a5a5a", "stroke4": "#3f3f3f", - "surface1": "#acacac", + "surface1": "#adadad", "surface2": "#b3b3b3", - "surface3": "#b9b9b9", - "surface4": "#a7a7a7", - "surface5": "#a1a1a1", + "surface3": "#bababa", + "surface4": "#a9a9a9", + "surface5": "#9f9f9f", "surface6": "#959595", }, "warnings": undefined, @@ -12873,25 +12318,23 @@ exports[`buildRamps > background ramp snapshots at lightness 70 1`] = ` "ramp": { "bgFill1": "#4d4d4d", "bgFill2": "#404040", - "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", "fgFill": "#f0f0f0", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", - "fgSurface1": "#757575", "fgSurface2": "#5a5a5a", - "fgSurface3": "#404040", - "fgSurface4": "#1e1e1e", + "fgSurface3": "#414141", + "fgSurface4": "#363636", + "fgSurface5": "#000", "stroke1": "#a9a9a9", "stroke2": "#999", "stroke3": "#5a5a5a", "stroke4": "#3f3f3f", - "surface1": "#acacac", + "surface1": "#adadad", "surface2": "#b3b3b3", - "surface3": "#b9b9b9", - "surface4": "#a7a7a7", - "surface5": "#a1a1a1", + "surface3": "#bababa", + "surface4": "#a9a9a9", + "surface5": "#9f9f9f", "surface6": "#959595", }, "warnings": undefined, @@ -12913,25 +12356,23 @@ exports[`buildRamps > background ramp snapshots at lightness 80 1`] = ` "ramp": { "bgFill1": "#8a3035", "bgFill2": "#7b2229", - "bgFillDark": "#231c1c", "bgFillInverted1": "#610315", "bgFillInverted2": "#231c1c", "fgFill": "#f3efef", - "fgFillDark": "#f3efef", "fgFillInverted": "#f3efef", - "fgSurface1": "#887272", "fgSurface2": "#6a5858", - "fgSurface3": "#4d3f3f", - "fgSurface4": "#231c1c", + "fgSurface3": "#4d4040", + "fgSurface4": "#423636", + "fgSurface5": "#000", "stroke1": "#da9c9b", - "stroke2": "#d48887", + "stroke2": "#c98988", "stroke3": "#8c4849", "stroke4": "#663233", - "surface1": "#ec9897", + "surface1": "#ed9998", "surface2": "#f99", - "surface3": "#efabaa", - "surface4": "#eb9291", - "surface5": "#e98a8a", + "surface3": "#f0acab", + "surface4": "#ed9493", + "surface5": "#e68787", "surface6": "#e3787a", }, "warnings": undefined, @@ -12948,25 +12389,23 @@ exports[`buildRamps > background ramp snapshots at lightness 80 1`] = ` "ramp": { "bgFill1": "#5c5900", "bgFill2": "#4f4c00", - "bgFillDark": "#1e1e1c", "bgFillInverted1": "#312f00", "bgFillInverted2": "#1e1e1c", "fgFill": "#f5f6ad", - "fgFillDark": "#f5f6ad", "fgFillInverted": "#f5f6ad", - "fgSurface1": "#acaca2", "fgSurface2": "#8b8c83", - "fgSurface3": "#6e6e67", - "fgSurface4": "#1e1e1c", + "fgSurface3": "#6f6f68", + "fgSurface4": "#4d4d48", + "fgSurface5": "#141412", "stroke1": "#f0f1b6", - "stroke2": "#dcdda7", + "stroke2": "#dcdda4", "stroke3": "#8d8d6a", "stroke4": "#6e6f52", - "surface1": "#f6f69d", + "surface1": "#f7f79e", "surface2": "#ff9", "surface3": "#fff", - "surface4": "#f0f09a", - "surface5": "#e8e897", + "surface4": "#f1f19b", + "surface5": "#e4e493", "surface6": "#d7d791", }, "warnings": undefined, @@ -12983,25 +12422,23 @@ exports[`buildRamps > background ramp snapshots at lightness 80 1`] = ` "ramp": { "bgFill1": "#006a00", "bgFill2": "#005c00", - "bgFillDark": "#1c1f1b", "bgFillInverted1": "#003700", "bgFillInverted2": "#1c1f1b", "fgFill": "#dbfbd9", - "fgFillDark": "#dbfbd9", "fgFillInverted": "#dbfbd9", - "fgSurface1": "#94a294", "fgSurface2": "#778277", - "fgSurface3": "#5d665c", - "fgSurface4": "#1c1f1b", + "fgSurface3": "#5c655c", + "fgSurface4": "#545c53", + "fgSurface5": "#121512", "stroke1": "#a5eea4", - "stroke2": "#94da93", - "stroke3": "#5c8a5b", + "stroke2": "#93da92", + "stroke3": "#5b895a", "stroke4": "#466c46", - "surface1": "#8ef78f", + "surface1": "#8df68e", "surface2": "#9f9", - "surface3": "#cefccd", - "surface4": "#82f283", - "surface5": "#7eeb7f", + "surface3": "#cdfbcc", + "surface4": "#85f586", + "surface5": "#7be87c", "surface6": "#7ad97b", }, "warnings": undefined, @@ -13018,25 +12455,23 @@ exports[`buildRamps > background ramp snapshots at lightness 80 1`] = ` "ramp": { "bgFill1": "#006566", "bgFill2": "#005758", - "bgFillDark": "#1c1f1f", "bgFillInverted1": "#003435", "bgFillInverted2": "#1c1f1f", "fgFill": "#cafbfb", - "fgFillDark": "#cafbfb", "fgFillInverted": "#cafbfb", - "fgSurface1": "#9aa5a5", "fgSurface2": "#7c8585", - "fgSurface3": "#616968", - "fgSurface4": "#1c1f1f", + "fgSurface3": "#606868", + "fgSurface4": "#444a4a", + "fgSurface5": "#121414", "stroke1": "#aeeeed", - "stroke2": "#9fd9d9", - "stroke3": "#638a8a", + "stroke2": "#9cdada", + "stroke3": "#628989", "stroke4": "#4c6c6b", - "surface1": "#94f6f6", + "surface1": "#93f5f5", "surface2": "#9ff", - "surface3": "#d4ffff", - "surface4": "#90f0f0", - "surface5": "#8de8e8", + "surface3": "#d3fefe", + "surface4": "#92f2f2", + "surface5": "#8ae5e5", "surface6": "#87d7d7", }, "warnings": undefined, @@ -13053,25 +12488,23 @@ exports[`buildRamps > background ramp snapshots at lightness 80 1`] = ` "ramp": { "bgFill1": "#3d338f", "bgFill2": "#322480", - "bgFillDark": "#0f1016", "bgFillInverted1": "#240d6b", "bgFillInverted2": "#0f1016", "fgFill": "#f0f0f2", - "fgFillDark": "#f0f0f2", "fgFillInverted": "#f0f0f2", - "fgSurface1": "#66687d", "fgSurface2": "#4d4e61", - "fgSurface3": "#333442", - "fgSurface4": "#0f1016", + "fgSurface3": "#343543", + "fgSurface4": "#292a36", + "fgSurface5": "#000", "stroke1": "#9496ce", - "stroke2": "#8486c6", + "stroke2": "#8386be", "stroke3": "#494595", "stroke4": "#302d67", - "surface1": "#9496e4", + "surface1": "#9597e5", "surface2": "#99f", - "surface3": "#a2a5e8", - "surface4": "#9091e2", - "surface5": "#8a8be0", + "surface3": "#a3a6e9", + "surface4": "#9293e4", + "surface5": "#8889de", "surface6": "#7f7ed9", }, "warnings": undefined, @@ -13088,25 +12521,23 @@ exports[`buildRamps > background ramp snapshots at lightness 80 1`] = ` "ramp": { "bgFill1": "#842386", "bgFill2": "#750f78", - "bgFillDark": "#221c21", "bgFillInverted1": "#550058", "bgFillInverted2": "#221c21", "fgFill": "#f5eef4", - "fgFillDark": "#f5eef4", "fgFillInverted": "#f5eef4", - "fgSurface1": "#8a7989", "fgSurface2": "#6c5e6b", - "fgSurface3": "#4f454f", - "fgSurface4": "#221c21", + "fgSurface3": "#514651", + "fgSurface4": "#463c45", + "fgSurface5": "#000", "stroke1": "#de9fdd", - "stroke2": "#d688d6", + "stroke2": "#c887c8", "stroke3": "#874d87", "stroke4": "#633763", - "surface1": "#e9e", + "surface1": "#f09bf0", "surface2": "#f9f", - "surface3": "#f2aef1", - "surface4": "#ec92ec", - "surface5": "#ea88ea", + "surface3": "#f4b0f3", + "surface4": "#ef95ef", + "surface5": "#e886e8", "surface6": "#e475e5", }, "warnings": undefined, @@ -13123,25 +12554,23 @@ exports[`buildRamps > background ramp snapshots at lightness 80 1`] = ` "ramp": { "bgFill1": "#823b3e", "bgFill2": "#732f32", - "bgFillDark": "#231c1c", "bgFillInverted1": "#58151b", "bgFillInverted2": "#231c1c", "fgFill": "#f3efef", - "fgFillDark": "#f3efef", "fgFillInverted": "#f3efef", - "fgSurface1": "#897575", "fgSurface2": "#6b5b5b", - "fgSurface3": "#4e4242", - "fgSurface4": "#231c1c", + "fgSurface3": "#4f4343", + "fgSurface4": "#443939", + "fgSurface5": "#000", "stroke1": "#d8a1a1", - "stroke2": "#d08d8d", + "stroke2": "#c78e8e", "stroke3": "#8c4d4e", "stroke4": "#663637", - "surface1": "#e89f9f", + "surface1": "#e9a0a0", "surface2": "#f5a3a3", - "surface3": "#ecb1b1", - "surface4": "#e79999", - "surface5": "#e49191", + "surface3": "#edb2b2", + "surface4": "#e89a9a", + "surface5": "#e18f8f", "surface6": "#de8081", }, "warnings": undefined, @@ -13158,25 +12587,23 @@ exports[`buildRamps > background ramp snapshots at lightness 80 1`] = ` "ramp": { "bgFill1": "#5b5900", "bgFill2": "#4e4c00", - "bgFillDark": "#1e1e1c", "bgFillInverted1": "#312f00", "bgFillInverted2": "#1e1e1c", "fgFill": "#f5f5b1", - "fgFillDark": "#f5f5b1", "fgFillInverted": "#f5f5b1", - "fgSurface1": "#a5a59c", "fgSurface2": "#85857e", - "fgSurface3": "#696963", - "fgSurface4": "#1e1e1c", + "fgSurface3": "#686962", + "fgSurface4": "#494945", + "fgSurface5": "#141413", "stroke1": "#e6e7b4", - "stroke2": "#d3d4a4", - "stroke3": "#868767", + "stroke2": "#d3d4a2", + "stroke3": "#858666", "stroke4": "#696950", "surface1": "#ecec9f", "surface2": "#f5f5a3", "surface3": "#fefeab", - "surface4": "#e6e69b", - "surface5": "#dfdf98", + "surface4": "#e8e89d", + "surface5": "#dcdc95", "surface6": "#cfcf91", }, "warnings": undefined, @@ -13193,25 +12620,23 @@ exports[`buildRamps > background ramp snapshots at lightness 80 1`] = ` "ramp": { "bgFill1": "#0f671b", "bgFill2": "#005a08", - "bgFillDark": "#1c1f1c", "bgFillInverted1": "#003700", "bgFillInverted2": "#1c1f1c", "fgFill": "#e0f8df", - "fgFillDark": "#e0f8df", "fgFillInverted": "#e0f8df", - "fgSurface1": "#919c91", "fgSurface2": "#747d74", "fgSurface3": "#5a615a", - "fgSurface4": "#1c1f1c", + "fgSurface4": "#515751", + "fgSurface5": "#121512", "stroke1": "#a8e5a7", - "stroke2": "#99d198", - "stroke3": "#5f845f", + "stroke2": "#96d195", + "stroke3": "#5e835e", "stroke4": "#486648", "surface1": "#93ee94", "surface2": "#a3f5a3", "surface3": "#c3f7c2", - "surface4": "#8fe98f", - "surface5": "#8be08c", + "surface4": "#91eb91", + "surface5": "#89de8a", "surface6": "#85d085", }, "warnings": undefined, @@ -13228,25 +12653,23 @@ exports[`buildRamps > background ramp snapshots at lightness 80 1`] = ` "ramp": { "bgFill1": "#006364", "bgFill2": "#005556", - "bgFillDark": "#1c1f1f", "bgFillInverted1": "#003435", "bgFillInverted2": "#1c1f1f", "fgFill": "#d3f9f9", - "fgFillDark": "#d3f9f9", "fgFillInverted": "#d3f9f9", - "fgSurface1": "#969f9f", "fgSurface2": "#788080", - "fgSurface3": "#5e6464", - "fgSurface4": "#1c1f1f", + "fgSurface3": "#5d6363", + "fgSurface4": "#545959", + "fgSurface5": "#131514", "stroke1": "#b1e4e4", - "stroke2": "#a2d1d1", - "stroke3": "#658484", + "stroke2": "#a0d1d2", + "stroke3": "#648383", "stroke4": "#4e6666", - "surface1": "#9eecec", + "surface1": "#9debeb", "surface2": "#a3f5f5", - "surface3": "#c2fafa", - "surface4": "#9ae6e6", - "surface5": "#97dede", + "surface3": "#c1f9f9", + "surface4": "#9ce8e8", + "surface5": "#94dbdb", "surface6": "#8fcece", }, "warnings": undefined, @@ -13263,25 +12686,23 @@ exports[`buildRamps > background ramp snapshots at lightness 80 1`] = ` "ramp": { "bgFill1": "#433e86", "bgFill2": "#373077", - "bgFillDark": "#191921", "bgFillInverted1": "#291f64", "bgFillInverted2": "#191921", "fgFill": "#f0f0f2", - "fgFillDark": "#f0f0f2", "fgFillInverted": "#f0f0f2", - "fgSurface1": "#6d6e81", "fgSurface2": "#535366", "fgSurface3": "#393a48", - "fgSurface4": "#191921", + "fgSurface4": "#2f303c", + "fgSurface5": "#000", "stroke1": "#9c9dce", - "stroke2": "#8c8dc6", - "stroke3": "#504b99", + "stroke2": "#8b8cbe", + "stroke3": "#4f4a98", "stroke4": "#36326c", "surface1": "#9e9ee2", "surface2": "#a3a3f5", "surface3": "#acade6", - "surface4": "#999ae0", - "surface5": "#9394dd", + "surface4": "#9a9be1", + "surface5": "#9091da", "surface6": "#8786d6", }, "warnings": undefined, @@ -13298,25 +12719,23 @@ exports[`buildRamps > background ramp snapshots at lightness 80 1`] = ` "ramp": { "bgFill1": "#7c327d", "bgFill2": "#6e246f", - "bgFillDark": "#211c21", "bgFillInverted1": "#540556", "bgFillInverted2": "#211c21", "fgFill": "#f4eef4", - "fgFillDark": "#f4eef4", "fgFillInverted": "#f4eef4", - "fgSurface1": "#8a7a8a", "fgSurface2": "#6c606c", - "fgSurface3": "#504650", - "fgSurface4": "#211c21", + "fgSurface3": "#514751", + "fgSurface4": "#463d46", + "fgSurface5": "#000", "stroke1": "#daa3da", - "stroke2": "#d28dd2", + "stroke2": "#c68cc5", "stroke3": "#855185", "stroke4": "#623a62", - "surface1": "#ea9fe9", + "surface1": "#eba0ea", "surface2": "#f5a3f5", - "surface3": "#eeb4ee", - "surface4": "#e898e8", - "surface5": "#e58fe5", + "surface3": "#efb5ef", + "surface4": "#ea9aea", + "surface5": "#e28de2", "surface6": "#de7cdf", }, "warnings": undefined, @@ -13333,25 +12752,23 @@ exports[`buildRamps > background ramp snapshots at lightness 80 1`] = ` "ramp": { "bgFill1": "#784445", "bgFill2": "#6a3739", - "bgFillDark": "#221c1c", "bgFillInverted1": "#4e1e21", "bgFillInverted2": "#221c1c", "fgFill": "#f3efef", - "fgFillDark": "#f3efef", "fgFillInverted": "#f3efef", - "fgSurface1": "#8b7979", "fgSurface2": "#6d5e5e", "fgSurface3": "#504545", - "fgSurface4": "#221c1c", + "fgSurface4": "#463b3b", + "fgSurface5": "#000", "stroke1": "#d5a8a7", - "stroke2": "#cd9494", - "stroke3": "#8b5253", + "stroke2": "#c49595", + "stroke3": "#8a5152", "stroke4": "#663a3b", "surface1": "#e4a6a6", "surface2": "#ebadad", "surface3": "#e9b8b8", - "surface4": "#e2a0a0", - "surface5": "#df9999", + "surface4": "#e4a2a2", + "surface5": "#dc9696", "surface6": "#d9888a", }, "warnings": undefined, @@ -13368,25 +12785,23 @@ exports[`buildRamps > background ramp snapshots at lightness 80 1`] = ` "ramp": { "bgFill1": "#5a581e", "bgFill2": "#4d4b0e", - "bgFillDark": "#1e1e1c", "bgFillInverted1": "#312f00", "bgFillInverted2": "#1e1e1c", "fgFill": "#f5f5bb", - "fgFillDark": "#f5f5bb", "fgFillInverted": "#f5f5bb", - "fgSurface1": "#9e9e96", "fgSurface2": "#7f7f78", "fgSurface3": "#63635e", - "fgSurface4": "#1e1e1c", + "fgSurface4": "#595954", + "fgSurface5": "#141413", "stroke1": "#dedeb2", - "stroke2": "#cbcba3", + "stroke2": "#cbcba0", "stroke3": "#808066", "stroke4": "#63634e", "surface1": "#e3e3a1", "surface2": "#ebebad", "surface3": "#f4f4ad", - "surface4": "#dddd9d", - "surface5": "#d6d699", + "surface4": "#dfdf9f", + "surface5": "#d3d396", "surface6": "#c7c791", }, "warnings": undefined, @@ -13403,25 +12818,23 @@ exports[`buildRamps > background ramp snapshots at lightness 80 1`] = ` "ramp": { "bgFill1": "#2b642e", "bgFill2": "#1c5621", - "bgFillDark": "#1c1f1c", "bgFillInverted1": "#003700", "bgFillInverted2": "#1c1f1c", "fgFill": "#e4f6e4", - "fgFillDark": "#e4f6e4", "fgFillInverted": "#e4f6e4", - "fgSurface1": "#8e978e", "fgSurface2": "#727971", - "fgSurface3": "#575d57", - "fgSurface4": "#1c1f1c", + "fgSurface3": "#585d57", + "fgSurface4": "#4e534e", + "fgSurface5": "#131513", "stroke1": "#acddab", - "stroke2": "#9cc99c", + "stroke2": "#9aca9a", "stroke3": "#617e61", "stroke4": "#496149", "surface1": "#9ce59c", "surface2": "#adebad", "surface3": "#bbf1bb", - "surface4": "#98df99", - "surface5": "#94d895", + "surface4": "#9be29c", + "surface5": "#91d592", "surface6": "#8cc88c", }, "warnings": undefined, @@ -13438,25 +12851,23 @@ exports[`buildRamps > background ramp snapshots at lightness 80 1`] = ` "ramp": { "bgFill1": "#236060", "bgFill2": "#135353", - "bgFillDark": "#1c1f1e", "bgFillInverted1": "#003435", "bgFillInverted2": "#1c1f1e", "fgFill": "#d9f7f7", - "fgFillDark": "#d9f7f7", "fgFillInverted": "#d9f7f7", - "fgSurface1": "#929999", "fgSurface2": "#747b7b", "fgSurface3": "#5a5f5f", - "fgSurface4": "#1c1f1e", + "fgSurface4": "#515555", + "fgSurface5": "#131414", "stroke1": "#b1dcdc", - "stroke2": "#a1c9c9", + "stroke2": "#9fc9c9", "stroke3": "#647e7e", "stroke4": "#4c6161", "surface1": "#a1e3e3", "surface2": "#adebeb", "surface3": "#b0f4f4", - "surface4": "#9edddd", - "surface5": "#9ad6d6", + "surface4": "#a1e0e0", + "surface5": "#98d3d3", "surface6": "#91c7c7", }, "warnings": undefined, @@ -13473,25 +12884,23 @@ exports[`buildRamps > background ramp snapshots at lightness 80 1`] = ` "ramp": { "bgFill1": "#49477c", "bgFill2": "#3d3a6e", - "bgFillDark": "#1d1d25", "bgFillInverted1": "#2b2759", "bgFillInverted2": "#1d1d25", "fgFill": "#f0f0f2", - "fgFillDark": "#f0f0f2", "fgFillInverted": "#f0f0f2", - "fgSurface1": "#747485", "fgSurface2": "#59596b", "fgSurface3": "#3f404d", - "fgSurface4": "#1d1d25", + "fgSurface4": "#363642", + "fgSurface5": "#000", "stroke1": "#a5a6ce", - "stroke2": "#9595c6", + "stroke2": "#9494be", "stroke3": "#56519b", "stroke4": "#3d3971", "surface1": "#a7a7df", "surface2": "#adadeb", "surface3": "#b5b6e5", - "surface4": "#a2a2dd", - "surface5": "#9c9cda", + "surface4": "#a4a4df", + "surface5": "#9a9ad8", "surface6": "#908fd3", }, "warnings": undefined, @@ -13508,25 +12917,23 @@ exports[`buildRamps > background ramp snapshots at lightness 80 1`] = ` "ramp": { "bgFill1": "#733e74", "bgFill2": "#663167", - "bgFillDark": "#211c21", "bgFillInverted1": "#4b174d", "bgFillInverted2": "#211c21", "fgFill": "#f4eef4", - "fgFillDark": "#f4eef4", "fgFillInverted": "#f4eef4", - "fgSurface1": "#8b7d8a", "fgSurface2": "#6d626d", - "fgSurface3": "#514951", - "fgSurface4": "#211c21", + "fgSurface3": "#514851", + "fgSurface4": "#473f47", + "fgSurface5": "#000", "stroke1": "#d7a8d7", - "stroke2": "#ce93ce", + "stroke2": "#c493c3", "stroke3": "#835583", "stroke4": "#613e61", "surface1": "#e5a5e5", "surface2": "#ebadeb", "surface3": "#ebb9ea", - "surface4": "#e39fe3", - "surface5": "#e096e0", + "surface4": "#e4a0e4", + "surface5": "#dd94dd", "surface6": "#d885d9", }, "warnings": undefined, @@ -13543,25 +12950,23 @@ exports[`buildRamps > background ramp snapshots at lightness 80 1`] = ` "ramp": { "bgFill1": "#6d4b4b", "bgFill2": "#5f3e3f", - "bgFillDark": "#221d1d", "bgFillInverted1": "#442526", "bgFillInverted2": "#221d1d", "fgFill": "#f3efef", - "fgFillDark": "#f3efef", "fgFillInverted": "#f3efef", - "fgSurface1": "#8d7d7d", "fgSurface2": "#6e6262", "fgSurface3": "#524848", - "fgSurface4": "#221d1d", + "fgSurface4": "#473f3f", + "fgSurface5": "#000", "stroke1": "#d3aeae", - "stroke2": "#c99b9b", - "stroke3": "#895859", + "stroke2": "#c29c9c", + "stroke3": "#885758", "stroke4": "#664040", "surface1": "#dfaeae", "surface2": "#e0b8b8", "surface3": "#e6c0c0", - "surface4": "#dda8a9", - "surface5": "#daa1a2", + "surface4": "#dfaaab", + "surface5": "#d79e9f", "surface6": "#d29293", }, "warnings": undefined, @@ -13578,25 +12983,23 @@ exports[`buildRamps > background ramp snapshots at lightness 80 1`] = ` "ramp": { "bgFill1": "#585834", "bgFill2": "#4b4b27", - "bgFillDark": "#1e1e1c", "bgFillInverted1": "#302f0a", "bgFillInverted2": "#1e1e1c", "fgFill": "#f4f4c9", - "fgFillDark": "#f4f4c9", "fgFillInverted": "#f4f4c9", - "fgSurface1": "#979791", "fgSurface2": "#797973", "fgSurface3": "#5d5d59", - "fgSurface4": "#1e1e1c", + "fgSurface4": "#535450", + "fgSurface5": "#141413", "stroke1": "#d5d5b2", - "stroke2": "#c2c2a2", - "stroke3": "#7a7a65", + "stroke2": "#c2c2a0", + "stroke3": "#797964", "stroke4": "#5d5d4d", "surface1": "#d9d9a4", "surface2": "#e0e0b8", "surface3": "#eae9b1", - "surface4": "#d3d3a0", - "surface5": "#cdcd9c", + "surface4": "#d5d5a2", + "surface5": "#caca99", "surface6": "#bebe93", }, "warnings": undefined, @@ -13613,25 +13016,23 @@ exports[`buildRamps > background ramp snapshots at lightness 80 1`] = ` "ramp": { "bgFill1": "#3b603c", "bgFill2": "#2f5230", - "bgFillDark": "#1d1f1d", "bgFillInverted1": "#113514", "bgFillInverted2": "#1d1f1d", "fgFill": "#e7f4e7", - "fgFillDark": "#e7f4e7", "fgFillInverted": "#e7f4e7", - "fgSurface1": "#8b928b", "fgSurface2": "#6f746f", - "fgSurface3": "#555955", - "fgSurface4": "#1d1f1d", + "fgSurface3": "#555a55", + "fgSurface4": "#4b504b", + "fgSurface5": "#131513", "stroke1": "#aed5ae", - "stroke2": "#9fc29f", + "stroke2": "#9dc39d", "stroke3": "#627962", "stroke4": "#4a5c4a", - "surface1": "#a3dca3", + "surface1": "#a4dda4", "surface2": "#b8e0b8", - "surface3": "#b6ebb7", - "surface4": "#9fd7a0", - "surface5": "#9ad09b", + "surface3": "#b7ecb8", + "surface4": "#a1d9a2", + "surface5": "#97cd98", "surface6": "#91c092", }, "warnings": undefined, @@ -13648,25 +13049,23 @@ exports[`buildRamps > background ramp snapshots at lightness 80 1`] = ` "ramp": { "bgFill1": "#385d5d", "bgFill2": "#2b5050", - "bgFillDark": "#1d1e1e", "bgFillInverted1": "#0c3333", "bgFillInverted2": "#1d1e1e", "fgFill": "#dff5f5", - "fgFillDark": "#dff5f5", "fgFillInverted": "#dff5f5", - "fgSurface1": "#8e9494", "fgSurface2": "#717676", "fgSurface3": "#575b5b", - "fgSurface4": "#1d1e1e", + "fgSurface4": "#4d5151", + "fgSurface5": "#131414", "stroke1": "#b1d4d4", - "stroke2": "#a2c2c2", + "stroke2": "#a0c2c2", "stroke3": "#647979", "stroke4": "#4c5c5c", "surface1": "#a6dbdb", "surface2": "#b8e0e0", "surface3": "#b3ebeb", - "surface4": "#a2d5d5", - "surface5": "#9dcece", + "surface4": "#a4d7d7", + "surface5": "#9acbcb", "surface6": "#94bfbf", }, "warnings": undefined, @@ -13683,25 +13082,23 @@ exports[`buildRamps > background ramp snapshots at lightness 80 1`] = ` "ramp": { "bgFill1": "#504f73", "bgFill2": "#444265", - "bgFillDark": "#1d1d24", "bgFillInverted1": "#2c2a4a", "bgFillInverted2": "#1d1d24", "fgFill": "#f0f0f2", - "fgFillDark": "#f0f0f2", "fgFillInverted": "#f0f0f2", - "fgSurface1": "#7a7b89", "fgSurface2": "#5f5f6f", - "fgSurface3": "#454552", - "fgSurface4": "#1d1d24", + "fgSurface3": "#464652", + "fgSurface4": "#3c3c47", + "fgSurface5": "#000", "stroke1": "#aeaece", - "stroke2": "#9e9ec4", + "stroke2": "#9e9ebf", "stroke3": "#5d5999", "stroke4": "#434072", - "surface1": "#b0b0dc", + "surface1": "#b1b1dd", "surface2": "#b8b8e0", - "surface3": "#bfbfe3", - "surface4": "#acabda", - "surface5": "#a6a5d6", + "surface3": "#c0c0e4", + "surface4": "#aeaddc", + "surface5": "#a4a3d3", "surface6": "#9998ce", }, "warnings": undefined, @@ -13718,25 +13115,23 @@ exports[`buildRamps > background ramp snapshots at lightness 80 1`] = ` "ramp": { "bgFill1": "#6a476a", "bgFill2": "#5c3a5d", - "bgFillDark": "#201d20", "bgFillInverted1": "#422143", "bgFillInverted2": "#201d20", "fgFill": "#f3eef3", - "fgFillDark": "#f3eef3", "fgFillInverted": "#f3eef3", - "fgSurface1": "#8b808b", "fgSurface2": "#6d646d", - "fgSurface3": "#524b52", - "fgSurface4": "#201d20", + "fgSurface3": "#514a51", + "fgSurface4": "#474147", + "fgSurface5": "#000", "stroke1": "#d4aed4", - "stroke2": "#ca9aca", - "stroke3": "#805a81", + "stroke2": "#c29ac1", + "stroke3": "#7f5980", "stroke4": "#5f4260", - "surface1": "#e0ade0", + "surface1": "#dfacdf", "surface2": "#e0b8e0", - "surface3": "#e7c0e7", - "surface4": "#dda6dd", - "surface5": "#d99fda", + "surface3": "#e6bfe6", + "surface4": "#dfa8df", + "surface5": "#d69cd7", "surface6": "#d18ed2", }, "warnings": undefined, @@ -13753,25 +13148,23 @@ exports[`buildRamps > background ramp snapshots at lightness 80 1`] = ` "ramp": { "bgFill1": "#625050", "bgFill2": "#544444", - "bgFillDark": "#211d1d", "bgFillInverted1": "#39292a", "bgFillInverted2": "#211d1d", "fgFill": "#f3efef", - "fgFillDark": "#f3efef", "fgFillInverted": "#f3efef", - "fgSurface1": "#8e8282", "fgSurface2": "#706666", "fgSurface3": "#544c4c", - "fgSurface4": "#211d1d", + "fgSurface4": "#494343", + "fgSurface5": "#000", "stroke1": "#cfb6b6", - "stroke2": "#c4a4a4", - "stroke3": "#845f60", + "stroke2": "#bfa5a5", + "stroke3": "#835f60", "stroke4": "#634646", "surface1": "#d9b7b7", "surface2": "#d6c2c2", "surface3": "#e2c8c8", - "surface4": "#d6b1b2", - "surface5": "#d2abab", + "surface4": "#d8b3b4", + "surface5": "#cfa8a8", "surface6": "#c99c9c", }, "warnings": undefined, @@ -13788,25 +13181,23 @@ exports[`buildRamps > background ramp snapshots at lightness 80 1`] = ` "ramp": { "bgFill1": "#575745", "bgFill2": "#4a4a39", - "bgFillDark": "#1e1e1d", "bgFillInverted1": "#2f2e1e", "bgFillInverted2": "#1e1e1d", "fgFill": "#f2f2d9", - "fgFillDark": "#f2f2d9", "fgFillInverted": "#f2f2d9", - "fgSurface1": "#90908b", "fgSurface2": "#72726f", "fgSurface3": "#575755", - "fgSurface4": "#1e1e1d", + "fgSurface4": "#4d4d4b", + "fgSurface5": "#141413", "stroke1": "#cbcbb3", - "stroke2": "#bab9a3", + "stroke2": "#bbbaa2", "stroke3": "#737365", "stroke4": "#57574c", "surface1": "#d0cfab", "surface2": "#d6d6c2", "surface3": "#e0dfb8", - "surface4": "#cacaa7", - "surface5": "#c4c3a2", + "surface4": "#cccca9", + "surface5": "#c1c09f", "surface6": "#b6b598", }, "warnings": undefined, @@ -13823,25 +13214,23 @@ exports[`buildRamps > background ramp snapshots at lightness 80 1`] = ` "ramp": { "bgFill1": "#495b49", "bgFill2": "#3d4e3d", - "bgFillDark": "#1d1e1d", "bgFillInverted1": "#213122", "bgFillInverted2": "#1d1e1d", "fgFill": "#ebf3eb", - "fgFillDark": "#ebf3eb", "fgFillInverted": "#ebf3eb", - "fgSurface1": "#898e89", "fgSurface2": "#6c706c", - "fgSurface3": "#525652", - "fgSurface4": "#1d1e1d", + "fgSurface3": "#535653", + "fgSurface4": "#4a4c4a", + "fgSurface5": "#131413", "stroke1": "#b2cdb2", - "stroke2": "#a2bba2", + "stroke2": "#a1bba1", "stroke3": "#647464", "stroke4": "#4a574a", - "surface1": "#acd3ac", + "surface1": "#add4ad", "surface2": "#c2d6c2", - "surface3": "#bbe3bb", - "surface4": "#a7cea8", - "surface5": "#a2c7a3", + "surface3": "#bce4bc", + "surface4": "#a9d0aa", + "surface5": "#a0c5a1", "surface6": "#98b898", }, "warnings": undefined, @@ -13858,25 +13247,23 @@ exports[`buildRamps > background ramp snapshots at lightness 80 1`] = ` "ramp": { "bgFill1": "#475959", "bgFill2": "#3b4c4c", - "bgFillDark": "#1d1e1e", "bgFillInverted1": "#203031", "bgFillInverted2": "#1d1e1e", "fgFill": "#e6f3f3", - "fgFillDark": "#e6f3f3", "fgFillInverted": "#e6f3f3", - "fgSurface1": "#8a8f8f", "fgSurface2": "#6e7171", - "fgSurface3": "#535656", - "fgSurface4": "#1d1e1e", + "fgSurface3": "#545656", + "fgSurface4": "#4a4d4d", + "fgSurface5": "#131414", "stroke1": "#b4cccc", - "stroke2": "#a4baba", + "stroke2": "#a3baba", "stroke3": "#657373", "stroke4": "#4c5757", "surface1": "#add2d2", "surface2": "#c2d6d6", "surface3": "#bbe2e2", - "surface4": "#a9cccc", - "surface5": "#a4c5c5", + "surface4": "#abcece", + "surface5": "#a2c3c3", "surface6": "#99b7b7", }, "warnings": undefined, @@ -13893,25 +13280,23 @@ exports[`buildRamps > background ramp snapshots at lightness 80 1`] = ` "ramp": { "bgFill1": "#535365", "bgFill2": "#474657", - "bgFillDark": "#1e1d22", "bgFillInverted1": "#2c2c3c", "bgFillInverted2": "#1e1d22", "fgFill": "#f0f0f2", - "fgFillDark": "#f0f0f2", "fgFillInverted": "#f0f0f2", - "fgSurface1": "#81828c", "fgSurface2": "#656571", "fgSurface3": "#4b4b55", - "fgSurface4": "#1e1d22", + "fgSurface4": "#42424b", + "fgSurface5": "#000", "stroke1": "#b7b7cd", - "stroke2": "#a6a6c1", + "stroke2": "#a6a6bd", "stroke3": "#646192", "stroke4": "#49476f", "surface1": "#babad7", "surface2": "#c2c2d6", "surface3": "#c9c9e0", - "surface4": "#b5b5d4", - "surface5": "#afaed0", + "surface4": "#b6b6d6", + "surface5": "#adacce", "surface6": "#a2a1c7", }, "warnings": undefined, @@ -13928,25 +13313,23 @@ exports[`buildRamps > background ramp snapshots at lightness 80 1`] = ` "ramp": { "bgFill1": "#604f60", "bgFill2": "#534253", - "bgFillDark": "#201d20", "bgFillInverted1": "#382939", "bgFillInverted2": "#201d20", "fgFill": "#f3eff3", - "fgFillDark": "#f3eff3", "fgFillInverted": "#f3eff3", - "fgSurface1": "#8c838c", "fgSurface2": "#6e676e", - "fgSurface3": "#534d53", - "fgSurface4": "#201d20", + "fgSurface3": "#524d52", + "fgSurface4": "#484348", + "fgSurface5": "#000", "stroke1": "#cfb5cf", - "stroke2": "#c3a2c3", - "stroke3": "#7c607d", + "stroke2": "#bea3be", + "stroke3": "#7b5f7c", "stroke4": "#5d475d", - "surface1": "#d9b6d9", + "surface1": "#d8b5d8", "surface2": "#d6c2d6", - "surface3": "#e2c7e2", - "surface4": "#d5b0d6", - "surface5": "#d1a8d2", + "surface3": "#e1c6e1", + "surface4": "#d6b1d7", + "surface5": "#cea5cf", "surface6": "#c899c8", }, "warnings": undefined, @@ -13963,16 +13346,14 @@ exports[`buildRamps > background ramp snapshots at lightness 80 1`] = ` "ramp": { "bgFill1": "#555", "bgFill2": "#484848", - "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", "fgFill": "#f0f0f0", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", - "fgSurface1": "#898989", "fgSurface2": "#6c6c6c", "fgSurface3": "#525252", - "fgSurface4": "#1e1e1e", + "fgSurface4": "#484848", + "fgSurface5": "#131313", "stroke1": "#c1c1c1", "stroke2": "#b0b0b0", "stroke3": "#6c6c6c", @@ -13980,8 +13361,8 @@ exports[`buildRamps > background ramp snapshots at lightness 80 1`] = ` "surface1": "#c5c5c5", "surface2": "#ccc", "surface3": "#d4d4d4", - "surface4": "#bfbfbf", - "surface5": "#b9b9b9", + "surface4": "#c1c1c1", + "surface5": "#b7b7b7", "surface6": "#acacac", }, "warnings": undefined, @@ -13998,16 +13379,14 @@ exports[`buildRamps > background ramp snapshots at lightness 80 1`] = ` "ramp": { "bgFill1": "#555", "bgFill2": "#484848", - "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", "fgFill": "#f0f0f0", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", - "fgSurface1": "#898989", "fgSurface2": "#6c6c6c", "fgSurface3": "#525252", - "fgSurface4": "#1e1e1e", + "fgSurface4": "#484848", + "fgSurface5": "#131313", "stroke1": "#c1c1c1", "stroke2": "#b0b0b0", "stroke3": "#6c6c6c", @@ -14015,8 +13394,8 @@ exports[`buildRamps > background ramp snapshots at lightness 80 1`] = ` "surface1": "#c5c5c5", "surface2": "#ccc", "surface3": "#d4d4d4", - "surface4": "#bfbfbf", - "surface5": "#b9b9b9", + "surface4": "#c1c1c1", + "surface5": "#b7b7b7", "surface6": "#acacac", }, "warnings": undefined, @@ -14033,16 +13412,14 @@ exports[`buildRamps > background ramp snapshots at lightness 80 1`] = ` "ramp": { "bgFill1": "#555", "bgFill2": "#484848", - "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", "fgFill": "#f0f0f0", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", - "fgSurface1": "#898989", "fgSurface2": "#6c6c6c", "fgSurface3": "#525252", - "fgSurface4": "#1e1e1e", + "fgSurface4": "#484848", + "fgSurface5": "#131313", "stroke1": "#c1c1c1", "stroke2": "#b0b0b0", "stroke3": "#6c6c6c", @@ -14050,8 +13427,8 @@ exports[`buildRamps > background ramp snapshots at lightness 80 1`] = ` "surface1": "#c5c5c5", "surface2": "#ccc", "surface3": "#d4d4d4", - "surface4": "#bfbfbf", - "surface5": "#b9b9b9", + "surface4": "#c1c1c1", + "surface5": "#b7b7b7", "surface6": "#acacac", }, "warnings": undefined, @@ -14068,16 +13445,14 @@ exports[`buildRamps > background ramp snapshots at lightness 80 1`] = ` "ramp": { "bgFill1": "#555", "bgFill2": "#484848", - "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", "fgFill": "#f0f0f0", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", - "fgSurface1": "#898989", "fgSurface2": "#6c6c6c", "fgSurface3": "#525252", - "fgSurface4": "#1e1e1e", + "fgSurface4": "#484848", + "fgSurface5": "#131313", "stroke1": "#c1c1c1", "stroke2": "#b0b0b0", "stroke3": "#6c6c6c", @@ -14085,8 +13460,8 @@ exports[`buildRamps > background ramp snapshots at lightness 80 1`] = ` "surface1": "#c5c5c5", "surface2": "#ccc", "surface3": "#d4d4d4", - "surface4": "#bfbfbf", - "surface5": "#b9b9b9", + "surface4": "#c1c1c1", + "surface5": "#b7b7b7", "surface6": "#acacac", }, "warnings": undefined, @@ -14103,16 +13478,14 @@ exports[`buildRamps > background ramp snapshots at lightness 80 1`] = ` "ramp": { "bgFill1": "#555", "bgFill2": "#484848", - "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", "fgFill": "#f0f0f0", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", - "fgSurface1": "#898989", "fgSurface2": "#6c6c6c", "fgSurface3": "#525252", - "fgSurface4": "#1e1e1e", + "fgSurface4": "#484848", + "fgSurface5": "#131313", "stroke1": "#c1c1c1", "stroke2": "#b0b0b0", "stroke3": "#6c6c6c", @@ -14120,8 +13493,8 @@ exports[`buildRamps > background ramp snapshots at lightness 80 1`] = ` "surface1": "#c5c5c5", "surface2": "#ccc", "surface3": "#d4d4d4", - "surface4": "#bfbfbf", - "surface5": "#b9b9b9", + "surface4": "#c1c1c1", + "surface5": "#b7b7b7", "surface6": "#acacac", }, "warnings": undefined, @@ -14138,16 +13511,14 @@ exports[`buildRamps > background ramp snapshots at lightness 80 1`] = ` "ramp": { "bgFill1": "#555", "bgFill2": "#484848", - "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", "fgFill": "#f0f0f0", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", - "fgSurface1": "#898989", "fgSurface2": "#6c6c6c", "fgSurface3": "#525252", - "fgSurface4": "#1e1e1e", + "fgSurface4": "#484848", + "fgSurface5": "#131313", "stroke1": "#c1c1c1", "stroke2": "#b0b0b0", "stroke3": "#6c6c6c", @@ -14155,8 +13526,8 @@ exports[`buildRamps > background ramp snapshots at lightness 80 1`] = ` "surface1": "#c5c5c5", "surface2": "#ccc", "surface3": "#d4d4d4", - "surface4": "#bfbfbf", - "surface5": "#b9b9b9", + "surface4": "#c1c1c1", + "surface5": "#b7b7b7", "surface6": "#acacac", }, "warnings": undefined, @@ -14178,25 +13549,23 @@ exports[`buildRamps > background ramp snapshots at lightness 90 1`] = ` "ramp": { "bgFill1": "#724849", "bgFill2": "#643b3c", - "bgFillDark": "#231c1c", "bgFillInverted1": "#482224", "bgFillInverted2": "#231c1c", "fgFill": "#f9eded", - "fgFillDark": "#f9eded", "fgFillInverted": "#f9eded", - "fgSurface1": "#a38d8d", "fgSurface2": "#84706f", - "fgSurface3": "#665655", - "fgSurface4": "#231c1c", + "fgSurface3": "#675656", + "fgSurface4": "#5b4c4c", + "fgSurface5": "#181313", "stroke1": "#e8c5c4", - "stroke2": "#e0afaf", + "stroke2": "#d6b0b0", "stroke3": "#ae5c60", "stroke4": "#864648", - "surface1": "#f4c5c5", + "surface1": "#f5c6c6", "surface2": "#fcc", - "surface3": "#f8d9d9", - "surface4": "#f2bfbf", - "surface5": "#f0b7b7", + "surface3": "#f9dada", + "surface4": "#f4c1c1", + "surface5": "#edb4b4", "surface6": "#eaa5a6", }, "warnings": undefined, @@ -14213,25 +13582,23 @@ exports[`buildRamps > background ramp snapshots at lightness 90 1`] = ` "ramp": { "bgFill1": "#59582b", "bgFill2": "#4c4b1d", - "bgFillDark": "#1e1e1c", "bgFillInverted1": "#312f00", "bgFillInverted2": "#1e1e1c", "fgFill": "#f4f4c0", - "fgFillDark": "#f4f4c0", "fgFillInverted": "#f4f4c0", - "fgSurface1": "#aeaea6", "fgSurface2": "#8d8d86", - "fgSurface3": "#6f706a", - "fgSurface4": "#1e1e1c", + "fgSurface3": "#71716b", + "fgSurface4": "#50504c", + "fgSurface5": "#141413", "stroke1": "#f2f2c7", - "stroke2": "#ddddb6", + "stroke2": "#ddddb3", "stroke3": "#8e8e74", "stroke4": "#6f6f5a", - "surface1": "#f7f7b6", + "surface1": "#f9f9b8", "surface2": "#ffc", "surface3": "#fff", - "surface4": "#f1f1b3", - "surface5": "#e9e9ae", + "surface4": "#f3f3b5", + "surface5": "#e6e6ab", "surface6": "#d9d9a5", }, "warnings": undefined, @@ -14248,25 +13615,23 @@ exports[`buildRamps > background ramp snapshots at lightness 90 1`] = ` "ramp": { "bgFill1": "#346236", "bgFill2": "#27542a", - "bgFillDark": "#1c1f1c", "bgFillInverted1": "#05360c", "bgFillInverted2": "#1c1f1c", "fgFill": "#dafbd9", - "fgFillDark": "#dafbd9", "fgFillInverted": "#dafbd9", - "fgSurface1": "#9caa9b", "fgSurface2": "#7e897d", - "fgSurface3": "#636c63", - "fgSurface4": "#1c1f1c", + "fgSurface3": "#626b62", + "fgSurface4": "#444b44", + "fgSurface5": "#121512", "stroke1": "#c2f2c2", - "stroke2": "#9ce39d", - "stroke3": "#629163", + "stroke2": "#9ed49e", + "stroke3": "#619062", "stroke4": "#4b714c", - "surface1": "#baf9ba", + "surface1": "#b9f8b9", "surface2": "#cfc", - "surface3": "#efffef", - "surface4": "#a6f7a7", - "surface5": "#8df392", + "surface3": "#eefeee", + "surface4": "#a8faa9", + "surface5": "#8bf190", "surface6": "#83e287", }, "warnings": undefined, @@ -14283,25 +13648,23 @@ exports[`buildRamps > background ramp snapshots at lightness 90 1`] = ` "ramp": { "bgFill1": "#2f5e5f", "bgFill2": "#215152", - "bgFillDark": "#1c1f1f", "bgFillInverted1": "#003434", "bgFillInverted2": "#1c1f1f", "fgFill": "#cdfbfb", - "fgFillDark": "#cdfbfb", "fgFillInverted": "#cdfbfb", - "fgSurface1": "#a0abaa", "fgSurface2": "#818a8a", - "fgSurface3": "#666d6d", - "fgSurface4": "#1c1f1f", + "fgSurface3": "#656c6c", + "fgSurface4": "#454a4a", + "fgSurface5": "#131515", "stroke1": "#bcf4f4", "stroke2": "#a8e0e0", - "stroke3": "#6a8f8f", + "stroke3": "#698e8e", "stroke4": "#527070", - "surface1": "#b0fafa", + "surface1": "#aff9f9", "surface2": "#cff", - "surface3": "#fff", - "surface4": "#9ef6f6", - "surface5": "#99eeef", + "surface3": "#fefefe", + "surface4": "#a1f9f9", + "surface5": "#96ebec", "surface6": "#93dddd", }, "warnings": undefined, @@ -14318,25 +13681,23 @@ exports[`buildRamps > background ramp snapshots at lightness 90 1`] = ` "ramp": { "bgFill1": "#514f7b", "bgFill2": "#44436d", - "bgFillDark": "#1d1d26", "bgFillInverted1": "#2c2951", "bgFillInverted2": "#1d1d26", "fgFill": "#efeff6", - "fgFillDark": "#efeff6", "fgFillInverted": "#efeff6", - "fgSurface1": "#8b8b9b", "fgSurface2": "#6d6d82", - "fgSurface3": "#535367", - "fgSurface4": "#1d1d26", + "fgSurface3": "#535366", + "fgSurface4": "#49495b", + "fgSurface5": "#13141b", "stroke1": "#c3c3e2", - "stroke2": "#b1b1da", - "stroke3": "#6b65b9", + "stroke2": "#b0b0d1", + "stroke3": "#6b65b8", "stroke4": "#51489b", "surface1": "#c5c5f0", "surface2": "#ccf", "surface3": "#d5d6f4", - "surface4": "#c0c0ee", - "surface5": "#b9b9ec", + "surface4": "#c2c2f0", + "surface5": "#b6b6e9", "surface6": "#abaae5", }, "warnings": undefined, @@ -14353,25 +13714,23 @@ exports[`buildRamps > background ramp snapshots at lightness 90 1`] = ` "ramp": { "bgFill1": "#6e436f", "bgFill2": "#603661", - "bgFillDark": "#211c21", "bgFillInverted1": "#461d47", "bgFillInverted2": "#211c21", "fgFill": "#faebf9", - "fgFillDark": "#faebf9", "fgFillInverted": "#faebf9", - "fgSurface1": "#a38fa2", "fgSurface2": "#827282", - "fgSurface3": "#655865", - "fgSurface4": "#211c21", + "fgSurface3": "#665965", + "fgSurface4": "#5b4f5b", + "fgSurface5": "#171317", "stroke1": "#eac6ea", - "stroke2": "#e1afe1", + "stroke2": "#d5aed4", "stroke3": "#a15fa3", "stroke4": "#7d487e", - "surface1": "#f5c5f5", + "surface1": "#f6c6f6", "surface2": "#fcf", - "surface3": "#f9dbf9", - "surface4": "#f3bef3", - "surface5": "#f0b5f1", + "surface3": "#fadcfa", + "surface4": "#f5c0f5", + "surface5": "#eeb3ef", "surface6": "#eaa2eb", }, "warnings": undefined, @@ -14388,25 +13747,23 @@ exports[`buildRamps > background ramp snapshots at lightness 90 1`] = ` "ramp": { "bgFill1": "#6d4b4c", "bgFill2": "#5f3e3f", - "bgFillDark": "#231c1c", "bgFillInverted1": "#432526", "bgFillInverted2": "#231c1c", "fgFill": "#f8eded", - "fgFillDark": "#f8eded", "fgFillInverted": "#f8eded", - "fgSurface1": "#a48f8f", "fgSurface2": "#857272", - "fgSurface3": "#675857", - "fgSurface4": "#231c1c", + "fgSurface3": "#675757", + "fgSurface4": "#5c4e4e", + "fgSurface5": "#181313", "stroke1": "#e7c8c8", - "stroke2": "#deb3b3", - "stroke3": "#ac6164", + "stroke2": "#d5b4b4", + "stroke3": "#ab6063", "stroke4": "#85494b", "surface1": "#f2c9c9", "surface2": "#fad1d1", "surface3": "#f7dddd", - "surface4": "#f0c3c3", - "surface5": "#edbbbb", + "surface4": "#f2c5c5", + "surface5": "#eab8b8", "surface6": "#e7a9aa", }, "warnings": undefined, @@ -14423,25 +13780,23 @@ exports[`buildRamps > background ramp snapshots at lightness 90 1`] = ` "ramp": { "bgFill1": "#585834", "bgFill2": "#4b4b28", - "bgFillDark": "#1e1e1d", "bgFillInverted1": "#302f0b", "bgFillInverted2": "#1e1e1d", "fgFill": "#f4f4c5", - "fgFillDark": "#f4f4c5", "fgFillInverted": "#f4f4c5", - "fgSurface1": "#aaaaa3", "fgSurface2": "#8a8a84", "fgSurface3": "#6d6d68", - "fgSurface4": "#1e1e1d", + "fgSurface4": "#4a4a46", + "fgSurface5": "#141413", "stroke1": "#ededc8", - "stroke2": "#d9d9b7", - "stroke3": "#8b8b74", + "stroke2": "#d9d9b5", + "stroke3": "#8a8a73", "stroke4": "#6d6d5b", "surface1": "#f2f2bb", "surface2": "#fafad1", "surface3": "#fff", - "surface4": "#ececb7", - "surface5": "#e5e4b2", + "surface4": "#eeeeb9", + "surface5": "#e2e1af", "surface6": "#d4d4a8", }, "warnings": undefined, @@ -14458,25 +13813,23 @@ exports[`buildRamps > background ramp snapshots at lightness 90 1`] = ` "ramp": { "bgFill1": "#3c5f3d", "bgFill2": "#2f5231", - "bgFillDark": "#1c1f1c", "bgFillInverted1": "#123515", "bgFillInverted2": "#1c1f1c", "fgFill": "#ddf9dd", - "fgFillDark": "#ddf9dd", "fgFillInverted": "#ddf9dd", - "fgSurface1": "#9ba79b", "fgSurface2": "#7d877d", - "fgSurface3": "#626a62", - "fgSurface4": "#1c1f1c", + "fgSurface3": "#626962", + "fgSurface4": "#454a45", + "fgSurface5": "#121512", "stroke1": "#c2efc3", - "stroke2": "#a3dea4", - "stroke3": "#668e67", + "stroke2": "#a7d6a7", + "stroke3": "#658d66", "stroke4": "#4f6f4f", - "surface1": "#bbf6bb", + "surface1": "#baf5ba", "surface2": "#d1fad1", - "surface3": "#e9fee9", - "surface4": "#a9f3ab", - "surface5": "#98ee9b", + "surface3": "#e8fde8", + "surface4": "#acf6ae", + "surface5": "#96ec99", "surface6": "#8fdd91", }, "warnings": undefined, @@ -14493,25 +13846,23 @@ exports[`buildRamps > background ramp snapshots at lightness 90 1`] = ` "ramp": { "bgFill1": "#385d5d", "bgFill2": "#2b5050", - "bgFillDark": "#1c1f1f", "bgFillInverted1": "#0d3333", "bgFillInverted2": "#1c1f1f", "fgFill": "#d3f9f9", - "fgFillDark": "#d3f9f9", "fgFillInverted": "#d3f9f9", - "fgSurface1": "#9fa8a8", "fgSurface2": "#808787", - "fgSurface3": "#656b6b", - "fgSurface4": "#1c1f1f", + "fgSurface3": "#656b6a", + "fgSurface4": "#454a4a", + "fgSurface5": "#131414", "stroke1": "#bfefef", - "stroke2": "#aedbdb", + "stroke2": "#acdbdb", "stroke3": "#6e8b8c", "stroke4": "#556d6d", "surface1": "#b2f6f6", "surface2": "#d1fafa", "surface3": "#eaffff", - "surface4": "#aaf0f1", - "surface5": "#a5e8e9", + "surface4": "#adf3f4", + "surface5": "#a3e6e7", "surface6": "#9dd8d8", }, "warnings": undefined, @@ -14528,25 +13879,23 @@ exports[`buildRamps > background ramp snapshots at lightness 90 1`] = ` "ramp": { "bgFill1": "#525173", "bgFill2": "#454466", - "bgFillDark": "#1d1d26", "bgFillInverted1": "#2b2a49", "bgFillInverted2": "#1d1d26", "fgFill": "#efeff7", - "fgFillDark": "#efeff7", "fgFillInverted": "#efeff7", - "fgSurface1": "#8e8e9d", "fgSurface2": "#707084", - "fgSurface3": "#565668", - "fgSurface4": "#1d1d26", + "fgSurface3": "#565669", + "fgSurface4": "#4c4c5d", + "fgSurface5": "#14141a", "stroke1": "#c7c7e3", - "stroke2": "#b5b5d9", + "stroke2": "#b4b4d2", "stroke3": "#6e69b6", "stroke4": "#544c9a", "surface1": "#cacaef", "surface2": "#d1d1fa", "surface3": "#dadaf4", - "surface4": "#c4c4ed", - "surface5": "#bebdea", + "surface4": "#c6c6ef", + "surface5": "#bbbae7", "surface6": "#afafe3", }, "warnings": undefined, @@ -14563,25 +13912,23 @@ exports[`buildRamps > background ramp snapshots at lightness 90 1`] = ` "ramp": { "bgFill1": "#6a476a", "bgFill2": "#5c3a5d", - "bgFillDark": "#211c21", "bgFillInverted1": "#412242", "bgFillInverted2": "#211c21", "fgFill": "#f9ebf9", - "fgFillDark": "#f9ebf9", "fgFillInverted": "#f9ebf9", - "fgSurface1": "#a391a3", "fgSurface2": "#837482", "fgSurface3": "#655a65", - "fgSurface4": "#211c21", + "fgSurface4": "#5b505b", + "fgSurface5": "#171317", "stroke1": "#e8c9e8", - "stroke2": "#dfb1df", + "stroke2": "#d2afd2", "stroke3": "#9f63a1", "stroke4": "#7b4b7c", "surface1": "#f2c9f3", "surface2": "#fad1fa", "surface3": "#f8def8", - "surface4": "#f0c2f1", - "surface5": "#edb9ee", + "surface4": "#f2c4f3", + "surface5": "#eab6eb", "surface6": "#e6a6e7", }, "warnings": undefined, @@ -14598,25 +13945,23 @@ exports[`buildRamps > background ramp snapshots at lightness 90 1`] = ` "ramp": { "bgFill1": "#674e4e", "bgFill2": "#5a4141", - "bgFillDark": "#221c1c", "bgFillInverted1": "#3e2828", "bgFillInverted2": "#221c1c", "fgFill": "#f8eded", - "fgFillDark": "#f8eded", "fgFillInverted": "#f8eded", - "fgSurface1": "#a49292", "fgSurface2": "#857474", "fgSurface3": "#675959", - "fgSurface4": "#221c1c", + "fgSurface4": "#5d5050", + "fgSurface5": "#181313", "stroke1": "#e6cccc", - "stroke2": "#dcb7b7", + "stroke2": "#d4b8b8", "stroke3": "#a96568", "stroke4": "#834d4f", "surface1": "#f0cdcd", "surface2": "#f5d6d6", "surface3": "#f6e1e1", - "surface4": "#edc7c7", - "surface5": "#eabfbf", + "surface4": "#efc9c9", + "surface5": "#e7bcbc", "surface6": "#e3aeaf", }, "warnings": undefined, @@ -14633,25 +13978,23 @@ exports[`buildRamps > background ramp snapshots at lightness 90 1`] = ` "ramp": { "bgFill1": "#57573d", "bgFill2": "#4b4a31", - "bgFillDark": "#1e1e1d", "bgFillInverted1": "#2f2f16", "bgFillInverted2": "#1e1e1d", "fgFill": "#f4f3ca", - "fgFillDark": "#f4f3ca", "fgFillInverted": "#f4f3ca", - "fgSurface1": "#a7a7a1", "fgSurface2": "#878782", "fgSurface3": "#6a6a66", - "fgSurface4": "#1e1e1d", + "fgSurface4": "#494946", + "fgSurface5": "#141413", "stroke1": "#e8e8c9", - "stroke2": "#d5d5b8", - "stroke3": "#888874", + "stroke2": "#d5d5b7", + "stroke3": "#878773", "stroke4": "#6a6a5a", "surface1": "#ededbe", "surface2": "#f5f5d6", "surface3": "#ffffd0", - "surface4": "#e7e7ba", - "surface5": "#e0e0b5", + "surface4": "#e9e9bc", + "surface5": "#ddddb2", "surface6": "#d0d0aa", }, "warnings": undefined, @@ -14668,25 +14011,23 @@ exports[`buildRamps > background ramp snapshots at lightness 90 1`] = ` "ramp": { "bgFill1": "#435d43", "bgFill2": "#365037", - "bgFillDark": "#1c1f1c", "bgFillInverted1": "#1b331c", "bgFillInverted2": "#1c1f1c", "fgFill": "#e1f8e0", - "fgFillDark": "#e1f8e0", "fgFillInverted": "#e1f8e0", - "fgSurface1": "#9ba49b", "fgSurface2": "#7d857d", "fgSurface3": "#626862", - "fgSurface4": "#1c1f1c", + "fgSurface4": "#454a45", + "fgSurface5": "#121412", "stroke1": "#c4ebc4", - "stroke2": "#aadaaa", + "stroke2": "#add5ad", "stroke3": "#6a8a6b", "stroke4": "#526c52", "surface1": "#bdf2be", "surface2": "#d6f5d6", "surface3": "#e7fbe7", - "surface4": "#aeefaf", - "surface5": "#a2e8a4", + "surface4": "#b0f2b1", + "surface5": "#a0e6a2", "surface6": "#98d79a", }, "warnings": undefined, @@ -14703,25 +14044,23 @@ exports[`buildRamps > background ramp snapshots at lightness 90 1`] = ` "ramp": { "bgFill1": "#405b5b", "bgFill2": "#344e4e", - "bgFillDark": "#1d1e1e", "bgFillInverted1": "#183232", "bgFillInverted2": "#1d1e1e", "fgFill": "#d9f7f7", - "fgFillDark": "#d9f7f7", "fgFillInverted": "#d9f7f7", - "fgSurface1": "#9ea5a5", "fgSurface2": "#7f8585", "fgSurface3": "#646868", - "fgSurface4": "#1d1e1e", + "fgSurface4": "#464949", + "fgSurface5": "#131414", "stroke1": "#c4eaea", - "stroke2": "#b3d6d6", + "stroke2": "#b1d6d6", "stroke3": "#718888", "stroke4": "#576a6a", "surface1": "#b8f1f1", "surface2": "#d6f5f5", "surface3": "#e6fcfc", - "surface4": "#b3ebeb", - "surface5": "#aee3e3", + "surface4": "#b6eeee", + "surface5": "#ace1e1", "surface6": "#a4d3d3", }, "warnings": undefined, @@ -14738,25 +14077,23 @@ exports[`buildRamps > background ramp snapshots at lightness 90 1`] = ` "ramp": { "bgFill1": "#53526c", "bgFill2": "#46455e", - "bgFillDark": "#1d1d25", "bgFillInverted1": "#2c2b42", "bgFillInverted2": "#1d1d25", "fgFill": "#efeff7", - "fgFillDark": "#efeff7", "fgFillInverted": "#efeff7", - "fgSurface1": "#91929f", "fgSurface2": "#737385", "fgSurface3": "#58586a", - "fgSurface4": "#1d1d25", + "fgSurface4": "#4f4f5f", + "fgSurface5": "#14141a", "stroke1": "#cccce3", - "stroke2": "#b9b9d9", + "stroke2": "#b8b8d1", "stroke3": "#726db1", "stroke4": "#575098", "surface1": "#ceceed", "surface2": "#d6d6f5", "surface3": "#dfdff4", - "surface4": "#c9c9eb", - "surface5": "#c2c2e8", + "surface4": "#cbcbed", + "surface5": "#bfbfe5", "surface6": "#b4b3e0", }, "warnings": undefined, @@ -14773,25 +14110,23 @@ exports[`buildRamps > background ramp snapshots at lightness 90 1`] = ` "ramp": { "bgFill1": "#654b65", "bgFill2": "#573e58", - "bgFillDark": "#211c21", "bgFillInverted1": "#3d253d", "bgFillInverted2": "#211c21", "fgFill": "#f8ecf8", - "fgFillDark": "#f8ecf8", "fgFillInverted": "#f8ecf8", - "fgSurface1": "#a393a3", "fgSurface2": "#837683", - "fgSurface3": "#655b65", - "fgSurface4": "#211c21", + "fgSurface3": "#665b66", + "fgSurface4": "#5b515b", + "fgSurface5": "#171316", "stroke1": "#e7cce7", - "stroke2": "#dcb5dc", + "stroke2": "#d2b4d2", "stroke3": "#9c679d", "stroke4": "#794f7a", "surface1": "#f0cdf0", "surface2": "#f5d6f5", "surface3": "#f7e2f7", - "surface4": "#eec6ee", - "surface5": "#eabdeb", + "surface4": "#efc7ef", + "surface5": "#e7bae8", "surface6": "#e2abe3", }, "warnings": undefined, @@ -14808,25 +14143,23 @@ exports[`buildRamps > background ramp snapshots at lightness 90 1`] = ` "ramp": { "bgFill1": "#615051", "bgFill2": "#544444", - "bgFillDark": "#221d1d", "bgFillInverted1": "#39292a", "bgFillInverted2": "#221d1d", "fgFill": "#f7eeee", - "fgFillDark": "#f7eeee", "fgFillInverted": "#f7eeee", - "fgSurface1": "#a39595", "fgSurface2": "#857676", - "fgSurface3": "#675c5c", - "fgSurface4": "#221d1d", + "fgSurface3": "#675b5b", + "fgSurface4": "#5d5252", + "fgSurface5": "#171313", "stroke1": "#e5d0d0", - "stroke2": "#d9bbbb", - "stroke3": "#a46a6c", + "stroke2": "#d3bcbc", + "stroke3": "#a3696b", "stroke4": "#7f5153", - "surface1": "#edd2d2", + "surface1": "#ecd1d1", "surface2": "#f0dbdb", - "surface3": "#f5e5e5", - "surface4": "#eacbcb", - "surface5": "#e7c3c4", + "surface3": "#f4e4e4", + "surface4": "#eccdcd", + "surface5": "#e4c0c1", "surface6": "#deb3b3", }, "warnings": undefined, @@ -14843,25 +14176,23 @@ exports[`buildRamps > background ramp snapshots at lightness 90 1`] = ` "ramp": { "bgFill1": "#575745", "bgFill2": "#4a4a39", - "bgFillDark": "#1e1e1d", "bgFillInverted1": "#2f2e1e", "bgFillInverted2": "#1e1e1d", "fgFill": "#f3f3d1", - "fgFillDark": "#f3f3d1", "fgFillInverted": "#f3f3d1", - "fgSurface1": "#a4a49f", "fgSurface2": "#848480", - "fgSurface3": "#686864", - "fgSurface4": "#1e1e1d", + "fgSurface3": "#686865", + "fgSurface4": "#494947", + "fgSurface5": "#141413", "stroke1": "#e4e4ca", - "stroke2": "#d1d1b9", + "stroke2": "#d1d1b8", "stroke3": "#858575", "stroke4": "#67675b", "surface1": "#e9e9c3", "surface2": "#f0f0db", "surface3": "#fafad0", - "surface4": "#e3e3be", - "surface5": "#dcdbb8", + "surface4": "#e5e5c0", + "surface5": "#d9d8b5", "surface6": "#ccccad", }, "warnings": undefined, @@ -14878,25 +14209,23 @@ exports[`buildRamps > background ramp snapshots at lightness 90 1`] = ` "ramp": { "bgFill1": "#495b4a", "bgFill2": "#3d4e3d", - "bgFillDark": "#1d1f1d", "bgFillInverted1": "#223122", "bgFillInverted2": "#1d1f1d", "fgFill": "#e4f6e4", - "fgFillDark": "#e4f6e4", "fgFillInverted": "#e4f6e4", - "fgSurface1": "#9ba29a", "fgSurface2": "#7c827c", - "fgSurface3": "#616661", - "fgSurface4": "#1d1f1d", + "fgSurface3": "#616761", + "fgSurface4": "#454945", + "fgSurface5": "#0e0f0e", "stroke1": "#c6e7c6", - "stroke2": "#b0d5b0", + "stroke2": "#b2d3b2", "stroke3": "#6e876f", "stroke4": "#556955", - "surface1": "#c1edc2", + "surface1": "#c2eec3", "surface2": "#dbf0db", - "surface3": "#e5f8e5", - "surface4": "#b5eab6", - "surface5": "#ace3ad", + "surface3": "#e6f9e6", + "surface4": "#b8edb9", + "surface5": "#aae0ab", "surface6": "#a2d2a3", }, "warnings": undefined, @@ -14913,25 +14242,23 @@ exports[`buildRamps > background ramp snapshots at lightness 90 1`] = ` "ramp": { "bgFill1": "#485959", "bgFill2": "#3b4c4c", - "bgFillDark": "#1d1e1e", "bgFillInverted1": "#203031", "bgFillInverted2": "#1d1e1e", "fgFill": "#dff6f6", - "fgFillDark": "#dff6f6", "fgFillInverted": "#dff6f6", - "fgSurface1": "#9da2a2", "fgSurface2": "#7e8282", - "fgSurface3": "#636666", - "fgSurface4": "#1d1e1e", + "fgSurface3": "#636767", + "fgSurface4": "#464949", + "fgSurface5": "#121313", "stroke1": "#c8e5e5", - "stroke2": "#b7d2d2", + "stroke2": "#b6d2d2", "stroke3": "#738585", "stroke4": "#596767", - "surface1": "#c1ebec", + "surface1": "#c2eced", "surface2": "#dbf0f0", - "surface3": "#e2f9f9", - "surface4": "#bce5e6", - "surface5": "#b6dede", + "surface3": "#e3fafa", + "surface4": "#bfe8e9", + "surface5": "#b3dbdb", "surface6": "#abcece", }, "warnings": undefined, @@ -14948,25 +14275,23 @@ exports[`buildRamps > background ramp snapshots at lightness 90 1`] = ` "ramp": { "bgFill1": "#545364", "bgFill2": "#474657", - "bgFillDark": "#1d1d24", "bgFillInverted1": "#2c2c3b", "bgFillInverted2": "#1d1d24", "fgFill": "#efeff6", - "fgFillDark": "#efeff6", "fgFillInverted": "#efeff6", - "fgSurface1": "#9595a0", "fgSurface2": "#777785", "fgSurface3": "#5b5b6b", - "fgSurface4": "#1d1d24", + "fgSurface4": "#515160", + "fgSurface5": "#141419", "stroke1": "#d0d0e3", - "stroke2": "#bdbdd7", - "stroke3": "#7572ab", + "stroke2": "#bdbdd1", + "stroke3": "#7471aa", "stroke4": "#595493", "surface1": "#d3d3eb", "surface2": "#dbdbf0", "surface3": "#e4e4f3", - "surface4": "#cdcde9", - "surface5": "#c7c6e5", + "surface4": "#cfcfeb", + "surface5": "#c4c3e2", "surface6": "#b8b7dd", }, "warnings": undefined, @@ -14983,25 +14308,23 @@ exports[`buildRamps > background ramp snapshots at lightness 90 1`] = ` "ramp": { "bgFill1": "#604f60", "bgFill2": "#534253", - "bgFillDark": "#201d20", "bgFillInverted1": "#382938", "bgFillInverted2": "#201d20", "fgFill": "#f7ecf7", - "fgFillDark": "#f7ecf7", "fgFillInverted": "#f7ecf7", - "fgSurface1": "#a295a2", "fgSurface2": "#827882", - "fgSurface3": "#655d65", - "fgSurface4": "#201d20", + "fgSurface3": "#655c65", + "fgSurface4": "#5b535b", + "fgSurface5": "#161316", "stroke1": "#e5cfe5", - "stroke2": "#d9b9d9", - "stroke3": "#986c99", + "stroke2": "#d1b9d1", + "stroke3": "#976b98", "stroke4": "#765377", - "surface1": "#edd1ed", + "surface1": "#ecd0ec", "surface2": "#f0dbf0", - "surface3": "#f6e5f6", - "surface4": "#eacaea", - "surface5": "#e6c2e7", + "surface3": "#f5e4f5", + "surface4": "#ecccec", + "surface5": "#e3bfe4", "surface6": "#deb0de", }, "warnings": undefined, @@ -15018,25 +14341,23 @@ exports[`buildRamps > background ramp snapshots at lightness 90 1`] = ` "ramp": { "bgFill1": "#5b5353", "bgFill2": "#4e4646", - "bgFillDark": "#211d1d", "bgFillInverted1": "#332b2c", "bgFillInverted2": "#211d1d", "fgFill": "#f5eeee", - "fgFillDark": "#f5eeee", "fgFillInverted": "#f5eeee", - "fgSurface1": "#a29898", "fgSurface2": "#847979", - "fgSurface3": "#675e5e", - "fgSurface4": "#211d1d", + "fgSurface3": "#665d5d", + "fgSurface4": "#5d5454", + "fgSurface5": "#161313", "stroke1": "#e2d4d4", - "stroke2": "#d5c0c0", - "stroke3": "#9c7172", + "stroke2": "#d0c1c1", + "stroke3": "#9a6f70", "stroke4": "#795757", - "surface1": "#e9d6d6", + "surface1": "#e8d5d5", "surface2": "#ebe0e0", - "surface3": "#f4e9e9", - "surface4": "#e6d0d0", - "surface5": "#e1c9c9", + "surface3": "#f3e8e8", + "surface4": "#e8d2d2", + "surface5": "#dec6c6", "surface6": "#d7b9b9", }, "warnings": undefined, @@ -15053,25 +14374,23 @@ exports[`buildRamps > background ramp snapshots at lightness 90 1`] = ` "ramp": { "bgFill1": "#56564d", "bgFill2": "#494941", - "bgFillDark": "#1e1e1d", "bgFillInverted1": "#2e2e26", "bgFillInverted2": "#1e1e1d", "fgFill": "#f2f2da", - "fgFillDark": "#f2f2da", "fgFillInverted": "#f2f2da", - "fgSurface1": "#a0a09d", "fgSurface2": "#81817e", "fgSurface3": "#656563", - "fgSurface4": "#1e1e1d", + "fgSurface4": "#474746", + "fgSurface5": "#0d0d0d", "stroke1": "#e0e0ce", - "stroke2": "#ccccbc", + "stroke2": "#ccb", "stroke3": "#818177", "stroke4": "#64645c", "surface1": "#e4e4c9", "surface2": "#ebebe0", "surface3": "#f5f4d8", - "surface4": "#dedec4", - "surface5": "#d7d6be", + "surface4": "#e0e0c6", + "surface5": "#d4d3bb", "surface6": "#c8c7b2", }, "warnings": undefined, @@ -15088,25 +14407,23 @@ exports[`buildRamps > background ramp snapshots at lightness 90 1`] = ` "ramp": { "bgFill1": "#4f584f", "bgFill2": "#434b43", - "bgFillDark": "#1d1e1d", "bgFillInverted1": "#283028", "bgFillInverted2": "#1d1e1d", "fgFill": "#e8f4e8", - "fgFillDark": "#e8f4e8", "fgFillInverted": "#e8f4e8", - "fgSurface1": "#9a9f9a", "fgSurface2": "#7c807c", - "fgSurface3": "#616461", - "fgSurface4": "#1d1e1d", + "fgSurface3": "#626561", + "fgSurface4": "#575b57", + "fgSurface5": "#131413", "stroke1": "#cbe3cb", - "stroke2": "#b7d0b7", + "stroke2": "#b8d0b8", "stroke3": "#738473", "stroke4": "#596659", - "surface1": "#c8e8c9", + "surface1": "#c9e9ca", "surface2": "#e0ebe0", - "surface3": "#e5f4e5", - "surface4": "#bfe3c0", - "surface5": "#b8dcb8", + "surface3": "#e6f5e6", + "surface4": "#c2e6c3", + "surface5": "#b6dab6", "surface6": "#acccac", }, "warnings": undefined, @@ -15123,25 +14440,23 @@ exports[`buildRamps > background ramp snapshots at lightness 90 1`] = ` "ramp": { "bgFill1": "#4f5757", "bgFill2": "#424a4a", - "bgFillDark": "#1d1e1e", "bgFillInverted1": "#272f2f", "bgFillInverted2": "#1d1e1e", "fgFill": "#e5f4f4", - "fgFillDark": "#e5f4f4", "fgFillInverted": "#e5f4f4", - "fgSurface1": "#9c9f9f", "fgSurface2": "#7d8080", "fgSurface3": "#626464", - "fgSurface4": "#1d1e1e", + "fgSurface4": "#585a5a", + "fgSurface5": "#141414", "stroke1": "#cde1e1", - "stroke2": "#bccdcd", + "stroke2": "#bacdcd", "stroke3": "#768282", "stroke4": "#5b6565", "surface1": "#cae6e6", "surface2": "#e0ebeb", "surface3": "#e3f5f5", - "surface4": "#c5e0e0", - "surface5": "#bfd9d9", + "surface4": "#c7e2e2", + "surface5": "#bcd6d6", "surface6": "#b2c9c9", }, "warnings": undefined, @@ -15158,25 +14473,23 @@ exports[`buildRamps > background ramp snapshots at lightness 90 1`] = ` "ramp": { "bgFill1": "#54545d", "bgFill2": "#484750", - "bgFillDark": "#1e1d22", "bgFillInverted1": "#2d2d34", "bgFillInverted2": "#1e1d22", "fgFill": "#efeff5", - "fgFillDark": "#efeff5", "fgFillInverted": "#efeff5", - "fgSurface1": "#9898a0", "fgSurface2": "#7a7a84", "fgSurface3": "#5e5e6a", - "fgSurface4": "#1e1d22", + "fgSurface4": "#54545f", + "fgSurface5": "#141418", "stroke1": "#d4d4e1", - "stroke2": "#c2c1d4", + "stroke2": "#c2c1cf", "stroke3": "#7876a1", "stroke4": "#5c5988", "surface1": "#d8d7e8", "surface2": "#e0e0eb", "surface3": "#e9e9f2", - "surface4": "#d2d2e5", - "surface5": "#cbcbe1", + "surface4": "#d4d4e7", + "surface5": "#c8c8de", "surface6": "#bdbcd7", }, "warnings": undefined, @@ -15193,25 +14506,23 @@ exports[`buildRamps > background ramp snapshots at lightness 90 1`] = ` "ramp": { "bgFill1": "#5b525b", "bgFill2": "#4e454e", - "bgFillDark": "#201d20", "bgFillInverted1": "#332b33", "bgFillInverted2": "#201d20", "fgFill": "#f5edf5", - "fgFillDark": "#f5edf5", "fgFillInverted": "#f5edf5", - "fgSurface1": "#a198a1", "fgSurface2": "#827a82", - "fgSurface3": "#655f65", - "fgSurface4": "#201d20", + "fgSurface3": "#655e65", + "fgSurface4": "#5b555b", + "fgSurface5": "#161416", "stroke1": "#e2d3e2", - "stroke2": "#d5bed5", - "stroke3": "#927292", + "stroke2": "#cfbecf", + "stroke3": "#917191", "stroke4": "#715871", "surface1": "#e9d5e9", "surface2": "#ebe0eb", "surface3": "#f4e8f4", - "surface4": "#e5cfe6", - "surface5": "#e1c7e1", + "surface4": "#e7d1e8", + "surface5": "#dec4de", "surface6": "#d7b6d7", }, "warnings": undefined, @@ -15228,25 +14539,23 @@ exports[`buildRamps > background ramp snapshots at lightness 90 1`] = ` "ramp": { "bgFill1": "#555", "bgFill2": "#484848", - "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", "fgFill": "#f0f0f0", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", - "fgSurface1": "#9c9c9c", "fgSurface2": "#7d7d7d", "fgSurface3": "#626262", - "fgSurface4": "#1e1e1e", + "fgSurface4": "#585858", + "fgSurface5": "#141414", "stroke1": "#dadada", "stroke2": "#c7c7c7", "stroke3": "#7d7d7d", "stroke4": "#616161", - "surface1": "#ddd", + "surface1": "#dedede", "surface2": "#e6e6e6", - "surface3": "#eee", - "surface4": "#d7d7d7", - "surface5": "#d1d1d1", + "surface3": "#efefef", + "surface4": "#dadada", + "surface5": "#cecece", "surface6": "#c2c2c2", }, "warnings": undefined, @@ -15263,25 +14572,23 @@ exports[`buildRamps > background ramp snapshots at lightness 90 1`] = ` "ramp": { "bgFill1": "#555", "bgFill2": "#484848", - "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", "fgFill": "#f0f0f0", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", - "fgSurface1": "#9c9c9c", "fgSurface2": "#7d7d7d", "fgSurface3": "#626262", - "fgSurface4": "#1e1e1e", + "fgSurface4": "#585858", + "fgSurface5": "#141414", "stroke1": "#dadada", "stroke2": "#c7c7c7", "stroke3": "#7d7d7d", "stroke4": "#616161", - "surface1": "#ddd", + "surface1": "#dedede", "surface2": "#e6e6e6", - "surface3": "#eee", - "surface4": "#d7d7d7", - "surface5": "#d1d1d1", + "surface3": "#efefef", + "surface4": "#dadada", + "surface5": "#cecece", "surface6": "#c2c2c2", }, "warnings": undefined, @@ -15298,25 +14605,23 @@ exports[`buildRamps > background ramp snapshots at lightness 90 1`] = ` "ramp": { "bgFill1": "#555", "bgFill2": "#484848", - "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", "fgFill": "#f0f0f0", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", - "fgSurface1": "#9c9c9c", "fgSurface2": "#7d7d7d", "fgSurface3": "#626262", - "fgSurface4": "#1e1e1e", + "fgSurface4": "#585858", + "fgSurface5": "#141414", "stroke1": "#dadada", "stroke2": "#c7c7c7", "stroke3": "#7d7d7d", "stroke4": "#616161", - "surface1": "#ddd", + "surface1": "#dedede", "surface2": "#e6e6e6", - "surface3": "#eee", - "surface4": "#d7d7d7", - "surface5": "#d1d1d1", + "surface3": "#efefef", + "surface4": "#dadada", + "surface5": "#cecece", "surface6": "#c2c2c2", }, "warnings": undefined, @@ -15333,25 +14638,23 @@ exports[`buildRamps > background ramp snapshots at lightness 90 1`] = ` "ramp": { "bgFill1": "#555", "bgFill2": "#484848", - "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", "fgFill": "#f0f0f0", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", - "fgSurface1": "#9c9c9c", "fgSurface2": "#7d7d7d", "fgSurface3": "#626262", - "fgSurface4": "#1e1e1e", + "fgSurface4": "#585858", + "fgSurface5": "#141414", "stroke1": "#dadada", "stroke2": "#c7c7c7", "stroke3": "#7d7d7d", "stroke4": "#616161", - "surface1": "#ddd", + "surface1": "#dedede", "surface2": "#e6e6e6", - "surface3": "#eee", - "surface4": "#d7d7d7", - "surface5": "#d1d1d1", + "surface3": "#efefef", + "surface4": "#dadada", + "surface5": "#cecece", "surface6": "#c2c2c2", }, "warnings": undefined, @@ -15368,25 +14671,23 @@ exports[`buildRamps > background ramp snapshots at lightness 90 1`] = ` "ramp": { "bgFill1": "#555", "bgFill2": "#484848", - "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", "fgFill": "#f0f0f0", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", - "fgSurface1": "#9c9c9c", "fgSurface2": "#7d7d7d", "fgSurface3": "#626262", - "fgSurface4": "#1e1e1e", + "fgSurface4": "#585858", + "fgSurface5": "#141414", "stroke1": "#dadada", "stroke2": "#c7c7c7", "stroke3": "#7d7d7d", "stroke4": "#616161", - "surface1": "#ddd", + "surface1": "#dedede", "surface2": "#e6e6e6", - "surface3": "#eee", - "surface4": "#d7d7d7", - "surface5": "#d1d1d1", + "surface3": "#efefef", + "surface4": "#dadada", + "surface5": "#cecece", "surface6": "#c2c2c2", }, "warnings": undefined, @@ -15403,25 +14704,23 @@ exports[`buildRamps > background ramp snapshots at lightness 90 1`] = ` "ramp": { "bgFill1": "#555", "bgFill2": "#484848", - "bgFillDark": "#1e1e1e", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", "fgFill": "#f0f0f0", - "fgFillDark": "#f0f0f0", "fgFillInverted": "#f0f0f0", - "fgSurface1": "#9c9c9c", "fgSurface2": "#7d7d7d", "fgSurface3": "#626262", - "fgSurface4": "#1e1e1e", + "fgSurface4": "#585858", + "fgSurface5": "#141414", "stroke1": "#dadada", "stroke2": "#c7c7c7", "stroke3": "#7d7d7d", "stroke4": "#616161", - "surface1": "#ddd", + "surface1": "#dedede", "surface2": "#e6e6e6", - "surface3": "#eee", - "surface4": "#d7d7d7", - "surface5": "#d1d1d1", + "surface3": "#efefef", + "surface4": "#dadada", + "surface5": "#cecece", "surface6": "#c2c2c2", }, "warnings": undefined, @@ -15445,7 +14744,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFill2": "#484848", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#cbcbcb", + "fgFill": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", @@ -15478,7 +14777,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFill2": "#484848", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#cbcbcb", + "fgFill": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", @@ -15511,7 +14810,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFill2": "#484848", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#cbcbcb", + "fgFill": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", @@ -15544,7 +14843,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFill2": "#484848", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#cbcbcb", + "fgFill": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", @@ -15577,7 +14876,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFill2": "#484848", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#cbcbcb", + "fgFill": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", @@ -15610,7 +14909,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFill2": "#484848", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#cbcbcb", + "fgFill": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", @@ -15643,7 +14942,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFill2": "#484848", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#cbcbcb", + "fgFill": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", @@ -15676,7 +14975,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFill2": "#484848", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#cbcbcb", + "fgFill": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", @@ -15709,7 +15008,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFill2": "#484848", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#cbcbcb", + "fgFill": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", @@ -15742,7 +15041,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFill2": "#484848", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#cbcbcb", + "fgFill": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", @@ -15775,7 +15074,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFill2": "#484848", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#cbcbcb", + "fgFill": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", @@ -15808,7 +15107,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFill2": "#484848", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#cbcbcb", + "fgFill": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", @@ -15841,7 +15140,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFill2": "#484848", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#cbcbcb", + "fgFill": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", @@ -15874,7 +15173,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFill2": "#484848", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#cbcbcb", + "fgFill": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", @@ -15907,7 +15206,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFill2": "#484848", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#cbcbcb", + "fgFill": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", @@ -15940,7 +15239,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFill2": "#484848", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#cbcbcb", + "fgFill": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", @@ -15973,7 +15272,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFill2": "#484848", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#cbcbcb", + "fgFill": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", @@ -16006,7 +15305,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFill2": "#484848", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#cbcbcb", + "fgFill": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", @@ -16039,7 +15338,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFill2": "#484848", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#cbcbcb", + "fgFill": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", @@ -16072,7 +15371,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFill2": "#484848", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#cbcbcb", + "fgFill": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", @@ -16105,7 +15404,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFill2": "#484848", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#cbcbcb", + "fgFill": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", @@ -16138,7 +15437,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFill2": "#484848", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#cbcbcb", + "fgFill": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", @@ -16171,7 +15470,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFill2": "#484848", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#cbcbcb", + "fgFill": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", @@ -16204,7 +15503,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFill2": "#484848", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#cbcbcb", + "fgFill": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", @@ -16237,7 +15536,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFill2": "#484848", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#cbcbcb", + "fgFill": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", @@ -16270,7 +15569,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFill2": "#484848", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#cbcbcb", + "fgFill": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", @@ -16303,7 +15602,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFill2": "#484848", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#cbcbcb", + "fgFill": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", @@ -16336,7 +15635,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFill2": "#484848", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#cbcbcb", + "fgFill": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", @@ -16369,7 +15668,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFill2": "#484848", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#cbcbcb", + "fgFill": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", @@ -16402,7 +15701,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFill2": "#484848", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#cbcbcb", + "fgFill": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", @@ -16435,7 +15734,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFill2": "#484848", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#cbcbcb", + "fgFill": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", @@ -16468,7 +15767,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFill2": "#484848", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#cbcbcb", + "fgFill": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", @@ -16501,7 +15800,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFill2": "#484848", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#cbcbcb", + "fgFill": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", @@ -16534,7 +15833,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFill2": "#484848", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#cbcbcb", + "fgFill": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", @@ -16567,7 +15866,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFill2": "#484848", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#cbcbcb", + "fgFill": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", @@ -16600,7 +15899,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "bgFill2": "#484848", "bgFillInverted1": "#2d2d2d", "bgFillInverted2": "#1e1e1e", - "fgFill": "#cbcbcb", + "fgFill": "#f0f0f0", "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", diff --git a/packages/theme/src/color-ramps/test/color-space-registration.test.ts b/packages/theme/src/color-ramps/test/color-space-registration.test.ts index ae1fe43ed51275..8d3dda70ae7047 100644 --- a/packages/theme/src/color-ramps/test/color-space-registration.test.ts +++ b/packages/theme/src/color-ramps/test/color-space-registration.test.ts @@ -18,37 +18,32 @@ describe( 'color-space registration', () => { expect( Object.keys( ColorSpace.registry ) ).toEqual( [] ); } ); - it.each( [ - 'assertValidSeedColor', - 'clampToGamut', - 'getColorString', - 'getContrast', - ] as const )( '%s works without prior registration', async ( name ) => { - const utils = await import( '../lib/color-utils' ); - // Do not let an import-time registration satisfy the utility's needs. - ColorSpace.registry = {}; + it.each( [ 'parseSeedColor', 'getColorString', 'getContrast' ] as const )( + '%s works without prior registration', + async ( name ) => { + const utils = await import( '../lib/color-utils' ); + // Do not let an import-time registration satisfy the utility's needs. + ColorSpace.registry = {}; - switch ( name ) { - case 'assertValidSeedColor': - expect( () => - utils.assertValidSeedColor( '#3858e9' ) - ).not.toThrow(); - break; - case 'clampToGamut': - expect( utils.clampToGamut( '#3858e9' ).space.id ).toBe( - 'oklch' - ); - break; - case 'getColorString': - expect( utils.getColorString( '#3858e9' ) ).toBe( '#3858e9' ); - break; - case 'getContrast': - expect( utils.getContrast( '#000000', '#ffffff' ) ).toBeCloseTo( - 21 - ); - break; + switch ( name ) { + case 'parseSeedColor': + expect( () => + utils.parseSeedColor( '#3858e9' ) + ).not.toThrow(); + break; + case 'getColorString': + expect( utils.getColorString( '#3858e9' ) ).toBe( + '#3858e9' + ); + break; + case 'getContrast': + expect( + utils.getContrast( '#000000', '#ffffff' ) + ).toBeCloseTo( 21 ); + break; + } } - } ); + ); it( 'builds a background ramp without prior registration', async () => { const { buildBgRamp } = await import( '..' ); diff --git a/packages/theme/src/color-ramps/test/index.test.ts b/packages/theme/src/color-ramps/test/index.test.ts index b8a87f7201ae1b..61e185ffa13664 100644 --- a/packages/theme/src/color-ramps/test/index.test.ts +++ b/packages/theme/src/color-ramps/test/index.test.ts @@ -1,6 +1,17 @@ import { describe, expect, it } from 'vitest'; -import { getLuminance, serialize, to, HSL, sRGB } from 'colorjs.io/fn'; -import { buildAccentRamp, buildBgRamp } from '..'; +import { + contrastAPCA, + deltaEOK2, + get, + getLuminance, + serialize, + to, + HSL, + OKLCH_sRGB as OklchSrgb, + OKLrab, + sRGB, +} from 'colorjs.io/fn'; +import { buildAccentRamp, buildBgRamp, checkAccessibleCombinations } from '..'; import { buildRamp } from '../lib'; import { clampToGamut, @@ -81,25 +92,6 @@ function getPerceptualContrastMagnitude( return Math.abs( contrastAPCA( background, foreground ) ); } -function expectAccessibleFillStates( ramp: ReturnType< typeof buildRamp > ) { - const restingLuminance = getLuminance( ramp.ramp.bgFill1 ); - const activeLuminance = getLuminance( ramp.ramp.bgFill2 ); - if ( ramp.direction === 'darker' ) { - expect( activeLuminance ).toBeLessThan( restingLuminance ); - } else { - expect( activeLuminance ).toBeGreaterThan( restingLuminance ); - } - expect( - getContrast( ramp.ramp.bgFill1, ramp.ramp.bgFill2 ) - ).toBeGreaterThanOrEqual( 1.2 ); - expect( - getContrast( ramp.ramp.bgFill1, ramp.ramp.fgFill ) - ).toBeGreaterThanOrEqual( 4.5 ); - expect( - getContrast( ramp.ramp.bgFill2, ramp.ramp.fgFill ) - ).toBeGreaterThanOrEqual( 4.5 ); -} - function getForegroundConstraintReferences( stepIndex: number, ramp: ReturnType< typeof buildBgRamp >, @@ -265,6 +257,372 @@ describe( 'buildRamps', () => { expect( result.warnings ).toBeUndefined(); } ); + it( 'keeps the serialized Blue stroke contrast at 3:1', () => { + const result = buildBgRamp( '#3876a8' ); + + expect( + getContrast( result.ramp.surface3, result.ramp.stroke3 ) + ).toBeGreaterThanOrEqual( 3 ); + } ); + + it( 'keeps a feasible interaction-state interval after color serialization', () => { + expect( + BG_RAMP_CONFIG.foregroundScale.perceptualTargets.normalToActive + ).toBe( MINIMUM_INTERACTION_STATE_APCA_INTERVAL ); + + const backgroundRamp = buildBgRamp( '#cc4541' ); + const primaryRamp = buildAccentRamp( '#1a1a1a', backgroundRamp ); + const normalContrast = getPerceptualContrastMagnitude( + backgroundRamp.ramp.surface2, + primaryRamp.ramp.fgSurface4 + ); + const activeContrast = getPerceptualContrastMagnitude( + backgroundRamp.ramp.surface2, + primaryRamp.ramp.fgSurface5 + ); + + expect( activeContrast - normalContrast ).toBeGreaterThanOrEqual( + MINIMUM_INTERACTION_STATE_APCA_INTERVAL + ); + expect( primaryRamp.ramp.fgSurface5 ).toBe( '#fefefe' ); + expect( + getPerceptualContrastMagnitude( + backgroundRamp.ramp.surface2, + '#fdfdfd' + ) - normalContrast + ).toBeLessThan( MINIMUM_INTERACTION_STATE_APCA_INTERVAL ); + } ); + + it( 'preserves the largest legal interaction-state interval when both targets cannot fit', () => { + const backgroundRamp = buildRamp( '#777777', BG_RAMP_CONFIG, { + mainDirection: 'darker', + } ); + const normal = backgroundRamp.ramp.fgSurface4; + const active = backgroundRamp.ramp.fgSurface5; + + expect( normal ).not.toBe( active ); + expect( + getPerceptualContrastMagnitude( + backgroundRamp.ramp.surface2, + active + ) - + getPerceptualContrastMagnitude( + backgroundRamp.ramp.surface2, + normal + ) + ).toBeGreaterThan( 0 ); + + for ( const reference of getForegroundConstraintReferences( + 3, + backgroundRamp, + backgroundRamp + ) ) { + expect( getContrast( reference, normal ) ).toBeGreaterThanOrEqual( + 4.5 + ); + } + } ); + + it( 'uses the alternate middle-gray polarity when it improves interaction spacing within the seed drift bound', () => { + const seed = '#777777'; + const preferredRamp = buildRamp( seed, BG_RAMP_CONFIG, { + mainDirection: 'darker', + } ); + const selectedRamp = buildBgRamp( seed ); + + expect( selectedRamp.direction ).toBe( 'lighter' ); + expect( selectedRamp.warnings ).toBeUndefined(); + expect( + checkAccessibleCombinations( { bgRamp: selectedRamp } ) + ).toEqual( [] ); + expect( + deltaEOK2( seed, selectedRamp.ramp.surface2 ) + ).toBeLessThanOrEqual( + deltaEOK2( seed, preferredRamp.ramp.surface2 ) + + MAXIMUM_ALTERNATE_POLARITY_DELTA_E_DRIFT + ); + expect( + getPerceptualContrastMagnitude( + selectedRamp.ramp.surface2, + selectedRamp.ramp.fgSurface5 + ) - + getPerceptualContrastMagnitude( + selectedRamp.ramp.surface2, + selectedRamp.ramp.fgSurface4 + ) + ).toBeGreaterThanOrEqual( MINIMUM_INTERACTION_STATE_APCA_INTERVAL ); + } ); + + it( 'keeps the preferred polarity when the alternate would distort the seed', () => { + const seed = '#a0a0a0'; + const preferredRamp = buildRamp( seed, BG_RAMP_CONFIG, { + mainDirection: 'darker', + } ); + const alternateRamp = buildRamp( seed, BG_RAMP_CONFIG, { + mainDirection: 'lighter', + } ); + const selectedRamp = buildBgRamp( seed ); + + expect( + deltaEOK2( seed, alternateRamp.ramp.surface2 ) - + deltaEOK2( seed, preferredRamp.ramp.surface2 ) + ).toBeGreaterThan( MAXIMUM_ALTERNATE_POLARITY_DELTA_E_DRIFT ); + expect( selectedRamp.direction ).toBe( 'darker' ); + expect( selectedRamp.ramp.surface2 ).toBe( + preferredRamp.ramp.surface2 + ); + } ); + + it( 'derives the strongest foreground without consuming the gamut endpoint', () => { + const backgroundRamp = buildBgRamp( DEFAULT_SEED_COLORS.background ); + const ramps = [ + backgroundRamp, + buildAccentRamp( DEFAULT_SEED_COLORS.primary, backgroundRamp ), + buildAccentRamp( DEFAULT_SEED_COLORS.error, backgroundRamp ), + ]; + const endpointContrast = getPerceptualContrastMagnitude( + backgroundRamp.ramp.surface2, + '#000' + ); + + for ( const ramp of ramps ) { + const foregroundContrast = getPerceptualContrastMagnitude( + backgroundRamp.ramp.surface2, + ramp.ramp.fgSurface5 + ); + + expect( ramp.ramp.fgSurface5 ).not.toBe( '#000' ); + expect( endpointContrast - foregroundContrast ).toBeGreaterThan( + 0 + ); + expect( endpointContrast - foregroundContrast ).toBeLessThan( 4.5 ); + } + } ); + + it( 'separates weak, normal, and active semantic foregrounds', () => { + const backgroundRamp = buildBgRamp( DEFAULT_SEED_COLORS.background ); + const ramps = [ + backgroundRamp, + buildAccentRamp( DEFAULT_SEED_COLORS.primary, backgroundRamp ), + buildAccentRamp( DEFAULT_SEED_COLORS.error, backgroundRamp ), + ]; + + for ( const ramp of ramps ) { + const contrasts = [ + getPerceptualContrastMagnitude( + backgroundRamp.ramp.surface2, + ramp.ramp.fgSurface3 + ), + getPerceptualContrastMagnitude( + backgroundRamp.ramp.surface2, + ramp.ramp.fgSurface4 + ), + getPerceptualContrastMagnitude( + backgroundRamp.ramp.surface2, + ramp.ramp.fgSurface5 + ), + ]; + const normalInterval = contrasts[ 1 ] - contrasts[ 0 ]; + const activeInterval = contrasts[ 2 ] - contrasts[ 1 ]; + + expect( normalInterval ).toBeGreaterThanOrEqual( 12 ); + expect( activeInterval ).toBeGreaterThanOrEqual( + MINIMUM_INTERACTION_STATE_APCA_INTERVAL + ); + } + + const normalContrasts = ramps.map( ( ramp ) => + getPerceptualContrastMagnitude( + backgroundRamp.ramp.surface2, + ramp.ramp.fgSurface4 + ) + ); + expect( + Math.max( ...normalContrasts ) - Math.min( ...normalContrasts ) + ).toBeLessThan( 1 ); + } ); + + it.each( perceptualSampleCombinations )( + 'orders retained foreground steps and keeps their WCAG floors for $background and $primary', + ( { background, primary } ) => { + const backgroundRamp = buildBgRamp( background ); + const ramps = [ + backgroundRamp, + buildAccentRamp( primary, backgroundRamp ), + buildAccentRamp( DEFAULT_SEED_COLORS.error, backgroundRamp ), + ]; + const contrastTargets = [ 3, 4.5, 4.5, 4.5 ]; + + for ( const ramp of ramps ) { + const perceptualContrasts = foregroundSteps.map( ( step ) => + getPerceptualContrastMagnitude( + backgroundRamp.ramp.surface2, + ramp.ramp[ step ] + ) + ); + + expect( perceptualContrasts ).toEqual( + [ ...perceptualContrasts ].sort( ( a, b ) => a - b ) + ); + + foregroundSteps.forEach( ( step, stepIndex ) => { + for ( const reference of getForegroundConstraintReferences( + stepIndex, + ramp, + backgroundRamp + ) ) { + expect( + getContrast( reference, ramp.ramp[ step ] ) + ).toBeGreaterThanOrEqual( + contrastTargets[ stepIndex ] + ); + } + } ); + } + } + ); + + it.each( perceptualSampleCombinations )( + 'keeps elevation and emphasis surfaces in semantic order for $background and $primary', + ( { background, primary } ) => { + const backgroundRamp = buildBgRamp( background ); + const ramps = [ + backgroundRamp, + buildAccentRamp( primary, backgroundRamp ), + buildAccentRamp( DEFAULT_SEED_COLORS.error, backgroundRamp ), + ]; + + for ( const ramp of ramps ) { + const lightness = ( step: keyof typeof ramp.ramp ) => + get( ramp.ramp[ step ], [ OKLrab, 'l' ] ); + const orderedSteps = + ramp.direction === 'lighter' + ? [ + 'surface1', + 'surface2', + 'surface3', + 'surface4', + 'surface5', + 'surface6', + ] + : [ + 'surface6', + 'surface5', + 'surface4', + 'surface1', + 'surface2', + 'surface3', + ]; + const lightnesses = orderedSteps.map( ( step ) => + lightness( step as keyof typeof ramp.ramp ) + ); + + for ( let index = 1; index < lightnesses.length; index++ ) { + expect( lightnesses[ index ] ).toBeGreaterThan( + lightnesses[ index - 1 ] + ); + } + + const lowerGap = + lightness( 'surface2' ) - lightness( 'surface1' ); + const upperGap = + lightness( 'surface3' ) - lightness( 'surface2' ); + const elevationGap = Math.max( lowerGap, upperGap ); + // Gaps stay balanced unless black or white clips one side. + expect( + Math.abs( + lowerGap - + Math.min( elevationGap, lightness( 'surface2' ) ) + ) + ).toBeLessThan( 0.004 ); + expect( + Math.abs( + upperGap - + Math.min( + elevationGap, + 1 - lightness( 'surface2' ) + ) + ) + ).toBeLessThan( 0.004 ); + expect( + Math.abs( + lightness( 'surface5' ) - + ( lightness( 'surface4' ) + + lightness( 'surface6' ) ) / + 2 + ) + ).toBeLessThan( 0.004 ); + } + } + ); + + it.each( perceptualSampleCombinations )( + 'keeps stroke strength ordered and ST3 accessible for $background and $primary', + ( { background, primary } ) => { + const backgroundRamp = buildBgRamp( background ); + const ramps = [ + backgroundRamp, + buildAccentRamp( primary, backgroundRamp ), + buildAccentRamp( DEFAULT_SEED_COLORS.error, backgroundRamp ), + ]; + + for ( const ramp of ramps ) { + const strokeContrasts = [ + 'stroke1', + 'stroke2', + 'stroke3', + 'stroke4', + ].map( ( step ) => + Math.abs( + contrastAPCA( + ramp.ramp.surface3, + ramp.ramp[ step as keyof typeof ramp.ramp ] + ) + ) + ); + + expect( strokeContrasts ).toEqual( + [ ...strokeContrasts ].sort( ( a, b ) => a - b ) + ); + expect( + getContrast( ramp.ramp.surface3, ramp.ramp.stroke3 ) + ).toBeGreaterThanOrEqual( 3 ); + } + } + ); + + it.each( [ + { background: '#4f386e', primary: '#608010' }, + { background: '#fcfcfc', primary: '#ffd700' }, + { background: '#1e1e1e', primary: '#00ffff' }, + ] )( + "preserves the accent seed's gamut-relative chroma for $background and $primary", + ( { background, primary } ) => { + const backgroundRamp = buildBgRamp( background ); + const primaryRamp = buildAccentRamp( primary, backgroundRamp ); + const seedRelativeChroma = get( primaryRamp.ramp.bgFill1, [ + OklchSrgb, + 'c', + ] ); + + for ( const step of [ + 'fgSurface3', + 'fgSurface4', + 'fgSurface5', + ] as const ) { + // FGS5 can approach a gamut boundary when both WCAG floors and + // the active-state APCA interval require the remaining headroom. + const maximumDifference = step === 'fgSurface5' ? 0.05 : 0.04; + expect( + Math.abs( + get( primaryRamp.ramp[ step ], [ OklchSrgb, 'c' ] ) - + seedRelativeChroma + ) + ).toBeLessThan( maximumDifference ); + } + } + ); + it.each( [ { background: DEFAULT_SEED_COLORS.background, @@ -313,7 +671,7 @@ describe( 'buildRamps', () => { } ); it( 'orders every contrast reference before its dependent step', () => { - const sortedSteps = sortByDependency( ACCENT_RAMP_CONFIG ); + const sortedSteps = sortByDependency( ACCENT_RAMP_CONFIG.steps ); expect( sortedSteps.indexOf( 'bgFill1' ) ).toBeLessThan( sortedSteps.indexOf( 'fgFill' ) @@ -338,20 +696,23 @@ describe( 'buildRamps', () => { it( 'checks every contrast reference before reusing a ramp color', () => { const config = { ...ACCENT_RAMP_CONFIG, - fgFill: { - ...ACCENT_RAMP_CONFIG.fgFill, - sameAsIfPossible: 'fgSurface4' as const, + steps: { + ...ACCENT_RAMP_CONFIG.steps, + fgFill: { + ...ACCENT_RAMP_CONFIG.steps.fgFill, + sameAsIfPossible: 'bgFillInverted1' as const, + }, }, }; const ramp = buildRamp( '#1fad1f', config ); expect( - getContrast( ramp.ramp.bgFill1, ramp.ramp.fgSurface4 ) + getContrast( ramp.ramp.bgFill1, ramp.ramp.bgFillInverted1 ) ).toBeGreaterThanOrEqual( 4.5 ); expect( - getContrast( ramp.ramp.bgFill2, ramp.ramp.fgSurface4 ) + getContrast( ramp.ramp.bgFill2, ramp.ramp.bgFillInverted1 ) ).toBeLessThan( 4.5 ); - expect( ramp.ramp.fgFill ).not.toBe( ramp.ramp.fgSurface4 ); + expect( ramp.ramp.fgFill ).not.toBe( ramp.ramp.bgFillInverted1 ); expect( getContrast( ramp.ramp.bgFill2, ramp.ramp.fgFill ) ).toBeGreaterThanOrEqual( 4.5 ); @@ -364,7 +725,7 @@ describe( 'buildRamps', () => { buildBgRamp( DEFAULT_SEED_COLORS.background ) ); - expect( stepsForStep( 'fgFill', ACCENT_RAMP_CONFIG ) ).toEqual( + expect( stepsForStep( 'fgFill', ACCENT_RAMP_CONFIG.steps ) ).toEqual( expect.arrayContaining( [ 'bgFill1', 'bgFill2' ] ) ); expect( ramp.ramp.bgFill1 ).not.toBe( seed ); diff --git a/packages/theme/tokens/color.json b/packages/theme/tokens/color.json index 6e679f35193371..19243a72b85a57 100644 --- a/packages/theme/tokens/color.json +++ b/packages/theme/tokens/color.json @@ -34,29 +34,29 @@ "surface2": { "$value": { "colorSpace": "srgb", - "components": [ 0.973, 0.973, 0.973 ], - "hex": "#f8f8f8" + "components": [ 0.965, 0.973, 0.996 ], + "hex": "#f6f8fe" } }, "surface6": { "$value": { "colorSpace": "srgb", - "components": [ 0.776, 0.824, 0.945 ], - "hex": "#c6d2f1" + "components": [ 0.78, 0.824, 0.933 ], + "hex": "#c7d2ee" } }, "surface5": { "$value": { "colorSpace": "srgb", - "components": [ 0.839, 0.875, 0.957 ], - "hex": "#d6dff4" + "components": [ 0.847, 0.875, 0.941 ], + "hex": "#d8dff0" } }, "surface4": { "$value": { "colorSpace": "srgb", - "components": [ 0.898, 0.918, 0.969 ], - "hex": "#e5eaf7" + "components": [ 0.902, 0.922, 0.973 ], + "hex": "#e6ebf8" } }, "surface3": { @@ -97,15 +97,15 @@ "stroke2": { "$value": { "colorSpace": "srgb", - "components": [ 0.604, 0.667, 0.82 ], - "hex": "#9aaad1" + "components": [ 0.608, 0.663, 0.788 ], + "hex": "#9ba9c9" } }, "stroke1": { "$value": { "colorSpace": "srgb", - "components": [ 0.686, 0.733, 0.855 ], - "hex": "#afbbda" + "components": [ 0.682, 0.733, 0.855 ], + "hex": "#aebbda" } }, "bgFillInverted2": { @@ -132,8 +132,8 @@ "surface1": { "$value": { "colorSpace": "srgb", - "components": [ 0.925, 0.945, 0.988 ], - "hex": "#ecf1fc" + "components": [ 0.929, 0.945, 0.984 ], + "hex": "#edf1fb" } }, "fgSurface4": { @@ -176,29 +176,29 @@ "surface2": { "$value": { "colorSpace": "srgb", - "components": [ 0.973, 0.973, 0.973 ], - "hex": "#f8f8f8" + "components": [ 0.953, 0.976, 1 ], + "hex": "#f3f9ff" } }, "surface6": { "$value": { "colorSpace": "srgb", - "components": [ 0.729, 0.839, 0.965 ], - "hex": "#bad6f6" + "components": [ 0.737, 0.835, 0.949 ], + "hex": "#bcd5f2" } }, "surface5": { "$value": { "colorSpace": "srgb", - "components": [ 0.827, 0.894, 0.969 ], - "hex": "#d3e4f7" + "components": [ 0.816, 0.882, 0.957 ], + "hex": "#d0e1f4" } }, "surface4": { "$value": { "colorSpace": "srgb", - "components": [ 0.886, 0.929, 0.98 ], - "hex": "#e2edfa" + "components": [ 0.878, 0.929, 0.988 ], + "hex": "#e0edfc" } }, "surface3": { @@ -239,15 +239,15 @@ "stroke2": { "$value": { "colorSpace": "srgb", - "components": [ 0.588, 0.702, 0.827 ], - "hex": "#96b3d3" + "components": [ 0.6, 0.702, 0.816 ], + "hex": "#99b3d0" } }, "stroke1": { "$value": { "colorSpace": "srgb", - "components": [ 0.667, 0.776, 0.898 ], - "hex": "#aac6e5" + "components": [ 0.678, 0.776, 0.886 ], + "hex": "#adc6e2" } }, "bgFillInverted2": { @@ -267,15 +267,15 @@ "fgFillInverted": { "$value": { "colorSpace": "srgb", - "components": [ 0.933, 0.941, 0.953 ], - "hex": "#eef0f3" + "components": [ 0.933, 0.941, 0.949 ], + "hex": "#eef0f2" } }, "surface1": { "$value": { "colorSpace": "srgb", - "components": [ 0.906, 0.945, 0.992 ], - "hex": "#e7f1fd" + "components": [ 0.91, 0.949, 0.996 ], + "hex": "#e8f2fe" } }, "fgSurface4": { @@ -318,29 +318,29 @@ "surface2": { "$value": { "colorSpace": "srgb", - "components": [ 0.973, 0.973, 0.973 ], - "hex": "#f8f8f8" + "components": [ 0.922, 1, 0.929 ], + "hex": "#ebffed" } }, "surface6": { "$value": { "colorSpace": "srgb", - "components": [ 0.537, 0.906, 0.608 ], - "hex": "#89e79b" + "components": [ 0.537, 0.91, 0.612 ], + "hex": "#89e89c" } }, "surface5": { "$value": { "colorSpace": "srgb", - "components": [ 0.655, 0.961, 0.71 ], - "hex": "#a7f5b5" + "components": [ 0.647, 0.949, 0.702 ], + "hex": "#a5f2b3" } }, "surface4": { "$value": { "colorSpace": "srgb", - "components": [ 0.761, 0.973, 0.792 ], - "hex": "#c2f8ca" + "components": [ 0.773, 0.984, 0.804 ], + "hex": "#c5fbcd" } }, "surface3": { @@ -353,43 +353,43 @@ "fgSurface3": { "$value": { "colorSpace": "srgb", - "components": [ 0, 0.502, 0.188 ], - "hex": "#008030" + "components": [ 0.188, 0.494, 0.267 ], + "hex": "#307e44" } }, "fgSurface2": { "$value": { "colorSpace": "srgb", - "components": [ 0.165, 0.62, 0.302 ], - "hex": "#2a9e4d" + "components": [ 0.169, 0.624, 0.306 ], + "hex": "#2b9f4e" } }, "stroke3": { "$value": { "colorSpace": "srgb", - "components": [ 0, 0.498, 0.184 ], - "hex": "#007f2f" + "components": [ 0, 0.502, 0.188 ], + "hex": "#008030" } }, "stroke4": { "$value": { "colorSpace": "srgb", - "components": [ 0, 0.376, 0.075 ], - "hex": "#006013" + "components": [ 0, 0.38, 0.075 ], + "hex": "#006113" } }, "stroke2": { "$value": { "colorSpace": "srgb", - "components": [ 0.51, 0.753, 0.553 ], - "hex": "#82c08d" + "components": [ 0.506, 0.757, 0.553 ], + "hex": "#81c18d" } }, "stroke1": { "$value": { "colorSpace": "srgb", - "components": [ 0.573, 0.82, 0.616 ], - "hex": "#92d19d" + "components": [ 0.573, 0.827, 0.616 ], + "hex": "#92d39d" } }, "bgFillInverted2": { @@ -409,15 +409,15 @@ "fgFillInverted": { "$value": { "colorSpace": "srgb", - "components": [ 0.922, 0.949, 0.925 ], - "hex": "#ebf2ec" + "components": [ 0.925, 0.949, 0.925 ], + "hex": "#ecf2ec" } }, "surface1": { "$value": { "colorSpace": "srgb", - "components": [ 0.831, 0.984, 0.851 ], - "hex": "#d4fbd9" + "components": [ 0.839, 0.992, 0.859 ], + "hex": "#d6fddb" } }, "fgSurface4": { @@ -460,29 +460,29 @@ "surface2": { "$value": { "colorSpace": "srgb", - "components": [ 0.973, 0.973, 0.973 ], - "hex": "#f8f8f8" + "components": [ 1, 0.969, 0.882 ], + "hex": "#fff7e1" } }, "surface6": { "$value": { "colorSpace": "srgb", - "components": [ 0.961, 0.804, 0.525 ], - "hex": "#f5cd86" + "components": [ 0.953, 0.8, 0.525 ], + "hex": "#f3cc86" } }, "surface5": { "$value": { "colorSpace": "srgb", - "components": [ 0.973, 0.859, 0.659 ], - "hex": "#f8dba8" + "components": [ 0.965, 0.855, 0.667 ], + "hex": "#f6daaa" } }, "surface4": { "$value": { "colorSpace": "srgb", - "components": [ 0.988, 0.918, 0.788 ], - "hex": "#fceac9" + "components": [ 1, 0.91, 0.753 ], + "hex": "#ffe8c0" } }, "surface3": { @@ -495,50 +495,50 @@ "fgSurface3": { "$value": { "colorSpace": "srgb", - "components": [ 0.576, 0.388, 0 ], - "hex": "#936300" + "components": [ 0.533, 0.404, 0.149 ], + "hex": "#886726" } }, "fgSurface2": { "$value": { "colorSpace": "srgb", - "components": [ 0.714, 0.502, 0 ], - "hex": "#b68000" + "components": [ 0.71, 0.498, 0 ], + "hex": "#b57f00" } }, "stroke3": { "$value": { "colorSpace": "srgb", - "components": [ 0.576, 0.392, 0 ], - "hex": "#936400" + "components": [ 0.576, 0.388, 0 ], + "hex": "#936300" } }, "stroke4": { "$value": { "colorSpace": "srgb", - "components": [ 0.439, 0.29, 0 ], - "hex": "#704a00" + "components": [ 0.439, 0.286, 0 ], + "hex": "#704900" } }, "stroke2": { "$value": { "colorSpace": "srgb", - "components": [ 0.784, 0.678, 0.498 ], - "hex": "#c8ad7f" + "components": [ 0.788, 0.655, 0.424 ], + "hex": "#c9a76c" } }, "stroke1": { "$value": { "colorSpace": "srgb", - "components": [ 0.882, 0.745, 0.502 ], - "hex": "#e1be80" + "components": [ 0.882, 0.741, 0.486 ], + "hex": "#e1bd7c" } }, "bgFillInverted2": { "$value": { "colorSpace": "srgb", - "components": [ 0.125, 0.118, 0.106 ], - "hex": "#201e1b" + "components": [ 0.122, 0.118, 0.106 ], + "hex": "#1f1e1b" } }, "bgFillInverted1": { @@ -551,22 +551,22 @@ "fgFillInverted": { "$value": { "colorSpace": "srgb", - "components": [ 0.965, 0.937, 0.898 ], - "hex": "#f6efe5" + "components": [ 0.961, 0.937, 0.898 ], + "hex": "#f5efe5" } }, "surface1": { "$value": { "colorSpace": "srgb", - "components": [ 1, 0.925, 0.796 ], - "hex": "#ffeccb" + "components": [ 1, 0.929, 0.8 ], + "hex": "#ffedcc" } }, "fgSurface4": { "$value": { "colorSpace": "srgb", - "components": [ 0.396, 0.298, 0.102 ], - "hex": "#654c1a" + "components": [ 0.396, 0.298, 0.098 ], + "hex": "#654c19" } }, "fgSurface5": { @@ -602,29 +602,29 @@ "surface2": { "$value": { "colorSpace": "srgb", - "components": [ 0.973, 0.973, 0.973 ], - "hex": "#f8f8f8" + "components": [ 1, 0.965, 0.961 ], + "hex": "#fff6f5" } }, "surface6": { "$value": { "colorSpace": "srgb", - "components": [ 0.969, 0.784, 0.757 ], - "hex": "#f7c8c1" + "components": [ 0.949, 0.788, 0.765 ], + "hex": "#f2c9c3" } }, "surface5": { "$value": { "colorSpace": "srgb", - "components": [ 0.973, 0.847, 0.827 ], - "hex": "#f8d8d3" + "components": [ 0.961, 0.851, 0.835 ], + "hex": "#f5d9d5" } }, "surface4": { "$value": { "colorSpace": "srgb", - "components": [ 0.98, 0.898, 0.882 ], - "hex": "#fae5e1" + "components": [ 0.988, 0.906, 0.89 ], + "hex": "#fce7e3" } }, "surface3": { @@ -637,8 +637,8 @@ "fgSurface3": { "$value": { "colorSpace": "srgb", - "components": [ 0.843, 0.102, 0.102 ], - "hex": "#d71a1a" + "components": [ 0.847, 0.102, 0.102 ], + "hex": "#d81a1a" } }, "fgSurface2": { @@ -665,22 +665,22 @@ "stroke2": { "$value": { "colorSpace": "srgb", - "components": [ 0.851, 0.604, 0.569 ], - "hex": "#d99a91" + "components": [ 0.808, 0.608, 0.576 ], + "hex": "#ce9b93" } }, "stroke1": { "$value": { "colorSpace": "srgb", - "components": [ 0.875, 0.686, 0.659 ], - "hex": "#dfafa8" + "components": [ 0.878, 0.686, 0.655 ], + "hex": "#e0afa7" } }, "bgFillInverted2": { "$value": { "colorSpace": "srgb", - "components": [ 0.141, 0.11, 0.106 ], - "hex": "#241c1b" + "components": [ 0.137, 0.11, 0.106 ], + "hex": "#231c1b" } }, "bgFillInverted1": { @@ -693,15 +693,15 @@ "fgFillInverted": { "$value": { "colorSpace": "srgb", - "components": [ 0.953, 0.937, 0.937 ], - "hex": "#f3efef" + "components": [ 0.949, 0.937, 0.937 ], + "hex": "#f2efef" } }, "surface1": { "$value": { "colorSpace": "srgb", - "components": [ 0.992, 0.925, 0.914 ], - "hex": "#fdece9" + "components": [ 0.992, 0.929, 0.918 ], + "hex": "#fdedea" } }, "fgSurface4": { @@ -886,29 +886,29 @@ "surface2": { "$value": { "colorSpace": "srgb", - "components": [ 0.973, 0.973, 0.973 ], - "hex": "#f8f8f8" + "components": [ 1, 0.976, 0.792 ], + "hex": "#fff9ca" } }, "surface6": { "$value": { "colorSpace": "srgb", - "components": [ 0.918, 0.824, 0.439 ], - "hex": "#ead270" + "components": [ 0.91, 0.82, 0.439 ], + "hex": "#e8d170" } }, "surface5": { "$value": { "colorSpace": "srgb", - "components": [ 0.961, 0.875, 0.529 ], - "hex": "#f5df87" + "components": [ 0.953, 0.871, 0.525 ], + "hex": "#f3de86" } }, "surface4": { "$value": { "colorSpace": "srgb", - "components": [ 1, 0.925, 0.569 ], - "hex": "#ffec91" + "components": [ 1, 0.918, 0.588 ], + "hex": "#ffea96" } }, "surface3": { @@ -921,8 +921,8 @@ "fgSurface3": { "$value": { "colorSpace": "srgb", - "components": [ 0.514, 0.416, 0 ], - "hex": "#836a00" + "components": [ 0.486, 0.424, 0.129 ], + "hex": "#7c6c21" } }, "fgSurface2": { @@ -935,29 +935,29 @@ "stroke3": { "$value": { "colorSpace": "srgb", - "components": [ 0.514, 0.42, 0 ], - "hex": "#836b00" + "components": [ 0.514, 0.416, 0 ], + "hex": "#836a00" } }, "stroke4": { "$value": { "colorSpace": "srgb", - "components": [ 0.388, 0.314, 0 ], - "hex": "#635000" + "components": [ 0.388, 0.31, 0 ], + "hex": "#634f00" } }, "stroke2": { "$value": { "colorSpace": "srgb", - "components": [ 0.749, 0.694, 0.482 ], - "hex": "#bfb17b" + "components": [ 0.745, 0.694, 0.486 ], + "hex": "#beb17c" } }, "stroke1": { "$value": { "colorSpace": "srgb", - "components": [ 0.816, 0.765, 0.545 ], - "hex": "#d0c38b" + "components": [ 0.816, 0.761, 0.545 ], + "hex": "#d0c28b" } }, "bgFillInverted2": { @@ -977,22 +977,22 @@ "fgFillInverted": { "$value": { "colorSpace": "srgb", - "components": [ 0.996, 0.945, 0.733 ], - "hex": "#fef1bb" + "components": [ 0.98, 0.945, 0.788 ], + "hex": "#faf1c9" } }, "surface1": { "$value": { "colorSpace": "srgb", - "components": [ 1, 0.949, 0.592 ], - "hex": "#fff297" + "components": [ 1, 0.937, 0.663 ], + "hex": "#ffefa9" } }, "fgSurface4": { "$value": { "colorSpace": "srgb", - "components": [ 0.365, 0.314, 0.086 ], - "hex": "#5d5016" + "components": [ 0.361, 0.31, 0.086 ], + "hex": "#5c4f16" } }, "fgSurface5": { From 32450cfca8cd335cb71a0e7644007109c8dd1017 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Thu, 10 Sep 2026 13:41:18 +0200 Subject: [PATCH 26/44] Theme: Move changelog entries to Unreleased --- packages/theme/CHANGELOG.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/theme/CHANGELOG.md b/packages/theme/CHANGELOG.md index 5552dd9a527a61..222ab75f046c75 100644 --- a/packages/theme/CHANGELOG.md +++ b/packages/theme/CHANGELOG.md @@ -28,9 +28,14 @@ - Preserve Lightning CSS `from global` references and keep nested fallback variables global when injecting design token fallbacks ([#83359](https://github.com/WordPress/gutenberg/pull/83359)). - Mark the `react` and `react-dom` peer dependencies as optional, since the design tokens and build tool plugins do not need them ([#83765](https://github.com/WordPress/gutenberg/pull/83765)). +### Enhancements + +- Generate perception-aware color ramps with ordered surface and stroke progressions, preserve available surface spacing near black and white, align normal content with resting interactive colors, reserve the fifth foreground step for interaction states, and keep WCAG contrast floors unchanged. ([#82294](https://github.com/WordPress/gutenberg/pull/82294)) + ### Internal - Generate and commit complete border radius and width tokens for Figma mode imports during the theme build. ([#84035](https://github.com/WordPress/gutenberg/pull/84035)) +- Register color spaces only when calculating colors, avoiding module-import side effects. ([#82294](https://github.com/WordPress/gutenberg/pull/82294)) - Update the Vite development dependency to 8.3.2 ([#83992](https://github.com/WordPress/gutenberg/pull/83992)). - Update the Vite development dependency to 8.3.1. ([#83569](https://github.com/WordPress/gutenberg/pull/83569)) - Restore color-scale Storybook accessibility checks with labeled color samples, readable seed labels, and keyboard scrolling ([#83358](https://github.com/WordPress/gutenberg/pull/83358)). @@ -58,7 +63,6 @@ ### Enhancements -- Generate perception-aware color ramps with ordered surface and stroke progressions, preserve available surface spacing near black and white, align normal content with resting interactive colors, reserve the fifth foreground step for interaction states, and keep WCAG contrast floors unchanged. ([#82294](https://github.com/WordPress/gutenberg/pull/82294)) - Add a Lightning CSS visitor plugin that injects design system token fallbacks, available as `@wordpress/theme/lightningcss-plugins/lightningcss-ds-token-fallbacks`, with an optional peer range of `>=1.33.0 <2.0.0`. ([#80401](https://github.com/WordPress/gutenberg/pull/80401)) ### Bug Fixes @@ -75,7 +79,6 @@ - Reuse seed-dependent calculations during color-ramp constraint searches. ([#82545](https://github.com/WordPress/gutenberg/pull/82545)) - Generate the default ramps before derived token artifacts so one build uses the current ramp algorithm throughout. ([#82525](https://github.com/WordPress/gutenberg/pull/82525)) - Cache relative luminance calculations used by color-ramp contrast checks. ([#82445](https://github.com/WordPress/gutenberg/pull/82445)) -- Register color spaces only when calculating colors, avoiding module-import side effects. ([#82294](https://github.com/WordPress/gutenberg/pull/82294)) - Enforce NodeNext module resolution in the build project so future declaration imports are checked against the package's published ESM resolution rules. ([#82088](https://github.com/WordPress/gutenberg/pull/82088)) - Update the `@types/node` development dependency to v24, matching the Node.js version the repository builds and tests against ([#82616](https://github.com/WordPress/gutenberg/pull/82616)). - Declare the `@storybook/addon-docs` development dependency used by the package's story documentation ([#82676](https://github.com/WordPress/gutenberg/pull/82676)). From c06b2e93f14078d73fc08a654d79fe43d856bbc4 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Fri, 11 Sep 2026 16:15:09 +0200 Subject: [PATCH 27/44] Theme: Restore active foreground token mappings --- .../internal/_wpds-token-fallbacks.scss | 12 ++++++------ packages/theme/prebuilt/css/design-tokens.css | 12 ++++++------ .../theme/prebuilt/js/design-token-fallbacks.mjs | 12 ++++++------ packages/theme/src/prebuilt/ts/color-tokens.ts | 14 +++++++------- .../theme/src/test/theme-provider.browser.test.tsx | 3 ++- packages/theme/tokens/color.json | 12 ++++++------ 6 files changed, 33 insertions(+), 32 deletions(-) diff --git a/packages/base-styles/internal/_wpds-token-fallbacks.scss b/packages/base-styles/internal/_wpds-token-fallbacks.scss index 7022e3ac5c4fa9..b6c51f4e35a0bd 100644 --- a/packages/base-styles/internal/_wpds-token-fallbacks.scss +++ b/packages/base-styles/internal/_wpds-token-fallbacks.scss @@ -81,27 +81,27 @@ $fallbacks: ( "--wpds-color-foreground-content-warning": "#654c19", "--wpds-color-foreground-content-warning-weak": "#886726", "--wpds-color-foreground-interactive-brand": - "color-mix(in oklch, var(--wp-admin-theme-color, #3858e9) 96%, white)", - "--wpds-color-foreground-interactive-brand-active": "color-mix(in oklch, var(--wp-admin-theme-color, #3858e9) 88%, black)", + "--wpds-color-foreground-interactive-brand-active": + "color-mix(in oklch, var(--wp-admin-theme-color, #3858e9) 35%, black)", "--wpds-color-foreground-interactive-brand-disabled": "#8d8d8d", "--wpds-color-foreground-interactive-brand-strong": "#fff", "--wpds-color-foreground-interactive-brand-strong-active": "#fff", "--wpds-color-foreground-interactive-brand-strong-disabled": "#8d8d8d", - "--wpds-color-foreground-interactive-error": "#d81a1a", - "--wpds-color-foreground-interactive-error-active": "#980f0f", + "--wpds-color-foreground-interactive-error": "#980f0f", + "--wpds-color-foreground-interactive-error-active": "#260101", "--wpds-color-foreground-interactive-error-disabled": "#8d8d8d", "--wpds-color-foreground-interactive-error-strong": "#f2efef", "--wpds-color-foreground-interactive-error-strong-active": "#f2efef", "--wpds-color-foreground-interactive-error-strong-disabled": "#8d8d8d", "--wpds-color-foreground-interactive-neutral": "#505050", - "--wpds-color-foreground-interactive-neutral-active": "#505050", + "--wpds-color-foreground-interactive-neutral-active": "#141414", "--wpds-color-foreground-interactive-neutral-disabled": "#8d8d8d", "--wpds-color-foreground-interactive-neutral-strong": "#f0f0f0", "--wpds-color-foreground-interactive-neutral-strong-active": "#f0f0f0", "--wpds-color-foreground-interactive-neutral-strong-disabled": "#8d8d8d", "--wpds-color-foreground-interactive-neutral-weak": "#717171", - "--wpds-color-foreground-interactive-neutral-weak-active": "#505050", + "--wpds-color-foreground-interactive-neutral-weak-active": "#141414", "--wpds-color-foreground-interactive-neutral-weak-disabled": "#8d8d8d", "--wpds-color-stroke-focus": "var(--wp-admin-theme-color, #3858e9)", "--wpds-color-stroke-interactive-brand": diff --git a/packages/theme/prebuilt/css/design-tokens.css b/packages/theme/prebuilt/css/design-tokens.css index c77760ad5a3de6..f67298e23e3fb7 100644 --- a/packages/theme/prebuilt/css/design-tokens.css +++ b/packages/theme/prebuilt/css/design-tokens.css @@ -142,9 +142,9 @@ /* Foreground color for content like text with warning tone and weak emphasis. */ --wpds-color-foreground-content-warning-weak: #886726; /* Foreground color for interactive elements with brand tone and normal emphasis. */ - --wpds-color-foreground-interactive-brand: #3f61ea; + --wpds-color-foreground-interactive-brand: #222eda; /* Foreground color for interactive elements with brand tone and normal emphasis that are hovered, focused, or active. */ - --wpds-color-foreground-interactive-brand-active: #222eda; + --wpds-color-foreground-interactive-brand-active: #03063b; /* Foreground color for interactive elements with brand tone and normal emphasis, in their disabled state. */ --wpds-color-foreground-interactive-brand-disabled: #8d8d8d; /* Foreground color for interactive elements with brand tone and strong emphasis. */ @@ -154,9 +154,9 @@ /* Foreground color for interactive elements with brand tone and strong emphasis, in their disabled state. */ --wpds-color-foreground-interactive-brand-strong-disabled: #8d8d8d; /* Foreground color for interactive elements with error tone and normal emphasis. */ - --wpds-color-foreground-interactive-error: #d81a1a; + --wpds-color-foreground-interactive-error: #980f0f; /* Foreground color for interactive elements with error tone and normal emphasis that are hovered, focused, or active. */ - --wpds-color-foreground-interactive-error-active: #980f0f; + --wpds-color-foreground-interactive-error-active: #260101; /* Foreground color for interactive elements with error tone and normal emphasis, in their disabled state. */ --wpds-color-foreground-interactive-error-disabled: #8d8d8d; /* Foreground color for interactive elements with error tone and strong emphasis. */ @@ -168,7 +168,7 @@ /* Foreground color for interactive elements with neutral tone and normal emphasis. */ --wpds-color-foreground-interactive-neutral: #505050; /* Foreground color for interactive elements with neutral tone and normal emphasis that are hovered, focused, or active. */ - --wpds-color-foreground-interactive-neutral-active: #505050; + --wpds-color-foreground-interactive-neutral-active: #141414; /* Foreground color for interactive elements with neutral tone and normal emphasis, in their disabled state. */ --wpds-color-foreground-interactive-neutral-disabled: #8d8d8d; /* Foreground color for interactive elements with neutral tone and strong emphasis. */ @@ -180,7 +180,7 @@ /* Foreground color for interactive elements with neutral tone and weak emphasis. */ --wpds-color-foreground-interactive-neutral-weak: #717171; /* Foreground color for interactive elements with neutral tone and weak emphasis that are hovered, focused, or active. */ - --wpds-color-foreground-interactive-neutral-weak-active: #505050; + --wpds-color-foreground-interactive-neutral-weak-active: #141414; /* Foreground color for interactive elements with neutral tone and weak emphasis, in their disabled state. */ --wpds-color-foreground-interactive-neutral-weak-disabled: #8d8d8d; /* Accessible stroke color applied to focus rings. */ diff --git a/packages/theme/prebuilt/js/design-token-fallbacks.mjs b/packages/theme/prebuilt/js/design-token-fallbacks.mjs index 69b39f7678db82..2732d5d7667044 100644 --- a/packages/theme/prebuilt/js/design-token-fallbacks.mjs +++ b/packages/theme/prebuilt/js/design-token-fallbacks.mjs @@ -79,27 +79,27 @@ export default { '--wpds-color-foreground-content-warning': '#654c19', '--wpds-color-foreground-content-warning-weak': '#886726', '--wpds-color-foreground-interactive-brand': - 'color-mix(in oklch, var(--wp-admin-theme-color, #3858e9) 96%, white)', - '--wpds-color-foreground-interactive-brand-active': 'color-mix(in oklch, var(--wp-admin-theme-color, #3858e9) 88%, black)', + '--wpds-color-foreground-interactive-brand-active': + 'color-mix(in oklch, var(--wp-admin-theme-color, #3858e9) 35%, black)', '--wpds-color-foreground-interactive-brand-disabled': '#8d8d8d', '--wpds-color-foreground-interactive-brand-strong': '#fff', '--wpds-color-foreground-interactive-brand-strong-active': '#fff', '--wpds-color-foreground-interactive-brand-strong-disabled': '#8d8d8d', - '--wpds-color-foreground-interactive-error': '#d81a1a', - '--wpds-color-foreground-interactive-error-active': '#980f0f', + '--wpds-color-foreground-interactive-error': '#980f0f', + '--wpds-color-foreground-interactive-error-active': '#260101', '--wpds-color-foreground-interactive-error-disabled': '#8d8d8d', '--wpds-color-foreground-interactive-error-strong': '#f2efef', '--wpds-color-foreground-interactive-error-strong-active': '#f2efef', '--wpds-color-foreground-interactive-error-strong-disabled': '#8d8d8d', '--wpds-color-foreground-interactive-neutral': '#505050', - '--wpds-color-foreground-interactive-neutral-active': '#505050', + '--wpds-color-foreground-interactive-neutral-active': '#141414', '--wpds-color-foreground-interactive-neutral-disabled': '#8d8d8d', '--wpds-color-foreground-interactive-neutral-strong': '#f0f0f0', '--wpds-color-foreground-interactive-neutral-strong-active': '#f0f0f0', '--wpds-color-foreground-interactive-neutral-strong-disabled': '#8d8d8d', '--wpds-color-foreground-interactive-neutral-weak': '#717171', - '--wpds-color-foreground-interactive-neutral-weak-active': '#505050', + '--wpds-color-foreground-interactive-neutral-weak-active': '#141414', '--wpds-color-foreground-interactive-neutral-weak-disabled': '#8d8d8d', '--wpds-color-stroke-focus': 'var(--wp-admin-theme-color, #3858e9)', '--wpds-color-stroke-interactive-brand': diff --git a/packages/theme/src/prebuilt/ts/color-tokens.ts b/packages/theme/src/prebuilt/ts/color-tokens.ts index 6c009421705f04..8d2cc9e91163b8 100644 --- a/packages/theme/src/prebuilt/ts/color-tokens.ts +++ b/packages/theme/src/prebuilt/ts/color-tokens.ts @@ -21,7 +21,6 @@ export default { 'foreground-interactive-brand-strong-active', ], 'primary-surface4': [ 'background-interactive-brand-weak-active' ], - 'primary-fgSurface3': [ 'foreground-interactive-brand' ], 'primary-stroke3': [ 'background-thumb-brand', 'background-thumb-brand-active', @@ -32,7 +31,8 @@ export default { 'primary-stroke4': [ 'stroke-interactive-brand-active' ], 'primary-stroke1': [ 'stroke-surface-brand' ], 'primary-surface1': [ 'background-surface-brand' ], - 'primary-fgSurface4': [ 'foreground-interactive-brand-active' ], + 'primary-fgSurface4': [ 'foreground-interactive-brand' ], + 'primary-fgSurface5': [ 'foreground-interactive-brand-active' ], 'info-surface2': [ 'background-surface-info-weak' ], 'info-surface4': [ 'background-surface-info' ], 'info-fgSurface3': [ 'foreground-content-info-weak' ], @@ -65,10 +65,7 @@ export default { 'background-interactive-error-weak-active', 'background-surface-error', ], - 'error-fgSurface3': [ - 'foreground-content-error-weak', - 'foreground-interactive-error', - ], + 'error-fgSurface3': [ 'foreground-content-error-weak' ], 'error-stroke3': [ 'stroke-interactive-error', 'stroke-interactive-error-strong', @@ -78,8 +75,9 @@ export default { 'error-stroke1': [ 'stroke-surface-error' ], 'error-fgSurface4': [ 'foreground-content-error', - 'foreground-interactive-error-active', + 'foreground-interactive-error', ], + 'error-fgSurface5': [ 'foreground-interactive-error-active' ], 'bg-surface2': [ 'background-surface-neutral' ], 'bg-surface5': [ 'background-interactive-brand-strong-disabled', @@ -140,6 +138,8 @@ export default { 'background-thumb-neutral', 'foreground-content-neutral', 'foreground-interactive-neutral', + ], + 'bg-fgSurface5': [ 'foreground-interactive-neutral-active', 'foreground-interactive-neutral-weak-active', ], diff --git a/packages/theme/src/test/theme-provider.browser.test.tsx b/packages/theme/src/test/theme-provider.browser.test.tsx index 64525185233e6e..8c553916c0d50d 100644 --- a/packages/theme/src/test/theme-provider.browser.test.tsx +++ b/packages/theme/src/test/theme-provider.browser.test.tsx @@ -96,7 +96,7 @@ describe( 'ThemeProvider', () => { expect( readProp( provider, SURFACE_BG ) ).toBe( BACKGROUND ); } ); - it( 'maps normal content and resting neutral foregrounds to the same scale step', async () => { + it( 'maps active neutral foregrounds to a stronger scale step', async () => { await render(
x
@@ -116,6 +116,7 @@ describe( 'ThemeProvider', () => { expect( activeForeground ).toBe( readProp( provider, FOREGROUND_NEUTRAL_WEAK_ACTIVE ) ); + expect( activeForeground ).not.toBe( restingForeground ); } ); it( 'does not define color tokens if neither customized nor inherited', async () => { diff --git a/packages/theme/tokens/color.json b/packages/theme/tokens/color.json index 19243a72b85a57..224a422d093fc3 100644 --- a/packages/theme/tokens/color.json +++ b/packages/theme/tokens/color.json @@ -1262,7 +1262,7 @@ "$description": "Foreground color for interactive elements with neutral tone and normal emphasis." }, "neutral-active": { - "$value": "{wpds-color.primitive.bg.fgSurface4}", + "$value": "{wpds-color.primitive.bg.fgSurface5}", "$description": "Foreground color for interactive elements with neutral tone and normal emphasis that are hovered, focused, or active." }, "neutral-disabled": { @@ -1286,7 +1286,7 @@ "$description": "Foreground color for interactive elements with neutral tone and weak emphasis." }, "neutral-weak-active": { - "$value": "{wpds-color.primitive.bg.fgSurface4}", + "$value": "{wpds-color.primitive.bg.fgSurface5}", "$description": "Foreground color for interactive elements with neutral tone and weak emphasis that are hovered, focused, or active." }, "neutral-weak-disabled": { @@ -1294,11 +1294,11 @@ "$description": "Foreground color for interactive elements with neutral tone and weak emphasis, in their disabled state." }, "brand": { - "$value": "{wpds-color.primitive.primary.fgSurface3}", + "$value": "{wpds-color.primitive.primary.fgSurface4}", "$description": "Foreground color for interactive elements with brand tone and normal emphasis." }, "brand-active": { - "$value": "{wpds-color.primitive.primary.fgSurface4}", + "$value": "{wpds-color.primitive.primary.fgSurface5}", "$description": "Foreground color for interactive elements with brand tone and normal emphasis that are hovered, focused, or active." }, "brand-disabled": { @@ -1318,11 +1318,11 @@ "$description": "Foreground color for interactive elements with brand tone and strong emphasis, in their disabled state." }, "error": { - "$value": "{wpds-color.primitive.error.fgSurface3}", + "$value": "{wpds-color.primitive.error.fgSurface4}", "$description": "Foreground color for interactive elements with error tone and normal emphasis." }, "error-active": { - "$value": "{wpds-color.primitive.error.fgSurface4}", + "$value": "{wpds-color.primitive.error.fgSurface5}", "$description": "Foreground color for interactive elements with error tone and normal emphasis that are hovered, focused, or active." }, "error-disabled": { From 0ec345be31633494425359149ff61f0492f37c01 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Tue, 6 Oct 2026 15:29:34 +0200 Subject: [PATCH 28/44] Theme: Prototype stronger headings and interaction cues --- .../internal/_wpds-token-fallbacks.scss | 21 +- packages/components/CHANGELOG.md | 1 + .../components/src/external-link/style.scss | 7 +- packages/components/src/notice/style.scss | 1 + packages/components/src/tab-panel/style.scss | 3 +- packages/theme/CHANGELOG.md | 3 +- packages/theme/docs/tokens.md | 1 + packages/theme/prebuilt/css/design-tokens.css | 22 +- .../prebuilt/js/design-token-fallbacks.mjs | 21 +- packages/theme/src/color-ramps/README.md | 2 +- .../src/color-ramps/lib/default-ramps.ts | 14 +- .../theme/src/color-ramps/lib/ramp-configs.ts | 12 +- .../test/__snapshots__/index.test.ts.snap | 810 +++++++++--------- .../theme/src/color-ramps/test/index.test.ts | 6 +- .../theme/src/prebuilt/ts/color-tokens.ts | 1 + packages/theme/src/prebuilt/ts/token-types.ts | 3 +- .../src/semantic-color-contrast-pairs.ts | 12 + .../build-plugin-parity.test.ts.snap | 6 +- packages/theme/tokens/color.json | 32 +- packages/ui/CHANGELOG.md | 1 + packages/ui/src/link/style.module.css | 10 +- packages/ui/src/popover/style.module.css | 4 +- packages/ui/src/tabs/style.module.css | 3 +- packages/ui/src/text/style.module.css | 10 + packages/ui/src/text/types.ts | 1 + .../src/utils/css/overlay-chrome.module.css | 4 +- storybook/package-styles/config.js | 3 +- .../foreground-emphasis.story.tsx | 131 +++ 28 files changed, 664 insertions(+), 481 deletions(-) create mode 100644 storybook/stories/design-system/foreground-emphasis.story.tsx diff --git a/packages/base-styles/internal/_wpds-token-fallbacks.scss b/packages/base-styles/internal/_wpds-token-fallbacks.scss index b6c51f4e35a0bd..1cc7d7dbc419d0 100644 --- a/packages/base-styles/internal/_wpds-token-fallbacks.scss +++ b/packages/base-styles/internal/_wpds-token-fallbacks.scss @@ -62,39 +62,40 @@ $fallbacks: ( "--wpds-color-background-thumb-brand-active": "var(--wp-admin-theme-color, #3858e9)", "--wpds-color-background-thumb-brand-disabled": "#dbdbdb", - "--wpds-color-background-thumb-neutral": "#1e1e1e", + "--wpds-color-background-thumb-neutral": "#464646", "--wpds-color-background-thumb-neutral-weak": "#8d8d8d", "--wpds-color-background-thumb-neutral-weak-active": "#6e6e6e", "--wpds-color-background-thumb-neutral-weak-disabled": "#dbdbdb", "--wpds-color-background-track-neutral": "#dbdbdb", "--wpds-color-background-track-neutral-weak": "#f0f0f0", - "--wpds-color-foreground-content-caution": "#5c4f16", + "--wpds-color-foreground-content-caution": "#514513", "--wpds-color-foreground-content-caution-weak": "#7c6c21", - "--wpds-color-foreground-content-error": "#980f0f", + "--wpds-color-foreground-content-error": "#850c0c", "--wpds-color-foreground-content-error-weak": "#d81a1a", - "--wpds-color-foreground-content-info": "#005092", + "--wpds-color-foreground-content-info": "#004580", "--wpds-color-foreground-content-info-weak": "#006fc7", - "--wpds-color-foreground-content-neutral": "#505050", + "--wpds-color-foreground-content-neutral": "#464646", + "--wpds-color-foreground-content-neutral-strong": "#141414", "--wpds-color-foreground-content-neutral-weak": "#717171", - "--wpds-color-foreground-content-success": "#215b2f", + "--wpds-color-foreground-content-success": "#1b4f29", "--wpds-color-foreground-content-success-weak": "#307e44", - "--wpds-color-foreground-content-warning": "#654c19", + "--wpds-color-foreground-content-warning": "#594215", "--wpds-color-foreground-content-warning-weak": "#886726", "--wpds-color-foreground-interactive-brand": - "color-mix(in oklch, var(--wp-admin-theme-color, #3858e9) 88%, black)", + "color-mix(in oklch, var(--wp-admin-theme-color, #3858e9) 80%, black)", "--wpds-color-foreground-interactive-brand-active": "color-mix(in oklch, var(--wp-admin-theme-color, #3858e9) 35%, black)", "--wpds-color-foreground-interactive-brand-disabled": "#8d8d8d", "--wpds-color-foreground-interactive-brand-strong": "#fff", "--wpds-color-foreground-interactive-brand-strong-active": "#fff", "--wpds-color-foreground-interactive-brand-strong-disabled": "#8d8d8d", - "--wpds-color-foreground-interactive-error": "#980f0f", + "--wpds-color-foreground-interactive-error": "#850c0c", "--wpds-color-foreground-interactive-error-active": "#260101", "--wpds-color-foreground-interactive-error-disabled": "#8d8d8d", "--wpds-color-foreground-interactive-error-strong": "#f2efef", "--wpds-color-foreground-interactive-error-strong-active": "#f2efef", "--wpds-color-foreground-interactive-error-strong-disabled": "#8d8d8d", - "--wpds-color-foreground-interactive-neutral": "#505050", + "--wpds-color-foreground-interactive-neutral": "#464646", "--wpds-color-foreground-interactive-neutral-active": "#141414", "--wpds-color-foreground-interactive-neutral-disabled": "#8d8d8d", "--wpds-color-foreground-interactive-neutral-strong": "#f0f0f0", diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 15abc1b2afc9fe..933cc4f81bdc48 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -43,6 +43,7 @@ - `FormToggle`, `ToggleControl`: Mark as not recommended for use in a WordPress environment, in favour of `Switch` and `SwitchControl` from `@wordpress/ui` ([#84097](https://github.com/WordPress/gutenberg/pull/84097)). - `ProgressBar`: Mark as not recommended and direct consumers to `Progress` from `@wordpress/ui` ([#84021](https://github.com/WordPress/gutenberg/pull/84021)). +- Add interaction backgrounds to `TabPanel` tabs and `Notice` dismiss buttons, and thicken `ExternalLink` underlines on hover, press, and keyboard focus. ([#82294](https://github.com/WordPress/gutenberg/pull/82294)) - `CheckboxControl`: Mark as not recommended for use in a WordPress environment, in favour of `CheckboxControl` from `@wordpress/ui` ([#83771](https://github.com/WordPress/gutenberg/pull/83771)). - `RadioControl`: Mark as not recommended for use in a WordPress environment, in favour of `RadioGroupControl` from `@wordpress/ui` ([#83872](https://github.com/WordPress/gutenberg/pull/83872)). - `RadioControl`: Match the `@wordpress/ui` Radio colors ([#83270](https://github.com/WordPress/gutenberg/pull/83270)). diff --git a/packages/components/src/external-link/style.scss b/packages/components/src/external-link/style.scss index be9ae4ddf2969a..31210697ce4003 100644 --- a/packages/components/src/external-link/style.scss +++ b/packages/components/src/external-link/style.scss @@ -9,8 +9,13 @@ } &:hover, - &:active { + &:active, + &:focus-visible { color: var(--wpds-color-foreground-interactive-brand-active); + + .components-external-link__contents { + text-decoration-thickness: max(0.12em, var(--wpds-border-width-sm)); + } } &:focus { diff --git a/packages/components/src/notice/style.scss b/packages/components/src/notice/style.scss index b5a109e17a33c2..52a88a528e0c43 100644 --- a/packages/components/src/notice/style.scss +++ b/packages/components/src/notice/style.scss @@ -68,6 +68,7 @@ &:not(:disabled):not([aria-disabled="true"]):not(.is-secondary):active, &:not(:disabled):not([aria-disabled="true"]):focus { color: var(--wpds-color-foreground-interactive-neutral-active); + background-color: var(--wpds-color-background-interactive-neutral-weak-active); } } diff --git a/packages/components/src/tab-panel/style.scss b/packages/components/src/tab-panel/style.scss index 07830ae5be0c4e..b9eb7856fa5b36 100644 --- a/packages/components/src/tab-panel/style.scss +++ b/packages/components/src/tab-panel/style.scss @@ -28,8 +28,9 @@ color: var(--wpds-color-foreground-interactive-neutral-disabled); } - &:not(:disabled, [aria-disabled="true"]):is(:hover, :focus-visible) { + &:not(:disabled, [aria-disabled="true"]):is(:hover, :active, :focus-visible) { color: var(--wpds-color-foreground-interactive-neutral-active); + background-color: var(--wpds-color-background-interactive-neutral-weak-active); } &:focus:not(:disabled) { diff --git a/packages/theme/CHANGELOG.md b/packages/theme/CHANGELOG.md index 222ab75f046c75..7afab2fb921cf2 100644 --- a/packages/theme/CHANGELOG.md +++ b/packages/theme/CHANGELOG.md @@ -19,6 +19,7 @@ ### New Features +- Add `--wpds-color-foreground-content-neutral-strong` for headings and other content with strong emphasis. ([#82294](https://github.com/WordPress/gutenberg/pull/82294)) - Add `--wpds-color-background-thumb-neutral` for neutral progress indicators. ([#83781](https://github.com/WordPress/gutenberg/pull/83781)) ### Bug Fixes @@ -30,7 +31,7 @@ ### Enhancements -- Generate perception-aware color ramps with ordered surface and stroke progressions, preserve available surface spacing near black and white, align normal content with resting interactive colors, reserve the fifth foreground step for interaction states, and keep WCAG contrast floors unchanged. ([#82294](https://github.com/WordPress/gutenberg/pull/82294)) +- Generate perception-aware color ramps with ordered surface and stroke progressions, preserve available surface spacing near black and white, align normal content with resting interactive colors, use the fifth foreground step for strong content and interaction states, and keep WCAG contrast floors unchanged. ([#82294](https://github.com/WordPress/gutenberg/pull/82294)) ### Internal diff --git a/packages/theme/docs/tokens.md b/packages/theme/docs/tokens.md index 91c4d3c119e8d4..a54854d05645fc 100644 --- a/packages/theme/docs/tokens.md +++ b/packages/theme/docs/tokens.md @@ -239,6 +239,7 @@ The semantic token set is role-based, not a complete matrix of every property, t | `--wpds-color-background-thumb-brand-disabled` | Background color for thumbs with a brand tone and normal emphasis (eg. slider thumb and filled track), in their disabled state. | | `--wpds-color-background-thumb-neutral-weak-disabled` | Background color for thumbs with a neutral tone and weak emphasis (eg. scrollbar thumb), in their disabled state. | | `--wpds-color-foreground-content-neutral` | Foreground color for content like text with normal emphasis. | +| `--wpds-color-foreground-content-neutral-strong` | Foreground color for content like headings with strong emphasis. | | `--wpds-color-foreground-content-neutral-weak` | Foreground color for content like text with weak emphasis. | | `--wpds-color-foreground-content-success` | Foreground color for content like text with success tone and normal emphasis. | | `--wpds-color-foreground-content-success-weak` | Foreground color for content like text with success tone and weak emphasis. | diff --git a/packages/theme/prebuilt/css/design-tokens.css b/packages/theme/prebuilt/css/design-tokens.css index f67298e23e3fb7..c4bc7a7bc24d8d 100644 --- a/packages/theme/prebuilt/css/design-tokens.css +++ b/packages/theme/prebuilt/css/design-tokens.css @@ -106,7 +106,7 @@ /* Background color for thumbs with a brand tone and normal emphasis (eg. slider thumb and filled track), in their disabled state. */ --wpds-color-background-thumb-brand-disabled: #dbdbdb; /* Background color for thumbs with a neutral tone and normal emphasis (eg. progressbar indicator). */ - --wpds-color-background-thumb-neutral: #1e1e1e; + --wpds-color-background-thumb-neutral: #464646; /* Background color for thumbs with a neutral tone and weak emphasis (eg. scrollbar thumb). */ --wpds-color-background-thumb-neutral-weak: #8d8d8d; /* Background color for thumbs with a neutral tone and weak emphasis (eg. scrollbar thumb) that are hovered, focused, or active. */ @@ -118,31 +118,33 @@ /* Background color for tracks with a neutral tone and weak emphasis (eg. scrollbar track). */ --wpds-color-background-track-neutral-weak: #f0f0f0; /* Foreground color for content like text with caution tone and normal emphasis. */ - --wpds-color-foreground-content-caution: #5c4f16; + --wpds-color-foreground-content-caution: #514513; /* Foreground color for content like text with caution tone and weak emphasis. */ --wpds-color-foreground-content-caution-weak: #7c6c21; /* Foreground color for content like text with error tone and normal emphasis. */ - --wpds-color-foreground-content-error: #980f0f; + --wpds-color-foreground-content-error: #850c0c; /* Foreground color for content like text with error tone and weak emphasis. */ --wpds-color-foreground-content-error-weak: #d81a1a; /* Foreground color for content like text with info tone and normal emphasis. */ - --wpds-color-foreground-content-info: #005092; + --wpds-color-foreground-content-info: #004580; /* Foreground color for content like text with info tone and weak emphasis. */ --wpds-color-foreground-content-info-weak: #006fc7; /* Foreground color for content like text with normal emphasis. */ - --wpds-color-foreground-content-neutral: #505050; + --wpds-color-foreground-content-neutral: #464646; + /* Foreground color for content like headings with strong emphasis. */ + --wpds-color-foreground-content-neutral-strong: #141414; /* Foreground color for content like text with weak emphasis. */ --wpds-color-foreground-content-neutral-weak: #717171; /* Foreground color for content like text with success tone and normal emphasis. */ - --wpds-color-foreground-content-success: #215b2f; + --wpds-color-foreground-content-success: #1b4f29; /* Foreground color for content like text with success tone and weak emphasis. */ --wpds-color-foreground-content-success-weak: #307e44; /* Foreground color for content like text with warning tone and normal emphasis. */ - --wpds-color-foreground-content-warning: #654c19; + --wpds-color-foreground-content-warning: #594215; /* Foreground color for content like text with warning tone and weak emphasis. */ --wpds-color-foreground-content-warning-weak: #886726; /* Foreground color for interactive elements with brand tone and normal emphasis. */ - --wpds-color-foreground-interactive-brand: #222eda; + --wpds-color-foreground-interactive-brand: #1d27bf; /* Foreground color for interactive elements with brand tone and normal emphasis that are hovered, focused, or active. */ --wpds-color-foreground-interactive-brand-active: #03063b; /* Foreground color for interactive elements with brand tone and normal emphasis, in their disabled state. */ @@ -154,7 +156,7 @@ /* Foreground color for interactive elements with brand tone and strong emphasis, in their disabled state. */ --wpds-color-foreground-interactive-brand-strong-disabled: #8d8d8d; /* Foreground color for interactive elements with error tone and normal emphasis. */ - --wpds-color-foreground-interactive-error: #980f0f; + --wpds-color-foreground-interactive-error: #850c0c; /* Foreground color for interactive elements with error tone and normal emphasis that are hovered, focused, or active. */ --wpds-color-foreground-interactive-error-active: #260101; /* Foreground color for interactive elements with error tone and normal emphasis, in their disabled state. */ @@ -166,7 +168,7 @@ /* Foreground color for interactive elements with error tone and strong emphasis, in their disabled state. */ --wpds-color-foreground-interactive-error-strong-disabled: #8d8d8d; /* Foreground color for interactive elements with neutral tone and normal emphasis. */ - --wpds-color-foreground-interactive-neutral: #505050; + --wpds-color-foreground-interactive-neutral: #464646; /* Foreground color for interactive elements with neutral tone and normal emphasis that are hovered, focused, or active. */ --wpds-color-foreground-interactive-neutral-active: #141414; /* Foreground color for interactive elements with neutral tone and normal emphasis, in their disabled state. */ diff --git a/packages/theme/prebuilt/js/design-token-fallbacks.mjs b/packages/theme/prebuilt/js/design-token-fallbacks.mjs index 2732d5d7667044..c5c4e4619ec882 100644 --- a/packages/theme/prebuilt/js/design-token-fallbacks.mjs +++ b/packages/theme/prebuilt/js/design-token-fallbacks.mjs @@ -60,39 +60,40 @@ export default { '--wpds-color-background-thumb-brand-active': 'var(--wp-admin-theme-color, #3858e9)', '--wpds-color-background-thumb-brand-disabled': '#dbdbdb', - '--wpds-color-background-thumb-neutral': '#1e1e1e', + '--wpds-color-background-thumb-neutral': '#464646', '--wpds-color-background-thumb-neutral-weak': '#8d8d8d', '--wpds-color-background-thumb-neutral-weak-active': '#6e6e6e', '--wpds-color-background-thumb-neutral-weak-disabled': '#dbdbdb', '--wpds-color-background-track-neutral': '#dbdbdb', '--wpds-color-background-track-neutral-weak': '#f0f0f0', - '--wpds-color-foreground-content-caution': '#5c4f16', + '--wpds-color-foreground-content-caution': '#514513', '--wpds-color-foreground-content-caution-weak': '#7c6c21', - '--wpds-color-foreground-content-error': '#980f0f', + '--wpds-color-foreground-content-error': '#850c0c', '--wpds-color-foreground-content-error-weak': '#d81a1a', - '--wpds-color-foreground-content-info': '#005092', + '--wpds-color-foreground-content-info': '#004580', '--wpds-color-foreground-content-info-weak': '#006fc7', - '--wpds-color-foreground-content-neutral': '#505050', + '--wpds-color-foreground-content-neutral': '#464646', + '--wpds-color-foreground-content-neutral-strong': '#141414', '--wpds-color-foreground-content-neutral-weak': '#717171', - '--wpds-color-foreground-content-success': '#215b2f', + '--wpds-color-foreground-content-success': '#1b4f29', '--wpds-color-foreground-content-success-weak': '#307e44', - '--wpds-color-foreground-content-warning': '#654c19', + '--wpds-color-foreground-content-warning': '#594215', '--wpds-color-foreground-content-warning-weak': '#886726', '--wpds-color-foreground-interactive-brand': - 'color-mix(in oklch, var(--wp-admin-theme-color, #3858e9) 88%, black)', + 'color-mix(in oklch, var(--wp-admin-theme-color, #3858e9) 80%, black)', '--wpds-color-foreground-interactive-brand-active': 'color-mix(in oklch, var(--wp-admin-theme-color, #3858e9) 35%, black)', '--wpds-color-foreground-interactive-brand-disabled': '#8d8d8d', '--wpds-color-foreground-interactive-brand-strong': '#fff', '--wpds-color-foreground-interactive-brand-strong-active': '#fff', '--wpds-color-foreground-interactive-brand-strong-disabled': '#8d8d8d', - '--wpds-color-foreground-interactive-error': '#980f0f', + '--wpds-color-foreground-interactive-error': '#850c0c', '--wpds-color-foreground-interactive-error-active': '#260101', '--wpds-color-foreground-interactive-error-disabled': '#8d8d8d', '--wpds-color-foreground-interactive-error-strong': '#f2efef', '--wpds-color-foreground-interactive-error-strong-active': '#f2efef', '--wpds-color-foreground-interactive-error-strong-disabled': '#8d8d8d', - '--wpds-color-foreground-interactive-neutral': '#505050', + '--wpds-color-foreground-interactive-neutral': '#464646', '--wpds-color-foreground-interactive-neutral-active': '#141414', '--wpds-color-foreground-interactive-neutral-disabled': '#8d8d8d', '--wpds-color-foreground-interactive-neutral-strong': '#f0f0f0', diff --git a/packages/theme/src/color-ramps/README.md b/packages/theme/src/color-ramps/README.md index 9c454e4efb9095..064db1d81fd5e2 100644 --- a/packages/theme/src/color-ramps/README.md +++ b/packages/theme/src/color-ramps/README.md @@ -29,7 +29,7 @@ Perceptual spacing is a preference; configured WCAG contrast floors take priorit 4. **Place foregrounds.** [`buildForegroundScale`](./lib/build-foreground-scale.ts) uses APCA, which treats light-on-dark and dark-on-light contrast differently. It compares foregrounds to the background ramp's reference. Neutral foregrounds retain tapering; accents preserve the seed's share of available chroma. WCAG floors apply to configured surfaces in both ramps. If the preferred gaps cannot fit, FGS4 moves toward its WCAG floor to leave range for FGS5. Rounded hex output is checked and corrected where possible. 5. **Build accents, then map tokens.** Accents run the same passes, inheriting the completed background's direction and a bounded version of its SF2 lightness. [`use-theme-provider-styles.ts`](../use-theme-provider-styles.ts) maps the results to semantic CSS properties using the generated alias map. -Surface foregrounds use FGS2 for disabled controls, FGS3 for weak emphasis, FGS4 for normal content and resting controls, and FGS5 for interaction states. FGS5 can approach black or white at the expense of chroma. FGS1 is no longer generated. Strong fills use separate foregrounds. See [`tokens/color.json`](../../tokens/color.json) for exact mappings. +Surface foregrounds use FGS2 for disabled controls, FGS3 for weak emphasis, FGS4 for normal content and resting controls, and FGS5 for strong neutral content and interaction states. FGS5 can approach black or white at the expense of chroma. FGS1 is no longer generated. Strong fills use separate foregrounds. See [`tokens/color.json`](../../tokens/color.json) for exact mappings. ## Checks and generated files diff --git a/packages/theme/src/color-ramps/lib/default-ramps.ts b/packages/theme/src/color-ramps/lib/default-ramps.ts index 79d004a079e57d..c913ded444c6b0 100644 --- a/packages/theme/src/color-ramps/lib/default-ramps.ts +++ b/packages/theme/src/color-ramps/lib/default-ramps.ts @@ -25,7 +25,7 @@ export const DEFAULT_RAMPS: Record< bgFillInverted1: '#2d2d2d', fgFillInverted: '#f0f0f0', surface1: '#f7f7f7', - fgSurface4: '#505050', + fgSurface4: '#464646', fgSurface5: '#141414', }, direction: 'darker', @@ -50,7 +50,7 @@ export const DEFAULT_RAMPS: Record< bgFillInverted1: '#13009f', fgFillInverted: '#eff0f2', surface1: '#edf1fb', - fgSurface4: '#222eda', + fgSurface4: '#1d27bf', fgSurface5: '#03063b', }, direction: 'darker', @@ -75,7 +75,7 @@ export const DEFAULT_RAMPS: Record< bgFillInverted1: '#002a69', fgFillInverted: '#eef0f2', surface1: '#e8f2fe', - fgSurface4: '#005092', + fgSurface4: '#004580', fgSurface5: '#00132a', }, direction: 'darker', @@ -100,7 +100,7 @@ export const DEFAULT_RAMPS: Record< bgFillInverted1: '#003701', fgFillInverted: '#ecf2ec', surface1: '#d6fddb', - fgSurface4: '#215b2f', + fgSurface4: '#1b4f29', fgSurface5: '#041708', }, direction: 'darker', @@ -125,7 +125,7 @@ export const DEFAULT_RAMPS: Record< bgFillInverted1: '#392c00', fgFillInverted: '#faf1c9', surface1: '#ffefa9', - fgSurface4: '#5c4f16', + fgSurface4: '#514513', fgSurface5: '#181402', }, direction: 'darker', @@ -150,7 +150,7 @@ export const DEFAULT_RAMPS: Record< bgFillInverted1: '#422800', fgFillInverted: '#f5efe5', surface1: '#ffedcc', - fgSurface4: '#654c19', + fgSurface4: '#594215', fgSurface5: '#1b1203', }, direction: 'darker', @@ -175,7 +175,7 @@ export const DEFAULT_RAMPS: Record< bgFillInverted1: '#640000', fgFillInverted: '#f2efef', surface1: '#fdedea', - fgSurface4: '#980f0f', + fgSurface4: '#850c0c', fgSurface5: '#260101', }, direction: 'darker', diff --git a/packages/theme/src/color-ramps/lib/ramp-configs.ts b/packages/theme/src/color-ramps/lib/ramp-configs.ts index d11b0c3ea1c8d6..6fc1e0b49f4674 100644 --- a/packages/theme/src/color-ramps/lib/ramp-configs.ts +++ b/packages/theme/src/color-ramps/lib/ramp-configs.ts @@ -57,14 +57,14 @@ const FOREGROUND_PERCEPTUAL_TARGETS = { // the hard gates for every surface on which a foreground can appear. // Prefer strong normal text, but lower it when that is necessary to preserve // a visible active state before the gamut endpoint. - normalContrast: 86, - // Let the interaction-state endpoint approach black or white when that makes - // the state change more visible. WCAG remains a hard floor. + normalContrast: 90, + // Let strong content and interaction states approach black or white. + // WCAG remains a hard floor. endpointReserve: 1, - // Keep weak supporting content visibly separate from normal content, then - // reserve a larger interval for the interaction-state-only endpoint. + // Separate weak supporting content from normal content, while leaving a + // smaller interval for strong content and interaction states. weakToNormal: 12, - normalToActive: 14, + normalToActive: 10, } as const; const FOREGROUND_SCALE_STEPS = [ diff --git a/packages/theme/src/color-ramps/test/__snapshots__/index.test.ts.snap b/packages/theme/src/color-ramps/test/__snapshots__/index.test.ts.snap index 1a1d6b82c46f89..f4fbd7057bfcf6 100644 --- a/packages/theme/src/color-ramps/test/__snapshots__/index.test.ts.snap +++ b/packages/theme/src/color-ramps/test/__snapshots__/index.test.ts.snap @@ -27,7 +27,7 @@ exports[`buildRamps > accent ramp snapshots for admin themes 1`] = ` "fgFillInverted": "#1b1e21", "fgSurface2": "#2b628f", "fgSurface3": "#437ba9", - "fgSurface4": "#cbe0f3", + "fgSurface4": "#d5e6f5", "fgSurface5": "#fdfefe", "stroke1": "#496680", "stroke2": "#55738d", @@ -67,7 +67,7 @@ exports[`buildRamps > accent ramp snapshots for admin themes 1`] = ` "fgFillInverted": "#1b1e21", "fgSurface2": "#2d6491", "fgSurface3": "#467faf", - "fgSurface4": "#cbe0f3", + "fgSurface4": "#d5e6f5", "fgSurface5": "#fdfefe", "stroke1": "#496680", "stroke2": "#55738d", @@ -107,7 +107,7 @@ exports[`buildRamps > accent ramp snapshots for admin themes 1`] = ` "fgFillInverted": "#1b1e21", "fgSurface2": "#366d9a", "fgSurface3": "#4d8bbe", - "fgSurface4": "#cde1f3", + "fgSurface4": "#d7e7f6", "fgSurface5": "#fdfefe", "stroke1": "#496680", "stroke2": "#55738d", @@ -147,7 +147,7 @@ exports[`buildRamps > accent ramp snapshots for admin themes 1`] = ` "fgFillInverted": "#1b1e21", "fgSurface2": "#4a81af", "fgSurface3": "#5aa0db", - "fgSurface4": "#d7e7f6", + "fgSurface4": "#e0edf8", "fgSurface5": "#fdfefe", "stroke1": "#496680", "stroke2": "#55738d", @@ -187,7 +187,7 @@ exports[`buildRamps > accent ramp snapshots for admin themes 1`] = ` "fgFillInverted": "#1b1e21", "fgSurface2": "#69a0d1", "fgSurface3": "#98c2e8", - "fgSurface4": "#dbe9f7", + "fgSurface4": "#e5eff9", "fgSurface5": "#fdfefe", "stroke1": "#4e6c87", "stroke2": "#5c7d9a", @@ -268,7 +268,7 @@ exports[`buildRamps > accent ramp snapshots for admin themes 1`] = ` "fgSurface2": "#2f6793", "fgSurface3": "#274b6a", "fgSurface4": "#22425d", - "fgSurface5": "#000", + "fgSurface5": "#071520", "stroke1": "#7096b9", "stroke2": "#6289aa", "stroke3": "#114c77", @@ -307,7 +307,7 @@ exports[`buildRamps > accent ramp snapshots for admin themes 1`] = ` "fgFillInverted": "#eef0f2", "fgSurface2": "#467dab", "fgSurface3": "#346187", - "fgSurface4": "#2e5779", + "fgSurface4": "#21405b", "fgSurface5": "#071520", "stroke1": "#91b0cd", "stroke2": "#80a0bd", @@ -347,7 +347,7 @@ exports[`buildRamps > accent ramp snapshots for admin themes 1`] = ` "fgFillInverted": "#eef0f2", "fgSurface2": "#5c93c3", "fgSurface3": "#4278a6", - "fgSurface4": "#2f587a", + "fgSurface4": "#284d6c", "fgSurface5": "#071520", "stroke1": "#b6cbde", "stroke2": "#a2b8cc", @@ -389,7 +389,7 @@ exports[`buildRamps > accent ramp snapshots for admin themes 1`] = ` "fgFillInverted": "#201d1b", "fgSurface2": "#7e5534", "fgSurface3": "#986d4b", - "fgSurface4": "#f0d9c7", + "fgSurface4": "#f3e0d2", "fgSurface5": "#fefdfd", "stroke1": "#755d4a", "stroke2": "#836a56", @@ -429,7 +429,7 @@ exports[`buildRamps > accent ramp snapshots for admin themes 1`] = ` "fgFillInverted": "#201d1b", "fgSurface2": "#805735", "fgSurface3": "#9e714d", - "fgSurface4": "#f0d9c8", + "fgSurface4": "#f3e0d2", "fgSurface5": "#fefdfd", "stroke1": "#755d4a", "stroke2": "#836a56", @@ -469,7 +469,7 @@ exports[`buildRamps > accent ramp snapshots for admin themes 1`] = ` "fgFillInverted": "#201d1b", "fgSurface2": "#8a603f", "fgSurface3": "#ab7b55", - "fgSurface4": "#f1dbca", + "fgSurface4": "#f4e2d5", "fgSurface5": "#fefdfd", "stroke1": "#755d4a", "stroke2": "#836a56", @@ -509,7 +509,7 @@ exports[`buildRamps > accent ramp snapshots for admin themes 1`] = ` "fgFillInverted": "#201d1b", "fgSurface2": "#9d7250", "fgSurface3": "#c58e63", - "fgSurface4": "#f3e1d4", + "fgSurface4": "#f6e8de", "fgSurface5": "#fefdfd", "stroke1": "#755d4a", "stroke2": "#836a56", @@ -549,8 +549,8 @@ exports[`buildRamps > accent ramp snapshots for admin themes 1`] = ` "fgFillInverted": "#201d1b", "fgSurface2": "#bc906d", "fgSurface3": "#e3b28c", - "fgSurface4": "#f5e5d9", - "fgSurface5": "#fefefd", + "fgSurface4": "#f7ece3", + "fgSurface5": "#fefdfd", "stroke1": "#7d634f", "stroke2": "#8d725c", "stroke3": "#e1b28e", @@ -630,7 +630,7 @@ exports[`buildRamps > accent ramp snapshots for admin themes 1`] = ` "fgSurface2": "#815836", "fgSurface3": "#5d412a", "fgSurface4": "#513824", - "fgSurface5": "#000", + "fgSurface5": "#190f07", "stroke1": "#ad8a6f", "stroke2": "#9d7c61", "stroke3": "#653e1c", @@ -669,7 +669,7 @@ exports[`buildRamps > accent ramp snapshots for admin themes 1`] = ` "fgFillInverted": "#f2efee", "fgSurface2": "#9a6f4d", "fgSurface3": "#795539", - "fgSurface4": "#6c4c32", + "fgSurface4": "#523824", "fgSurface5": "#1d1108", "stroke1": "#cca486", "stroke2": "#bc9577", @@ -709,7 +709,7 @@ exports[`buildRamps > accent ramp snapshots for admin themes 1`] = ` "fgFillInverted": "#f2efee", "fgSurface2": "#b28663", "fgSurface3": "#956a47", - "fgSurface4": "#6f4e33", + "fgSurface4": "#62442c", "fgSurface5": "#1d1108", "stroke1": "#dfc1aa", "stroke2": "#cdae96", @@ -751,7 +751,7 @@ exports[`buildRamps > accent ramp snapshots for admin themes 1`] = ` "fgFillInverted": "#1e1e1c", "fgSurface2": "#5b6235", "fgSurface3": "#727b47", - "fgSurface4": "#d5e58a", + "fgSurface4": "#dcec8e", "fgSurface5": "#fdfef8", "stroke1": "#61664b", "stroke2": "#6d7357", @@ -791,7 +791,7 @@ exports[`buildRamps > accent ramp snapshots for admin themes 1`] = ` "fgFillInverted": "#1e1e1c", "fgSurface2": "#5c6436", "fgSurface3": "#767f4a", - "fgSurface4": "#d5e58a", + "fgSurface4": "#dcec8e", "fgSurface5": "#fdfef8", "stroke1": "#61664b", "stroke2": "#6d7357", @@ -831,7 +831,7 @@ exports[`buildRamps > accent ramp snapshots for admin themes 1`] = ` "fgFillInverted": "#1e1e1c", "fgSurface2": "#656d3f", "fgSurface3": "#808a51", - "fgSurface4": "#d7e78b", + "fgSurface4": "#dded8f", "fgSurface5": "#fdfef8", "stroke1": "#61664b", "stroke2": "#6d7357", @@ -871,7 +871,7 @@ exports[`buildRamps > accent ramp snapshots for admin themes 1`] = ` "fgFillInverted": "#1e1e1c", "fgSurface2": "#788152", "fgSurface3": "#95a15f", - "fgSurface4": "#dded8e", + "fgSurface4": "#e3f492", "fgSurface5": "#fdfef8", "stroke1": "#61664b", "stroke2": "#6d7357", @@ -911,8 +911,8 @@ exports[`buildRamps > accent ramp snapshots for admin themes 1`] = ` "fgFillInverted": "#1e1e1c", "fgSurface2": "#97a070", "fgSurface3": "#b7c576", - "fgSurface4": "#e0f090", - "fgSurface5": "#fdfef8", + "fgSurface4": "#e6f794", + "fgSurface5": "#fdfef9", "stroke1": "#676d4f", "stroke2": "#737a5b", "stroke3": "#b9c391", @@ -992,7 +992,7 @@ exports[`buildRamps > accent ramp snapshots for admin themes 1`] = ` "fgSurface2": "#60673a", "fgSurface3": "#464c2a", "fgSurface4": "#3d4324", - "fgSurface5": "#000", + "fgSurface5": "#131508", "stroke1": "#909770", "stroke2": "#828863", "stroke3": "#464d20", @@ -1031,7 +1031,7 @@ exports[`buildRamps > accent ramp snapshots for admin themes 1`] = ` "fgFillInverted": "#eff2e3", "fgSurface2": "#767e4f", "fgSurface3": "#5b6238", - "fgSurface4": "#515831", + "fgSurface4": "#3c4123", "fgSurface5": "#131508", "stroke1": "#aab386", "stroke2": "#9aa277", @@ -1071,7 +1071,7 @@ exports[`buildRamps > accent ramp snapshots for admin themes 1`] = ` "fgFillInverted": "#eff2e3", "fgSurface2": "#8a9363", "fgSurface3": "#6e7745", - "fgSurface4": "#525832", + "fgSurface4": "#484e2b", "fgSurface5": "#131508", "stroke1": "#b7c090", "stroke2": "#a6b081", @@ -1113,7 +1113,7 @@ exports[`buildRamps > accent ramp snapshots for admin themes 1`] = ` "fgFillInverted": "#211d1b", "fgSurface2": "#934c00", "fgSurface3": "#ae641e", - "fgSurface4": "#fcd6ba", + "fgSurface4": "#fddec7", "fgSurface5": "#fffdfc", "stroke1": "#7a5a41", "stroke2": "#89674d", @@ -1153,7 +1153,7 @@ exports[`buildRamps > accent ramp snapshots for admin themes 1`] = ` "fgFillInverted": "#211d1b", "fgSurface2": "#954d00", "fgSurface3": "#b3671f", - "fgSurface4": "#fcd6ba", + "fgSurface4": "#fddec7", "fgSurface5": "#fffdfc", "stroke1": "#7a5a41", "stroke2": "#89674d", @@ -1193,7 +1193,7 @@ exports[`buildRamps > accent ramp snapshots for admin themes 1`] = ` "fgFillInverted": "#211d1b", "fgSurface2": "#9f5707", "fgSurface3": "#c47123", - "fgSurface4": "#fcd8bd", + "fgSurface4": "#fde0cb", "fgSurface5": "#fffdfc", "stroke1": "#7a5a41", "stroke2": "#89674d", @@ -1233,7 +1233,7 @@ exports[`buildRamps > accent ramp snapshots for admin themes 1`] = ` "fgFillInverted": "#211d1b", "fgSurface2": "#b36925", "fgSurface3": "#e1822a", - "fgSurface4": "#fddfc9", + "fgSurface4": "#fde6d6", "fgSurface5": "#fffdfc", "stroke1": "#7a5a41", "stroke2": "#89674d", @@ -1273,7 +1273,7 @@ exports[`buildRamps > accent ramp snapshots for admin themes 1`] = ` "fgFillInverted": "#211d1b", "fgSurface2": "#d48747", "fgSurface3": "#f9a96a", - "fgSurface4": "#fde3d0", + "fgSurface4": "#fdebdd", "fgSurface5": "#fffdfc", "stroke1": "#836046", "stroke2": "#926e53", @@ -1354,7 +1354,7 @@ exports[`buildRamps > accent ramp snapshots for admin themes 1`] = ` "fgSurface2": "#964e00", "fgSurface3": "#6b3b0e", "fgSurface4": "#5d320b", - "fgSurface5": "#000", + "fgSurface5": "#1f0d01", "stroke1": "#b48764", "stroke2": "#a57958", "stroke3": "#763400", @@ -1393,8 +1393,8 @@ exports[`buildRamps > accent ramp snapshots for admin themes 1`] = ` "fgFillInverted": "#f3efed", "fgSurface2": "#b06521", "fgSurface3": "#894d15", - "fgSurface4": "#7c4512", - "fgSurface5": "#210e02", + "fgSurface4": "#5c320b", + "fgSurface5": "#1f0d01", "stroke1": "#d5a27b", "stroke2": "#c4926c", "stroke3": "#914900", @@ -1433,7 +1433,7 @@ exports[`buildRamps > accent ramp snapshots for admin themes 1`] = ` "fgFillInverted": "#f3efed", "fgSurface2": "#c87c3c", "fgSurface3": "#a9611d", - "fgSurface4": "#7d4613", + "fgSurface4": "#6f3e0f", "fgSurface5": "#210e02", "stroke1": "#e6c1a6", "stroke2": "#d3ac8f", @@ -1480,7 +1480,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgFillInverted": "#1e1e1e", "fgSurface2": "#5e5e5e", "fgSurface3": "#767676", - "fgSurface4": "#ddd", + "fgSurface4": "#e3e3e3", "fgSurface5": "#fdfdfd", "stroke1": "#787878", "stroke2": "#858585", @@ -1520,7 +1520,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgFillInverted": "#1e1e1e", "fgSurface2": "#606060", "fgSurface3": "#7a7a7a", - "fgSurface4": "#ddd", + "fgSurface4": "#e3e3e3", "fgSurface5": "#fdfdfd", "stroke1": "#787878", "stroke2": "#858585", @@ -1560,7 +1560,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgFillInverted": "#1e1e1e", "fgSurface2": "#696969", "fgSurface3": "#868686", - "fgSurface4": "#dedede", + "fgSurface4": "#e5e5e5", "fgSurface5": "#fdfdfd", "stroke1": "#787878", "stroke2": "#858585", @@ -1600,7 +1600,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgFillInverted": "#1e1e1e", "fgSurface2": "#7c7c7c", "fgSurface3": "#9b9b9b", - "fgSurface4": "#e4e4e4", + "fgSurface4": "#ebebeb", "fgSurface5": "#fdfdfd", "stroke1": "#787878", "stroke2": "#858585", @@ -1640,7 +1640,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgFillInverted": "#1e1e1e", "fgSurface2": "#9a9a9a", "fgSurface3": "#bebebe", - "fgSurface4": "#e8e8e8", + "fgSurface4": "#eee", "fgSurface5": "#fefefe", "stroke1": "#787878", "stroke2": "#858585", @@ -1721,7 +1721,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgSurface2": "#626262", "fgSurface3": "#484848", "fgSurface4": "#3e3e3e", - "fgSurface5": "#000", + "fgSurface5": "#141414", "stroke1": "#929292", "stroke2": "#838383", "stroke3": "#484848", @@ -1760,7 +1760,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface2": "#787878", "fgSurface3": "#5d5d5d", - "fgSurface4": "#535353", + "fgSurface4": "#3e3e3e", "fgSurface5": "#141414", "stroke1": "#acacac", "stroke2": "#9d9d9d", @@ -1800,7 +1800,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", - "fgSurface4": "#555", + "fgSurface4": "#4b4b4b", "fgSurface5": "#141414", "stroke1": "#c9c9c9", "stroke2": "#b7b7b7", @@ -1842,7 +1842,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgFillInverted": "#1a1e27", "fgSurface2": "#314ede", "fgSurface3": "#496ceb", - "fgSurface4": "#d1ddfa", + "fgSurface4": "#dae4fb", "fgSurface5": "#fdfeff", "stroke1": "#435caa", "stroke2": "#506ab9", @@ -1882,7 +1882,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgFillInverted": "#1a1e27", "fgSurface2": "#324fe0", "fgSurface3": "#4d71ec", - "fgSurface4": "#d2ddfa", + "fgSurface4": "#dae4fb", "fgSurface5": "#fdfeff", "stroke1": "#435caa", "stroke2": "#506ab9", @@ -1922,7 +1922,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgFillInverted": "#1a1e27", "fgSurface2": "#3959ea", "fgSurface3": "#5b7eed", - "fgSurface4": "#d4dffa", + "fgSurface4": "#dce5fb", "fgSurface5": "#fdfeff", "stroke1": "#435caa", "stroke2": "#506ab9", @@ -1962,7 +1962,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgFillInverted": "#1a1e27", "fgSurface2": "#486dff", "fgSurface3": "#7696f0", - "fgSurface4": "#dbe5fb", + "fgSurface4": "#e4ebfc", "fgSurface5": "#fdfeff", "stroke1": "#435caa", "stroke2": "#506ab9", @@ -2002,7 +2002,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgFillInverted": "#1a1e27", "fgSurface2": "#6a91ff", "fgSurface3": "#a0b8f5", - "fgSurface4": "#e0e8fb", + "fgSurface4": "#e8eefc", "fgSurface5": "#fdfeff", "stroke1": "#4962ad", "stroke2": "#5771bc", @@ -2083,7 +2083,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgSurface2": "#3351e2", "fgSurface3": "#202bce", "fgSurface4": "#1b24b5", - "fgSurface5": "#000", + "fgSurface5": "#03063b", "stroke1": "#7d91c4", "stroke2": "#6d81b5", "stroke3": "#2031c3", @@ -2122,7 +2122,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgFillInverted": "#eff0f2", "fgSurface2": "#466bfd", "fgSurface3": "#2d47e7", - "fgSurface4": "#2636e6", + "fgSurface4": "#1822aa", "fgSurface5": "#03063b", "stroke1": "#9dacd2", "stroke2": "#8b9bc2", @@ -2162,7 +2162,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgFillInverted": "#eff0f2", "fgSurface2": "#6088ff", "fgSurface3": "#4669eb", - "fgSurface4": "#2531e5", + "fgSurface4": "#1f2acb", "fgSurface5": "#03063b", "stroke1": "#aebbda", "stroke2": "#9ba9c9", @@ -2204,7 +2204,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgFillInverted": "#1a1f24", "fgSurface2": "#005bbf", "fgSurface3": "#0078d5", - "fgSurface4": "#c4e0ff", + "fgSurface4": "#d0e6ff", "fgSurface5": "#fcfeff", "stroke1": "#27476a", "stroke2": "#325478", @@ -2244,7 +2244,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgFillInverted": "#1a1f24", "fgSurface2": "#005dc1", "fgSurface3": "#007cdd", - "fgSurface4": "#c5e0ff", + "fgSurface4": "#d0e6ff", "fgSurface5": "#fcfeff", "stroke1": "#27476a", "stroke2": "#325478", @@ -2284,7 +2284,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgFillInverted": "#1a1f24", "fgSurface2": "#0266d1", "fgSurface3": "#0087f0", - "fgSurface4": "#c8e1ff", + "fgSurface4": "#d3e7ff", "fgSurface5": "#fcfeff", "stroke1": "#27476a", "stroke2": "#325478", @@ -2324,7 +2324,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgFillInverted": "#1a1f24", "fgSurface2": "#007be8", "fgSurface3": "#3d9eff", - "fgSurface4": "#d2e7ff", + "fgSurface4": "#ddedff", "fgSurface5": "#fcfeff", "stroke1": "#3c6490", "stroke2": "#48719f", @@ -2364,7 +2364,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgFillInverted": "#1a1f24", "fgSurface2": "#289eff", "fgSurface3": "#89c1ff", - "fgSurface4": "#d7eaff", + "fgSurface4": "#e2efff", "fgSurface5": "#fcfeff", "stroke1": "#416b9a", "stroke2": "#4c79a8", @@ -2445,7 +2445,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgSurface2": "#005fc6", "fgSurface3": "#004986", "fgSurface4": "#003f75", - "fgSurface5": "#000", + "fgSurface5": "#001329", "stroke1": "#6896c8", "stroke2": "#5986b9", "stroke3": "#004599", @@ -2484,7 +2484,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgFillInverted": "#eef0f2", "fgSurface2": "#0077e4", "fgSurface3": "#005eaa", - "fgSurface4": "#00549a", + "fgSurface4": "#003d72", "fgSurface5": "#00132a", "stroke1": "#8db0d7", "stroke2": "#7b9ec7", @@ -2524,7 +2524,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgFillInverted": "#eef0f2", "fgSurface2": "#0190ff", "fgSurface3": "#0075d0", - "fgSurface4": "#005499", + "fgSurface4": "#004a88", "fgSurface5": "#00132a", "stroke1": "#b4cbe5", "stroke2": "#9fb7d3", @@ -2566,7 +2566,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgFillInverted": "#1b1f1c", "fgSurface2": "#006e1e", "fgSurface3": "#348648", - "fgSurface4": "#6af88d", + "fgSurface4": "#95f9a9", "fgSurface5": "#fafffb", "stroke1": "#3d5d42", "stroke2": "#486a4e", @@ -2606,7 +2606,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgFillInverted": "#1b1f1c", "fgSurface2": "#007020", "fgSurface3": "#368a4b", - "fgSurface4": "#6af88d", + "fgSurface4": "#95f9a9", "fgSurface5": "#fafffb", "stroke1": "#3d5d42", "stroke2": "#486a4e", @@ -2646,7 +2646,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgFillInverted": "#1b1f1c", "fgSurface2": "#007b2b", "fgSurface3": "#3c9853", - "fgSurface4": "#78f895", + "fgSurface4": "#9ef9af", "fgSurface5": "#fafffb", "stroke1": "#3d5d42", "stroke2": "#486a4e", @@ -2686,7 +2686,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgFillInverted": "#1b1f1c", "fgSurface2": "#149141", "fgSurface3": "#47b062", - "fgSurface4": "#9df9ae", + "fgSurface4": "#bafac5", "fgSurface5": "#fafffb", "stroke1": "#3d5d42", "stroke2": "#486a4e", @@ -2726,7 +2726,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgFillInverted": "#1b1f1c", "fgSurface2": "#43b260", "fgSurface3": "#59d979", - "fgSurface4": "#aafab8", + "fgSurface4": "#c5fbcd", "fgSurface5": "#fafffb", "stroke1": "#4d7454", "stroke2": "#5a8260", @@ -2807,7 +2807,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgSurface2": "#007627", "fgSurface3": "#1e552c", "fgSurface4": "#194b26", - "fgSurface5": "#000", + "fgSurface5": "#041708", "stroke1": "#6ea277", "stroke2": "#609369", "stroke3": "#00580f", @@ -2846,7 +2846,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgFillInverted": "#ecf2ec", "fgSurface2": "#0f8e3f", "fgSurface3": "#286d3a", - "fgSurface4": "#246233", + "fgSurface4": "#174623", "fgSurface5": "#041708", "stroke1": "#83be8d", "stroke2": "#71ad7b", @@ -2886,7 +2886,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgFillInverted": "#ecf2ec", "fgSurface2": "#31a352", "fgSurface3": "#328246", - "fgSurface4": "#236032", + "fgSurface4": "#1e542c", "fgSurface5": "#041708", "stroke1": "#96d8a1", "stroke2": "#85c690", @@ -2928,7 +2928,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgFillInverted": "#1f1e1b", "fgSurface2": "#725c00", "fgSurface3": "#887525", - "fgSurface4": "#fadc58", + "fgSurface4": "#fbe37f", "fgSurface5": "#fffef8", "stroke1": "#817854", "stroke2": "#8e8661", @@ -2968,7 +2968,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgFillInverted": "#1f1e1b", "fgSurface2": "#745e00", "fgSurface3": "#8c7926", - "fgSurface4": "#fadc58", + "fgSurface4": "#fbe37f", "fgSurface5": "#fffef8", "stroke1": "#817854", "stroke2": "#8e8661", @@ -3008,7 +3008,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgFillInverted": "#1f1e1b", "fgSurface2": "#7f6703", "fgSurface3": "#9a852b", - "fgSurface4": "#fadd62", + "fgSurface4": "#fbe587", "fgSurface5": "#fffef8", "stroke1": "#817854", "stroke2": "#8e8661", @@ -3048,7 +3048,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgFillInverted": "#1f1e1b", "fgSurface2": "#957a00", "fgSurface3": "#b19a33", - "fgSurface4": "#fbe485", + "fgSurface4": "#fceba5", "fgSurface5": "#fffef8", "stroke1": "#817854", "stroke2": "#8e8661", @@ -3088,7 +3088,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgFillInverted": "#1f1e1b", "fgSurface2": "#b69801", "fgSurface3": "#d9bc40", - "fgSurface4": "#fce895", + "fgSurface4": "#fdeeb3", "fgSurface5": "#fffef8", "stroke1": "#817854", "stroke2": "#8e8661", @@ -3169,7 +3169,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgSurface2": "#776000", "fgSurface3": "#544814", "fgSurface4": "#493e10", - "fgSurface5": "#000", + "fgSurface5": "#181402", "stroke1": "#9d9268", "stroke2": "#8e855b", "stroke3": "#594700", @@ -3208,7 +3208,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgFillInverted": "#faf1c9", "fgSurface2": "#907600", "fgSurface3": "#6b5d1c", - "fgSurface4": "#605318", + "fgSurface4": "#483d0f", "fgSurface5": "#181402", "stroke1": "#b9ad7b", "stroke2": "#a99d6c", @@ -3248,7 +3248,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgFillInverted": "#faf1c9", "fgSurface2": "#aa8c00", "fgSurface3": "#847224", - "fgSurface4": "#625418", + "fgSurface4": "#564a14", "fgSurface5": "#181402", "stroke1": "#d6c88f", "stroke2": "#c4b780", @@ -3290,7 +3290,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgFillInverted": "#1f1e1b", "fgSurface2": "#815600", "fgSurface3": "#947029", - "fgSurface4": "#fad99f", + "fgSurface4": "#fbe0b2", "fgSurface5": "#fffdfb", "stroke1": "#7b6a4c", "stroke2": "#887758", @@ -3330,7 +3330,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgFillInverted": "#1f1e1b", "fgSurface2": "#825700", "fgSurface3": "#99752b", - "fgSurface4": "#fad99f", + "fgSurface4": "#fbe0b2", "fgSurface5": "#fffdfb", "stroke1": "#7b6a4c", "stroke2": "#887758", @@ -3370,7 +3370,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgFillInverted": "#1f1e1b", "fgSurface2": "#8f6003", "fgSurface3": "#a88030", - "fgSurface4": "#fadaa4", + "fgSurface4": "#fbe2b7", "fgSurface5": "#fffdfb", "stroke1": "#7b6a4c", "stroke2": "#887758", @@ -3410,7 +3410,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgFillInverted": "#1f1e1b", "fgSurface2": "#a67100", "fgSurface3": "#c19339", - "fgSurface4": "#fbe1b5", + "fgSurface4": "#fce8c7", "fgSurface5": "#fffdfb", "stroke1": "#7b6a4c", "stroke2": "#887758", @@ -3450,7 +3450,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgFillInverted": "#1f1e1b", "fgSurface2": "#c6900a", "fgSurface3": "#ebb447", - "fgSurface4": "#fbe5be", + "fgSurface4": "#fcecd0", "fgSurface5": "#fffdfb", "stroke1": "#7b6a4c", "stroke2": "#887758", @@ -3531,7 +3531,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgSurface2": "#855900", "fgSurface3": "#5b4416", "fgSurface4": "#4f3b12", - "fgSurface5": "#000", + "fgSurface5": "#1b1203", "stroke1": "#a58e67", "stroke2": "#96805a", "stroke3": "#644100", @@ -3570,7 +3570,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgFillInverted": "#f5efe5", "fgSurface2": "#a26e00", "fgSurface3": "#75581f", - "fgSurface4": "#694f1b", + "fgSurface4": "#4f3a12", "fgSurface5": "#1b1203", "stroke1": "#c3a97b", "stroke2": "#b3996c", @@ -3610,7 +3610,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgFillInverted": "#f5efe5", "fgSurface2": "#ba8500", "fgSurface3": "#906d28", - "fgSurface4": "#6b501b", + "fgSurface4": "#5f4717", "fgSurface5": "#1b1203", "stroke1": "#e2c491", "stroke2": "#d0b381", @@ -3652,7 +3652,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgFillInverted": "#231c1b", "fgSurface2": "#c10008", "fgSurface3": "#e81d1d", - "fgSurface4": "#fdd4cd", + "fgSurface4": "#fedcd7", "fgSurface5": "#fffdfd", "stroke1": "#8c4840", "stroke2": "#9c554d", @@ -3692,7 +3692,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgFillInverted": "#231c1b", "fgSurface2": "#c2000b", "fgSurface3": "#ef1e1e", - "fgSurface4": "#fdd4cd", + "fgSurface4": "#fedcd7", "fgSurface5": "#fffdfd", "stroke1": "#8c4840", "stroke2": "#9c554d", @@ -3732,7 +3732,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgFillInverted": "#231c1b", "fgSurface2": "#ce1b1a", "fgSurface3": "#fa3730", - "fgSurface4": "#fed6d0", + "fgSurface4": "#feded9", "fgSurface5": "#fffdfd", "stroke1": "#8c4840", "stroke2": "#9c554d", @@ -3772,7 +3772,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgFillInverted": "#231c1b", "fgSurface2": "#e3382f", "fgSurface3": "#fb6a5c", - "fgSurface4": "#feddd8", + "fgSurface4": "#fee5e1", "fgSurface5": "#fffdfd", "stroke1": "#8c4840", "stroke2": "#9c554d", @@ -3812,7 +3812,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgFillInverted": "#231c1b", "fgSurface2": "#ff6153", "fgSurface3": "#fca094", - "fgSurface4": "#fee1dd", + "fgSurface4": "#fee9e6", "fgSurface5": "#fffdfd", "stroke1": "#9b5047", "stroke2": "#ab5e54", @@ -3893,7 +3893,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgSurface2": "#c3030d", "fgSurface3": "#8f0d0d", "fgSurface4": "#7e0a0a", - "fgSurface5": "#000", + "fgSurface5": "#260101", "stroke1": "#ce786d", "stroke2": "#be695e", "stroke3": "#930000", @@ -3932,7 +3932,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgFillInverted": "#f2efef", "fgSurface2": "#e1342d", "fgSurface3": "#b81515", - "fgSurface4": "#a61111", + "fgSurface4": "#760909", "fgSurface5": "#260101", "stroke1": "#da9d94", "stroke2": "#c88980", @@ -3972,7 +3972,7 @@ exports[`buildRamps > accent ramp snapshots for defaults 1`] = ` "fgFillInverted": "#f2efef", "fgSurface2": "#fd5145", "fgSurface3": "#e21c1c", - "fgSurface4": "#a11010", + "fgSurface4": "#8e0d0d", "fgSurface5": "#260101", "stroke1": "#e0afa7", "stroke2": "#ce9b93", @@ -4011,7 +4011,7 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "fgFillInverted": "#34120e", "fgSurface2": "#786562", "fgSurface3": "#98807c", - "fgSurface4": "#e4dedd", + "fgSurface4": "#e9e5e4", "fgSurface5": "#fefdfd", "stroke1": "#341612", "stroke2": "#482622", @@ -4044,7 +4044,7 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "fgFillInverted": "#1f1f12", "fgSurface2": "#7f8075", "fgSurface3": "#9fa093", - "fgSurface4": "#e6e7d5", + "fgSurface4": "#eceddb", "fgSurface5": "#fefff2", "stroke1": "#393a24", "stroke2": "#45462f", @@ -4077,7 +4077,7 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "fgFillInverted": "#11230f", "fgSurface2": "#707c6f", "fgSurface3": "#8c9a8a", - "fgSurface4": "#dae8d9", + "fgSurface4": "#e3ede2", "fgSurface5": "#fcfefc", "stroke1": "#21381f", "stroke2": "#2d452b", @@ -4110,7 +4110,7 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "fgFillInverted": "#122121", "fgSurface2": "#737d7d", "fgSurface3": "#909c9c", - "fgSurface4": "#d7e8e8", + "fgSurface4": "#dfeeed", "fgSurface5": "#fbfefe", "stroke1": "#233838", "stroke2": "#2f4545", @@ -4143,7 +4143,7 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "fgFillInverted": "#0e1c3d", "fgSurface2": "#596376", "fgSurface3": "#777f8f", - "fgSurface4": "#dcdee2", + "fgSurface4": "#e3e4e8", "fgSurface5": "#fdfefe", "stroke1": "#061436", "stroke2": "#182951", @@ -4176,7 +4176,7 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "fgFillInverted": "#2e112d", "fgSurface2": "#776775", "fgSurface3": "#968394", - "fgSurface4": "#e5dee4", + "fgSurface4": "#eae5ea", "fgSurface5": "#fefdfe", "stroke1": "#331932", "stroke2": "#462945", @@ -4209,7 +4209,7 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "fgFillInverted": "#321311", "fgSurface2": "#766462", "fgSurface3": "#95807d", - "fgSurface4": "#e3dedd", + "fgSurface4": "#e9e4e4", "fgSurface5": "#fefdfd", "stroke1": "#321715", "stroke2": "#462724", @@ -4242,7 +4242,7 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "fgFillInverted": "#1f1f11", "fgSurface2": "#7b7b72", "fgSurface3": "#999a8e", - "fgSurface4": "#e4e5d4", + "fgSurface4": "#ebecda", "fgSurface5": "#fefff2", "stroke1": "#353522", "stroke2": "#41422e", @@ -4275,7 +4275,7 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "fgFillInverted": "#112310", "fgSurface2": "#6d786c", "fgSurface3": "#8a9789", - "fgSurface4": "#d9e6d8", + "fgSurface4": "#e1ece0", "fgSurface5": "#fcfefc", "stroke1": "#1f331e", "stroke2": "#2c412a", @@ -4308,7 +4308,7 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "fgFillInverted": "#112121", "fgSurface2": "#6f7978", "fgSurface3": "#8c9897", - "fgSurface4": "#d6e6e6", + "fgSurface4": "#deecec", "fgSurface5": "#fbfefe", "stroke1": "#213434", "stroke2": "#2d4141", @@ -4341,7 +4341,7 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "fgFillInverted": "#151b3a", "fgSurface2": "#5f6478", "fgSurface3": "#7d818f", - "fgSurface4": "#dcdee1", + "fgSurface4": "#e3e4e7", "fgSurface5": "#fdfdfe", "stroke1": "#101635", "stroke2": "#21294c", @@ -4374,7 +4374,7 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "fgFillInverted": "#2d122c", "fgSurface2": "#756774", "fgSurface3": "#948293", - "fgSurface4": "#e4dee4", + "fgSurface4": "#eae4e9", "fgSurface5": "#fefdfe", "stroke1": "#311930", "stroke2": "#432942", @@ -4407,7 +4407,7 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "fgFillInverted": "#301514", "fgSurface2": "#746564", "fgSurface3": "#93807f", - "fgSurface4": "#e2dedd", + "fgSurface4": "#e8e4e4", "fgSurface5": "#fefdfd", "stroke1": "#301817", "stroke2": "#432826", @@ -4440,7 +4440,7 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "fgFillInverted": "#1f1f11", "fgSurface2": "#77776f", "fgSurface3": "#95958b", - "fgSurface4": "#e3e4d4", + "fgSurface4": "#e9eada", "fgSurface5": "#fefef3", "stroke1": "#303020", "stroke2": "#3d3e2c", @@ -4473,7 +4473,7 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "fgFillInverted": "#112311", "fgSurface2": "#6b756b", "fgSurface3": "#879386", - "fgSurface4": "#d8e5d7", + "fgSurface4": "#e0ebe0", "fgSurface5": "#fcfefc", "stroke1": "#1d2f1c", "stroke2": "#2a3d29", @@ -4506,7 +4506,7 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "fgFillInverted": "#112121", "fgSurface2": "#6c7574", "fgSurface3": "#889393", - "fgSurface4": "#d5e5e5", + "fgSurface4": "#dcebeb", "fgSurface5": "#fbfefe", "stroke1": "#1f2f2f", "stroke2": "#2c3d3d", @@ -4539,7 +4539,7 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "fgFillInverted": "#181b37", "fgSurface2": "#626575", "fgSurface3": "#7f818e", - "fgSurface4": "#dddee1", + "fgSurface4": "#e3e4e7", "fgSurface5": "#fdfdfe", "stroke1": "#161934", "stroke2": "#272a49", @@ -4572,7 +4572,7 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "fgFillInverted": "#2b142b", "fgSurface2": "#736672", "fgSurface3": "#928291", - "fgSurface4": "#e3dde3", + "fgSurface4": "#e9e4e9", "fgSurface5": "#fefdfe", "stroke1": "#2f1a2e", "stroke2": "#412a40", @@ -4605,7 +4605,7 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "fgFillInverted": "#2d1717", "fgSurface2": "#736666", "fgSurface3": "#918181", - "fgSurface4": "#e2dedd", + "fgSurface4": "#e7e4e4", "fgSurface5": "#fefdfd", "stroke1": "#2e1b1a", "stroke2": "#3f2a29", @@ -4638,7 +4638,7 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "fgFillInverted": "#1f1f12", "fgSurface2": "#72726a", "fgSurface3": "#909087", - "fgSurface4": "#e2e2d4", + "fgSurface4": "#e8e9da", "fgSurface5": "#fefef4", "stroke1": "#2b2c1e", "stroke2": "#39392a", @@ -4671,7 +4671,7 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "fgFillInverted": "#132313", "fgSurface2": "#697168", "fgSurface3": "#848f84", - "fgSurface4": "#d8e4d7", + "fgSurface4": "#e0e9e0", "fgSurface5": "#fcfefc", "stroke1": "#1d2c1c", "stroke2": "#293a29", @@ -4704,7 +4704,7 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "fgFillInverted": "#132121", "fgSurface2": "#6a7171", "fgSurface3": "#868f8f", - "fgSurface4": "#d6e3e3", + "fgSurface4": "#dceaea", "fgSurface5": "#fbfefe", "stroke1": "#1e2b2b", "stroke2": "#2b3939", @@ -4737,7 +4737,7 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "fgFillInverted": "#1b1b33", "fgSurface2": "#656673", "fgSurface3": "#81828d", - "fgSurface4": "#dddee0", + "fgSurface4": "#e4e4e6", "fgSurface5": "#fdfdfe", "stroke1": "#1b1c32", "stroke2": "#2b2c44", @@ -4770,7 +4770,7 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "fgFillInverted": "#291629", "fgSurface2": "#716670", "fgSurface3": "#8f828f", - "fgSurface4": "#e2dde2", + "fgSurface4": "#e8e4e8", "fgSurface5": "#fefdfe", "stroke1": "#2d1b2d", "stroke2": "#3e2a3e", @@ -4803,7 +4803,7 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "fgFillInverted": "#291919", "fgSurface2": "#706767", "fgSurface3": "#8f8483", - "fgSurface4": "#e1dede", + "fgSurface4": "#e7e4e4", "fgSurface5": "#fefdfd", "stroke1": "#2c1e1e", "stroke2": "#3c2d2d", @@ -4836,7 +4836,7 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "fgFillInverted": "#1f1f15", "fgSurface2": "#6f6f69", "fgSurface3": "#8c8c85", - "fgSurface4": "#e1e1d6", + "fgSurface4": "#e7e7dc", "fgSurface5": "#fefef6", "stroke1": "#27271e", "stroke2": "#36362c", @@ -4869,7 +4869,7 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "fgFillInverted": "#162216", "fgSurface2": "#696f69", "fgSurface3": "#848c84", - "fgSurface4": "#d9e2d9", + "fgSurface4": "#e1e8e1", "fgSurface5": "#fdfefc", "stroke1": "#1d281d", "stroke2": "#2b362b", @@ -4902,7 +4902,7 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "fgFillInverted": "#162020", "fgSurface2": "#686e6e", "fgSurface3": "#858c8c", - "fgSurface4": "#d7e2e2", + "fgSurface4": "#dee8e8", "fgSurface5": "#fcfefe", "stroke1": "#1e2727", "stroke2": "#2b3535", @@ -4935,7 +4935,7 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "fgFillInverted": "#1c1c2d", "fgSurface2": "#676770", "fgSurface3": "#84848b", - "fgSurface4": "#dedee0", + "fgSurface4": "#e5e5e6", "fgSurface5": "#fdfdfe", "stroke1": "#1f1e2e", "stroke2": "#2d2d3f", @@ -4968,7 +4968,7 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "fgFillInverted": "#261926", "fgSurface2": "#6f676f", "fgSurface3": "#8d838c", - "fgSurface4": "#e1dee1", + "fgSurface4": "#e7e4e7", "fgSurface5": "#fefdfe", "stroke1": "#2a1e2a", "stroke2": "#3b2d3b", @@ -5001,7 +5001,7 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "fgFillInverted": "#1e1e1e", "fgSurface2": "#6c6c6c", "fgSurface3": "#898989", - "fgSurface4": "#dfdfdf", + "fgSurface4": "#e5e5e5", "fgSurface5": "#fdfdfd", "stroke1": "#242424", "stroke2": "#323232", @@ -5034,7 +5034,7 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "fgFillInverted": "#1e1e1e", "fgSurface2": "#6c6c6c", "fgSurface3": "#898989", - "fgSurface4": "#dfdfdf", + "fgSurface4": "#e5e5e5", "fgSurface5": "#fdfdfd", "stroke1": "#242424", "stroke2": "#323232", @@ -5067,7 +5067,7 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "fgFillInverted": "#1e1e1e", "fgSurface2": "#6c6c6c", "fgSurface3": "#898989", - "fgSurface4": "#dfdfdf", + "fgSurface4": "#e5e5e5", "fgSurface5": "#fdfdfd", "stroke1": "#242424", "stroke2": "#323232", @@ -5100,7 +5100,7 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "fgFillInverted": "#1e1e1e", "fgSurface2": "#6c6c6c", "fgSurface3": "#898989", - "fgSurface4": "#dfdfdf", + "fgSurface4": "#e5e5e5", "fgSurface5": "#fdfdfd", "stroke1": "#242424", "stroke2": "#323232", @@ -5133,7 +5133,7 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "fgFillInverted": "#1e1e1e", "fgSurface2": "#6c6c6c", "fgSurface3": "#898989", - "fgSurface4": "#dfdfdf", + "fgSurface4": "#e5e5e5", "fgSurface5": "#fdfdfd", "stroke1": "#242424", "stroke2": "#323232", @@ -5166,7 +5166,7 @@ exports[`buildRamps > background ramp snapshots at lightness 10 1`] = ` "fgFillInverted": "#1e1e1e", "fgSurface2": "#6c6c6c", "fgSurface3": "#898989", - "fgSurface4": "#dfdfdf", + "fgSurface4": "#e5e5e5", "fgSurface5": "#fdfdfd", "stroke1": "#242424", "stroke2": "#323232", @@ -5204,8 +5204,8 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "fgFillInverted": "#2f1612", "fgSurface2": "#917a76", "fgSurface3": "#ae9a97", - "fgSurface4": "#ebe7e6", - "fgSurface5": "#fefefd", + "fgSurface4": "#f0edec", + "fgSurface5": "#fefdfd", "stroke1": "#572a23", "stroke2": "#673730", "stroke3": "#c06559", @@ -5237,7 +5237,7 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "fgFillInverted": "#1e1e1b", "fgSurface2": "#aaab9d", "fgSurface3": "#cfd0c0", - "fgSurface4": "#e8e9d7", + "fgSurface4": "#eeefdd", "fgSurface5": "#fefff2", "stroke1": "#5c5d3d", "stroke2": "#696a49", @@ -5270,7 +5270,7 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "fgFillInverted": "#1b1f1b", "fgSurface2": "#9fae9d", "fgSurface3": "#c0d2be", - "fgSurface4": "#dfebde", + "fgSurface4": "#e8f0e7", "fgSurface5": "#fcfefc", "stroke1": "#3f643c", "stroke2": "#4b7147", @@ -5303,7 +5303,7 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "fgFillInverted": "#1c1f1f", "fgSurface2": "#9fadac", "fgSurface3": "#c1d2d1", - "fgSurface4": "#daebeb", + "fgSurface4": "#e4f0f0", "fgSurface5": "#fbfefe", "stroke1": "#406160", "stroke2": "#4c6e6d", @@ -5336,7 +5336,7 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "fgFillInverted": "#05184c", "fgSurface2": "#646d80", "fgSurface3": "#828a98", - "fgSurface4": "#dfe1e5", + "fgSurface4": "#e6e7ea", "fgSurface5": "#fdfefe", "stroke1": "#0d204d", "stroke2": "#1c3364", @@ -5369,8 +5369,8 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "fgFillInverted": "#261925", "fgSurface2": "#958293", "fgSurface3": "#b6a2b5", - "fgSurface4": "#ebe6eb", - "fgSurface5": "#fefdfe", + "fgSurface4": "#f1edf0", + "fgSurface5": "#fefefe", "stroke1": "#582f56", "stroke2": "#673c65", "stroke3": "#bb6bb8", @@ -5402,7 +5402,7 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "fgFillInverted": "#301512", "fgSurface2": "#8c7673", "fgSurface3": "#a99693", - "fgSurface4": "#e9e4e4", + "fgSurface4": "#efebea", "fgSurface5": "#fefdfd", "stroke1": "#522823", "stroke2": "#613430", @@ -5435,7 +5435,7 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "fgFillInverted": "#1e1e1b", "fgSurface2": "#abac9e", "fgSurface3": "#d0d1c1", - "fgSurface4": "#e8e9d7", + "fgSurface4": "#eeefdd", "fgSurface5": "#fefff2", "stroke1": "#5c5d3d", "stroke2": "#696a49", @@ -5468,7 +5468,7 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "fgFillInverted": "#1b1f1b", "fgSurface2": "#98a797", "fgSurface3": "#bacbb8", - "fgSurface4": "#dfebde", + "fgSurface4": "#e8f0e7", "fgSurface5": "#fcfefc", "stroke1": "#3c5e39", "stroke2": "#486b45", @@ -5501,7 +5501,7 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "fgFillInverted": "#1c1f1f", "fgSurface2": "#9daaaa", "fgSurface3": "#c0d0d0", - "fgSurface4": "#dbebea", + "fgSurface4": "#e4f0f0", "fgSurface5": "#fbfefe", "stroke1": "#3f5f5e", "stroke2": "#4b6c6b", @@ -5534,7 +5534,7 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "fgFillInverted": "#0e1848", "fgSurface2": "#666d7f", "fgSurface3": "#858a98", - "fgSurface4": "#dfe0e4", + "fgSurface4": "#e6e7ea", "fgSurface5": "#fdfefe", "stroke1": "#16214b", "stroke2": "#253260", @@ -5567,8 +5567,8 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "fgFillInverted": "#281827", "fgSurface2": "#907e8e", "fgSurface3": "#b19cb0", - "fgSurface4": "#ebe6eb", - "fgSurface5": "#fefdfe", + "fgSurface4": "#f1edf0", + "fgSurface5": "#fefefe", "stroke1": "#532d51", "stroke2": "#613960", "stroke3": "#b369b1", @@ -5600,7 +5600,7 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "fgFillInverted": "#2f1514", "fgSurface2": "#897674", "fgSurface3": "#a79492", - "fgSurface4": "#e8e3e3", + "fgSurface4": "#edeae9", "fgSurface5": "#fefdfd", "stroke1": "#4d2825", "stroke2": "#5c3532", @@ -5633,8 +5633,8 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "fgFillInverted": "#1e1e1b", "fgSurface2": "#a4a498", "fgSurface3": "#c8c9ba", - "fgSurface4": "#e8e9d8", - "fgSurface5": "#fefff2", + "fgSurface4": "#eeefde", + "fgSurface5": "#fefef2", "stroke1": "#57583b", "stroke2": "#636446", "stroke3": "#a5a773", @@ -5666,8 +5666,8 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "fgFillInverted": "#1a201a", "fgSurface2": "#8f9c8d", "fgSurface3": "#b0c0ae", - "fgSurface4": "#e0ebdf", - "fgSurface5": "#fcfffc", + "fgSurface4": "#e8f0e7", + "fgSurface5": "#fcfefc", "stroke1": "#375535", "stroke2": "#426140", "stroke3": "#70a66d", @@ -5699,7 +5699,7 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "fgFillInverted": "#1b1f1f", "fgSurface2": "#939e9e", "fgSurface3": "#b4c2c2", - "fgSurface4": "#dbebea", + "fgSurface4": "#e4f0f0", "fgSurface5": "#fbfefe", "stroke1": "#395555", "stroke2": "#456262", @@ -5732,7 +5732,7 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "fgFillInverted": "#151842", "fgSurface2": "#6a6e7e", "fgSurface3": "#888b98", - "fgSurface4": "#dfe0e4", + "fgSurface4": "#e6e7e9", "fgSurface5": "#fdfdfe", "stroke1": "#1e234a", "stroke2": "#2c335d", @@ -5765,7 +5765,7 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "fgFillInverted": "#281728", "fgSurface2": "#8a7a89", "fgSurface3": "#ab98aa", - "fgSurface4": "#eae5e9", + "fgSurface4": "#efebef", "fgSurface5": "#fefdfe", "stroke1": "#4c2b4b", "stroke2": "#5b385a", @@ -5798,7 +5798,7 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "fgFillInverted": "#2d1717", "fgSurface2": "#867675", "fgSurface3": "#a49494", - "fgSurface4": "#e7e3e3", + "fgSurface4": "#eceae9", "fgSurface5": "#fefdfd", "stroke1": "#492a2a", "stroke2": "#583736", @@ -5831,8 +5831,8 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "fgFillInverted": "#1e1e1a", "fgSurface2": "#97988d", "fgSurface3": "#babaae", - "fgSurface4": "#e8e9d9", - "fgSurface5": "#fefef4", + "fgSurface4": "#eeefdf", + "fgSurface5": "#fefef3", "stroke1": "#4d4e37", "stroke2": "#595a42", "stroke3": "#999a6f", @@ -5864,7 +5864,7 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "fgFillInverted": "#192019", "fgSurface2": "#879286", "fgSurface3": "#a6b3a5", - "fgSurface4": "#e1eae0", + "fgSurface4": "#e9f0e8", "fgSurface5": "#fcfefc", "stroke1": "#334c32", "stroke2": "#3f593e", @@ -5897,7 +5897,7 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "fgFillInverted": "#1a1f1f", "fgSurface2": "#8a9493", "fgSurface3": "#a9b5b5", - "fgSurface4": "#dceaea", + "fgSurface4": "#e5f0f0", "fgSurface5": "#fbfefe", "stroke1": "#364c4c", "stroke2": "#425959", @@ -5930,7 +5930,7 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "fgFillInverted": "#191a39", "fgSurface2": "#717280", "fgSurface3": "#8f909a", - "fgSurface4": "#e1e1e4", + "fgSurface4": "#e7e8ea", "fgSurface5": "#fdfdfe", "stroke1": "#27284a", "stroke2": "#35365b", @@ -5963,7 +5963,7 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "fgFillInverted": "#281727", "fgSurface2": "#877986", "fgSurface3": "#a898a7", - "fgSurface4": "#e8e4e8", + "fgSurface4": "#eeeaee", "fgSurface5": "#fefdfe", "stroke1": "#482d48", "stroke2": "#573a56", @@ -5996,7 +5996,7 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "fgFillInverted": "#281a1a", "fgSurface2": "#857a79", "fgSurface3": "#a39999", - "fgSurface4": "#e7e4e4", + "fgSurface4": "#eceaea", "fgSurface5": "#fefdfd", "stroke1": "#463030", "stroke2": "#553d3d", @@ -6029,7 +6029,7 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "fgFillInverted": "#1e1e19", "fgSurface2": "#8c8c85", "fgSurface3": "#adada4", - "fgSurface4": "#e8e8dd", + "fgSurface4": "#eeefe3", "fgSurface5": "#fefef6", "stroke1": "#444434", "stroke2": "#505040", @@ -6062,7 +6062,7 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "fgFillInverted": "#192019", "fgSurface2": "#818981", "fgSurface3": "#a0aaa0", - "fgSurface4": "#e2eae2", + "fgSurface4": "#eaefea", "fgSurface5": "#fcfefc", "stroke1": "#324432", "stroke2": "#3d503d", @@ -6095,7 +6095,7 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "fgFillInverted": "#191f1f", "fgSurface2": "#838a8a", "fgSurface3": "#a3abab", - "fgSurface4": "#dfeaea", + "fgSurface4": "#e7f0ef", "fgSurface5": "#fcfefe", "stroke1": "#344444", "stroke2": "#3f5050", @@ -6128,7 +6128,7 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "fgFillInverted": "#1c1c2e", "fgSurface2": "#787881", "fgSurface3": "#96979d", - "fgSurface4": "#e3e3e5", + "fgSurface4": "#eaeaeb", "fgSurface5": "#fdfdfe", "stroke1": "#303049", "stroke2": "#3d3d58", @@ -6161,7 +6161,7 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "fgFillInverted": "#251925", "fgSurface2": "#857b85", "fgSurface3": "#a599a5", - "fgSurface4": "#e7e4e7", + "fgSurface4": "#edebed", "fgSurface5": "#fefdfe", "stroke1": "#443044", "stroke2": "#523d52", @@ -6194,7 +6194,7 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "fgFillInverted": "#1e1e1e", "fgSurface2": "#828282", "fgSurface3": "#a2a2a2", - "fgSurface4": "#e6e6e6", + "fgSurface4": "#ededed", "fgSurface5": "#fdfdfd", "stroke1": "#3a3a3a", "stroke2": "#474747", @@ -6227,7 +6227,7 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "fgFillInverted": "#1e1e1e", "fgSurface2": "#828282", "fgSurface3": "#a2a2a2", - "fgSurface4": "#e6e6e6", + "fgSurface4": "#ededed", "fgSurface5": "#fdfdfd", "stroke1": "#3a3a3a", "stroke2": "#474747", @@ -6260,7 +6260,7 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "fgFillInverted": "#1e1e1e", "fgSurface2": "#828282", "fgSurface3": "#a2a2a2", - "fgSurface4": "#e6e6e6", + "fgSurface4": "#ededed", "fgSurface5": "#fdfdfd", "stroke1": "#3a3a3a", "stroke2": "#474747", @@ -6293,7 +6293,7 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "fgFillInverted": "#1e1e1e", "fgSurface2": "#828282", "fgSurface3": "#a2a2a2", - "fgSurface4": "#e6e6e6", + "fgSurface4": "#ededed", "fgSurface5": "#fdfdfd", "stroke1": "#3a3a3a", "stroke2": "#474747", @@ -6326,7 +6326,7 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "fgFillInverted": "#1e1e1e", "fgSurface2": "#828282", "fgSurface3": "#a2a2a2", - "fgSurface4": "#e6e6e6", + "fgSurface4": "#ededed", "fgSurface5": "#fdfdfd", "stroke1": "#3a3a3a", "stroke2": "#474747", @@ -6359,7 +6359,7 @@ exports[`buildRamps > background ramp snapshots at lightness 20 1`] = ` "fgFillInverted": "#1e1e1e", "fgSurface2": "#828282", "fgSurface3": "#a2a2a2", - "fgSurface4": "#e6e6e6", + "fgSurface4": "#ededed", "fgSurface5": "#fdfdfd", "stroke1": "#3a3a3a", "stroke2": "#474747", @@ -6397,8 +6397,8 @@ exports[`buildRamps > background ramp snapshots at lightness 30 1`] = ` "fgFillInverted": "#241c1b", "fgSurface2": "#af9b97", "fgSurface3": "#ccc0be", - "fgSurface4": "#ebe7e6", - "fgSurface5": "#fefefd", + "fgSurface4": "#f1edec", + "fgSurface5": "#fefdfd", "stroke1": "#7d3e36", "stroke2": "#8d4b43", "stroke3": "#d88c80", @@ -6529,7 +6529,7 @@ exports[`buildRamps > background ramp snapshots at lightness 30 1`] = ` "fgFillInverted": "#0b1b42", "fgSurface2": "#747d8e", "fgSurface3": "#959ca8", - "fgSurface4": "#e5e6ea", + "fgSurface4": "#ecedef", "fgSurface5": "#fdfefe", "stroke1": "#152e6a", "stroke2": "#223e7e", @@ -6562,8 +6562,8 @@ exports[`buildRamps > background ramp snapshots at lightness 30 1`] = ` "fgFillInverted": "#221c21", "fgSurface2": "#bca8bb", "fgSurface3": "#dbd1da", - "fgSurface4": "#ebe6eb", - "fgSurface5": "#fefdfe", + "fgSurface4": "#f1edf0", + "fgSurface5": "#fefefe", "stroke1": "#7f467d", "stroke2": "#8f548d", "stroke3": "#e094dd", @@ -6595,8 +6595,8 @@ exports[`buildRamps > background ramp snapshots at lightness 30 1`] = ` "fgFillInverted": "#241c1a", "fgSurface2": "#a8928f", "fgSurface3": "#c4b7b5", - "fgSurface4": "#ebe7e6", - "fgSurface5": "#fefefd", + "fgSurface4": "#f0eded", + "fgSurface5": "#fefdfd", "stroke1": "#733a34", "stroke2": "#834741", "stroke3": "#d28277", @@ -6727,7 +6727,7 @@ exports[`buildRamps > background ramp snapshots at lightness 30 1`] = ` "fgFillInverted": "#0f1a41", "fgSurface2": "#747b8c", "fgSurface3": "#9499a6", - "fgSurface4": "#e4e5e8", + "fgSurface4": "#eaebee", "fgSurface5": "#fdfefe", "stroke1": "#1d2d65", "stroke2": "#2b3d79", @@ -6760,8 +6760,8 @@ exports[`buildRamps > background ramp snapshots at lightness 30 1`] = ` "fgFillInverted": "#221c21", "fgSurface2": "#b59fb3", "fgSurface3": "#d3c7d2", - "fgSurface4": "#ebe6eb", - "fgSurface5": "#fefdfe", + "fgSurface4": "#f1edf0", + "fgSurface5": "#fefefe", "stroke1": "#764274", "stroke2": "#854f83", "stroke3": "#dc89d9", @@ -6793,8 +6793,8 @@ exports[`buildRamps > background ramp snapshots at lightness 30 1`] = ` "fgFillInverted": "#251b1a", "fgSurface2": "#a38e8b", "fgSurface3": "#bfb2b1", - "fgSurface4": "#ebe7e6", - "fgSurface5": "#fefefd", + "fgSurface4": "#f0eded", + "fgSurface5": "#fefdfd", "stroke1": "#6c3936", "stroke2": "#7b4542", "stroke3": "#cd7e78", @@ -6859,7 +6859,7 @@ exports[`buildRamps > background ramp snapshots at lightness 30 1`] = ` "fgFillInverted": "#1b1f1b", "fgSurface2": "#9fae9d", "fgSurface3": "#c1d4bf", - "fgSurface4": "#dfebde", + "fgSurface4": "#e8f0e7", "fgSurface5": "#fcfefc", "stroke1": "#3f643c", "stroke2": "#4b7147", @@ -6892,7 +6892,7 @@ exports[`buildRamps > background ramp snapshots at lightness 30 1`] = ` "fgFillInverted": "#1c1f1f", "fgSurface2": "#9fadac", "fgSurface3": "#c1d1d1", - "fgSurface4": "#daebeb", + "fgSurface4": "#e4f0f0", "fgSurface5": "#fafefe", "stroke1": "#406161", "stroke2": "#4c6e6e", @@ -6925,7 +6925,7 @@ exports[`buildRamps > background ramp snapshots at lightness 30 1`] = ` "fgFillInverted": "#151b3a", "fgSurface2": "#787c8c", "fgSurface3": "#989ba7", - "fgSurface4": "#e4e5e8", + "fgSurface4": "#ebebee", "fgSurface5": "#fdfdfe", "stroke1": "#283064", "stroke2": "#353f76", @@ -6958,8 +6958,8 @@ exports[`buildRamps > background ramp snapshots at lightness 30 1`] = ` "fgFillInverted": "#211c21", "fgSurface2": "#aa97a9", "fgSurface3": "#cabcc9", - "fgSurface4": "#ebe6eb", - "fgSurface5": "#fefdfe", + "fgSurface4": "#f1edf0", + "fgSurface5": "#fefefe", "stroke1": "#6d3f6b", "stroke2": "#7c4c7a", "stroke3": "#d181cf", @@ -6991,8 +6991,8 @@ exports[`buildRamps > background ramp snapshots at lightness 30 1`] = ` "fgFillInverted": "#241c1b", "fgSurface2": "#9f8e8c", "fgSurface3": "#bcb1b0", - "fgSurface4": "#eae7e7", - "fgSurface5": "#fefefd", + "fgSurface4": "#f0eded", + "fgSurface5": "#fefdfd", "stroke1": "#673c3b", "stroke2": "#754846", "stroke3": "#c3807d", @@ -7024,8 +7024,8 @@ exports[`buildRamps > background ramp snapshots at lightness 30 1`] = ` "fgFillInverted": "#1e1e1b", "fgSurface2": "#aaab9e", "fgSurface3": "#cfd0c1", - "fgSurface4": "#e8e9d8", - "fgSurface5": "#fefff2", + "fgSurface4": "#eeefde", + "fgSurface5": "#fefef2", "stroke1": "#5c5d3f", "stroke2": "#68694a", "stroke3": "#acad79", @@ -7057,7 +7057,7 @@ exports[`buildRamps > background ramp snapshots at lightness 30 1`] = ` "fgFillInverted": "#1c1f1c", "fgSurface2": "#a0ae9f", "fgSurface3": "#c2d3c1", - "fgSurface4": "#e0eadf", + "fgSurface4": "#e8f0e8", "fgSurface5": "#fcfefc", "stroke1": "#426341", "stroke2": "#4e704d", @@ -7090,7 +7090,7 @@ exports[`buildRamps > background ramp snapshots at lightness 30 1`] = ` "fgFillInverted": "#1c1f1f", "fgSurface2": "#a0acac", "fgSurface3": "#c3d2d2", - "fgSurface4": "#dbebea", + "fgSurface4": "#e5f0f0", "fgSurface5": "#fbfefe", "stroke1": "#426060", "stroke2": "#4e6d6d", @@ -7123,8 +7123,8 @@ exports[`buildRamps > background ramp snapshots at lightness 30 1`] = ` "fgFillInverted": "#1a1c2f", "fgSurface2": "#82838f", "fgSurface3": "#a2a3ac", - "fgSurface4": "#e7e7ea", - "fgSurface5": "#fdfdfe", + "fgSurface4": "#edeeef", + "fgSurface5": "#fefefe", "stroke1": "#363865", "stroke2": "#424675", "stroke3": "#7b81b1", @@ -7156,7 +7156,7 @@ exports[`buildRamps > background ramp snapshots at lightness 30 1`] = ` "fgFillInverted": "#211c21", "fgSurface2": "#a493a3", "fgSurface3": "#c4b7c3", - "fgSurface4": "#ebe6ea", + "fgSurface4": "#f0edf0", "fgSurface5": "#fefdfe", "stroke1": "#664066", "stroke2": "#754c74", @@ -7189,7 +7189,7 @@ exports[`buildRamps > background ramp snapshots at lightness 30 1`] = ` "fgFillInverted": "#221d1c", "fgSurface2": "#a09494", "fgSurface3": "#beb6b6", - "fgSurface4": "#e9e7e7", + "fgSurface4": "#efeded", "fgSurface5": "#fefdfd", "stroke1": "#634444", "stroke2": "#715050", @@ -7222,8 +7222,8 @@ exports[`buildRamps > background ramp snapshots at lightness 30 1`] = ` "fgFillInverted": "#1e1e1c", "fgSurface2": "#aaaba1", "fgSurface3": "#d0d1c5", - "fgSurface4": "#e8e9dc", - "fgSurface5": "#fefff6", + "fgSurface4": "#eeefe2", + "fgSurface5": "#fefef5", "stroke1": "#5c5c46", "stroke2": "#696952", "stroke3": "#adad87", @@ -7255,7 +7255,7 @@ exports[`buildRamps > background ramp snapshots at lightness 30 1`] = ` "fgFillInverted": "#1c1f1c", "fgSurface2": "#a4aea3", "fgSurface3": "#c7d4c7", - "fgSurface4": "#e2eae2", + "fgSurface4": "#eaefea", "fgSurface5": "#fcfefc", "stroke1": "#496149", "stroke2": "#566f56", @@ -7288,7 +7288,7 @@ exports[`buildRamps > background ramp snapshots at lightness 30 1`] = ` "fgFillInverted": "#1c1f1f", "fgSurface2": "#a2acab", "fgSurface3": "#c6d1d1", - "fgSurface4": "#dfeaea", + "fgSurface4": "#e7f0ef", "fgSurface5": "#fbfefe", "stroke1": "#495f5f", "stroke2": "#556c6c", @@ -7321,8 +7321,8 @@ exports[`buildRamps > background ramp snapshots at lightness 30 1`] = ` "fgFillInverted": "#1d1d25", "fgSurface2": "#8f9097", "fgSurface3": "#b0b1b6", - "fgSurface4": "#e7e7e9", - "fgSurface5": "#fdfdfe", + "fgSurface4": "#eeeeef", + "fgSurface5": "#fdfefe", "stroke1": "#446", "stroke2": "#505074", "stroke3": "#8d8eac", @@ -7354,8 +7354,8 @@ exports[`buildRamps > background ramp snapshots at lightness 30 1`] = ` "fgFillInverted": "#211d20", "fgSurface2": "#a296a2", "fgSurface3": "#c2bac2", - "fgSurface4": "#eae7ea", - "fgSurface5": "#fefefe", + "fgSurface4": "#efedef", + "fgSurface5": "#fefdfe", "stroke1": "#614661", "stroke2": "#6f526e", "stroke3": "#ba8aba", @@ -7387,7 +7387,7 @@ exports[`buildRamps > background ramp snapshots at lightness 30 1`] = ` "fgFillInverted": "#1e1e1e", "fgSurface2": "#a1a1a1", "fgSurface3": "#c5c5c5", - "fgSurface4": "#e8e8e8", + "fgSurface4": "#eee", "fgSurface5": "#fefefe", "stroke1": "#545454", "stroke2": "#606060", @@ -7420,7 +7420,7 @@ exports[`buildRamps > background ramp snapshots at lightness 30 1`] = ` "fgFillInverted": "#1e1e1e", "fgSurface2": "#a1a1a1", "fgSurface3": "#c5c5c5", - "fgSurface4": "#e8e8e8", + "fgSurface4": "#eee", "fgSurface5": "#fefefe", "stroke1": "#545454", "stroke2": "#606060", @@ -7453,7 +7453,7 @@ exports[`buildRamps > background ramp snapshots at lightness 30 1`] = ` "fgFillInverted": "#1e1e1e", "fgSurface2": "#a1a1a1", "fgSurface3": "#c5c5c5", - "fgSurface4": "#e8e8e8", + "fgSurface4": "#eee", "fgSurface5": "#fefefe", "stroke1": "#545454", "stroke2": "#606060", @@ -7486,7 +7486,7 @@ exports[`buildRamps > background ramp snapshots at lightness 30 1`] = ` "fgFillInverted": "#1e1e1e", "fgSurface2": "#a1a1a1", "fgSurface3": "#c5c5c5", - "fgSurface4": "#e8e8e8", + "fgSurface4": "#eee", "fgSurface5": "#fefefe", "stroke1": "#545454", "stroke2": "#606060", @@ -7519,7 +7519,7 @@ exports[`buildRamps > background ramp snapshots at lightness 30 1`] = ` "fgFillInverted": "#1e1e1e", "fgSurface2": "#a1a1a1", "fgSurface3": "#c5c5c5", - "fgSurface4": "#e8e8e8", + "fgSurface4": "#eee", "fgSurface5": "#fefefe", "stroke1": "#545454", "stroke2": "#606060", @@ -7552,7 +7552,7 @@ exports[`buildRamps > background ramp snapshots at lightness 30 1`] = ` "fgFillInverted": "#1e1e1e", "fgSurface2": "#a1a1a1", "fgSurface3": "#c5c5c5", - "fgSurface4": "#e8e8e8", + "fgSurface4": "#eee", "fgSurface5": "#fefefe", "stroke1": "#545454", "stroke2": "#606060", @@ -7590,8 +7590,8 @@ exports[`buildRamps > background ramp snapshots at lightness 40 1`] = ` "fgFillInverted": "#241c1b", "fgSurface2": "#baa9a6", "fgSurface3": "#d9d1cf", - "fgSurface4": "#ebe7e6", - "fgSurface5": "#fefefd", + "fgSurface4": "#f1edec", + "fgSurface5": "#fefdfd", "stroke1": "#8c473d", "stroke2": "#9d554a", "stroke3": "#de9d93", @@ -7624,7 +7624,7 @@ exports[`buildRamps > background ramp snapshots at lightness 40 1`] = ` "fgSurface2": "#68685f", "fgSurface3": "#4d4d46", "fgSurface4": "#44443e", - "fgSurface5": "#000", + "fgSurface5": "#141412", "stroke1": "#bdbf81", "stroke2": "#acae72", "stroke3": "#686944", @@ -7722,7 +7722,7 @@ exports[`buildRamps > background ramp snapshots at lightness 40 1`] = ` "fgFillInverted": "#161e2e", "fgSurface2": "#8991a0", "fgSurface3": "#adb2bc", - "fgSurface4": "#e6e8eb", + "fgSurface4": "#edeef0", "fgSurface5": "#fdfefe", "stroke1": "#1b3d8f", "stroke2": "#264a9d", @@ -7755,8 +7755,8 @@ exports[`buildRamps > background ramp snapshots at lightness 40 1`] = ` "fgFillInverted": "#221c21", "fgSurface2": "#bca8bb", "fgSurface3": "#dbd1da", - "fgSurface4": "#ebe6eb", - "fgSurface5": "#fefdfe", + "fgSurface4": "#f1edf0", + "fgSurface5": "#fefefe", "stroke1": "#7f477e", "stroke2": "#8f548d", "stroke3": "#e094dd", @@ -7788,8 +7788,8 @@ exports[`buildRamps > background ramp snapshots at lightness 40 1`] = ` "fgFillInverted": "#241c1b", "fgSurface2": "#bbaaa7", "fgSurface3": "#dad2d1", - "fgSurface4": "#ebe7e6", - "fgSurface5": "#fefefd", + "fgSurface4": "#f1eded", + "fgSurface5": "#fefdfd", "stroke1": "#8c473e", "stroke2": "#9c544b", "stroke3": "#df9e95", @@ -7920,7 +7920,7 @@ exports[`buildRamps > background ramp snapshots at lightness 40 1`] = ` "fgFillInverted": "#161d30", "fgSurface2": "#868d9b", "fgSurface3": "#a9aeb8", - "fgSurface4": "#e6e8ea", + "fgSurface4": "#edeef0", "fgSurface5": "#fdfefe", "stroke1": "#253c82", "stroke2": "#324b94", @@ -7953,8 +7953,8 @@ exports[`buildRamps > background ramp snapshots at lightness 40 1`] = ` "fgFillInverted": "#221c21", "fgSurface2": "#bca8bb", "fgSurface3": "#dbd2db", - "fgSurface4": "#ece7ec", - "fgSurface5": "#fffeff", + "fgSurface4": "#f1edf0", + "fgSurface5": "#fefefe", "stroke1": "#80477e", "stroke2": "#8f548d", "stroke3": "#e195dd", @@ -7986,8 +7986,8 @@ exports[`buildRamps > background ramp snapshots at lightness 40 1`] = ` "fgFillInverted": "#231c1b", "fgSurface2": "#b8a9a7", "fgSurface3": "#d8d1d0", - "fgSurface4": "#ebe7e6", - "fgSurface5": "#fefefd", + "fgSurface4": "#f0eded", + "fgSurface5": "#fefdfd", "stroke1": "#894944", "stroke2": "#985550", "stroke3": "#da9e98", @@ -8118,7 +8118,7 @@ exports[`buildRamps > background ramp snapshots at lightness 40 1`] = ` "fgFillInverted": "#191d2c", "fgSurface2": "#8b8f9c", "fgSurface3": "#aeb0b9", - "fgSurface4": "#e7e8ea", + "fgSurface4": "#edeef0", "fgSurface5": "#fdfefe", "stroke1": "#333f7f", "stroke2": "#404d90", @@ -8151,8 +8151,8 @@ exports[`buildRamps > background ramp snapshots at lightness 40 1`] = ` "fgFillInverted": "#221c21", "fgSurface2": "#bca8ba", "fgSurface3": "#dad1da", - "fgSurface4": "#ebe6eb", - "fgSurface5": "#fefdfe", + "fgSurface4": "#f1edf0", + "fgSurface5": "#fefefe", "stroke1": "#7e477d", "stroke2": "#8e548c", "stroke3": "#df95dc", @@ -8184,8 +8184,8 @@ exports[`buildRamps > background ramp snapshots at lightness 40 1`] = ` "fgFillInverted": "#221c1c", "fgSurface2": "#b4a7a6", "fgSurface3": "#d5cecd", - "fgSurface4": "#eae7e7", - "fgSurface5": "#fefefd", + "fgSurface4": "#f0eded", + "fgSurface5": "#fefdfd", "stroke1": "#824c4b", "stroke2": "#915957", "stroke3": "#d19e9b", @@ -8316,8 +8316,8 @@ exports[`buildRamps > background ramp snapshots at lightness 40 1`] = ` "fgFillInverted": "#1c1d25", "fgSurface2": "#9799a3", "fgSurface3": "#bbbcc3", - "fgSurface4": "#e7e7ea", - "fgSurface5": "#fdfdfe", + "fgSurface4": "#edeeef", + "fgSurface5": "#fefefe", "stroke1": "#454982", "stroke2": "#515691", "stroke3": "#9297bf", @@ -8349,7 +8349,7 @@ exports[`buildRamps > background ramp snapshots at lightness 40 1`] = ` "fgFillInverted": "#211c21", "fgSurface2": "#b7a6b6", "fgSurface3": "#d7ced6", - "fgSurface4": "#ebe6eb", + "fgSurface4": "#f0edf0", "fgSurface5": "#fefdfe", "stroke1": "#794b79", "stroke2": "#885787", @@ -8382,8 +8382,8 @@ exports[`buildRamps > background ramp snapshots at lightness 40 1`] = ` "fgFillInverted": "#211d1d", "fgSurface2": "#b2a8a8", "fgSurface3": "#d2cdcd", - "fgSurface4": "#eae7e7", - "fgSurface5": "#fefefe", + "fgSurface4": "#efeded", + "fgSurface5": "#fefdfd", "stroke1": "#775151", "stroke2": "#855e5d", "stroke3": "#c6a1a1", @@ -8448,7 +8448,7 @@ exports[`buildRamps > background ramp snapshots at lightness 40 1`] = ` "fgFillInverted": "#1c1f1c", "fgSurface2": "#a1ada1", "fgSurface3": "#c5d3c5", - "fgSurface4": "#e1eae1", + "fgSurface4": "#e9f0e9", "fgSurface5": "#fcfefc", "stroke1": "#466246", "stroke2": "#526f52", @@ -8481,7 +8481,7 @@ exports[`buildRamps > background ramp snapshots at lightness 40 1`] = ` "fgFillInverted": "#1c1f1f", "fgSurface2": "#a1acac", "fgSurface3": "#c4d0d0", - "fgSurface4": "#ddeaea", + "fgSurface4": "#e6f0f0", "fgSurface5": "#fbfefe", "stroke1": "#465f5f", "stroke2": "#526c6c", @@ -8514,8 +8514,8 @@ exports[`buildRamps > background ramp snapshots at lightness 40 1`] = ` "fgFillInverted": "#1d1e22", "fgSurface2": "#a9aab0", "fgSurface3": "#cdced1", - "fgSurface4": "#e7e7e9", - "fgSurface5": "#fdfdfe", + "fgSurface4": "#eeeeef", + "fgSurface5": "#fdfefe", "stroke1": "#575782", "stroke2": "#63638f", "stroke3": "#a7a8c0", @@ -8547,8 +8547,8 @@ exports[`buildRamps > background ramp snapshots at lightness 40 1`] = ` "fgFillInverted": "#201d20", "fgSurface2": "#b3a7b3", "fgSurface3": "#d4ced4", - "fgSurface4": "#eae7ea", - "fgSurface5": "#fefefe", + "fgSurface4": "#f0edef", + "fgSurface5": "#fefdfe", "stroke1": "#715071", "stroke2": "#7f5d7f", "stroke3": "#ca9cca", @@ -8580,7 +8580,7 @@ exports[`buildRamps > background ramp snapshots at lightness 40 1`] = ` "fgFillInverted": "#1e1e1e", "fgSurface2": "#aaa", "fgSurface3": "#d0d0d0", - "fgSurface4": "#e8e8e8", + "fgSurface4": "#eee", "fgSurface5": "#fefefe", "stroke1": "#5b5b5b", "stroke2": "#676767", @@ -8613,7 +8613,7 @@ exports[`buildRamps > background ramp snapshots at lightness 40 1`] = ` "fgFillInverted": "#1e1e1e", "fgSurface2": "#aaa", "fgSurface3": "#d0d0d0", - "fgSurface4": "#e8e8e8", + "fgSurface4": "#eee", "fgSurface5": "#fefefe", "stroke1": "#5b5b5b", "stroke2": "#676767", @@ -8646,7 +8646,7 @@ exports[`buildRamps > background ramp snapshots at lightness 40 1`] = ` "fgFillInverted": "#1e1e1e", "fgSurface2": "#aaa", "fgSurface3": "#d0d0d0", - "fgSurface4": "#e8e8e8", + "fgSurface4": "#eee", "fgSurface5": "#fefefe", "stroke1": "#5b5b5b", "stroke2": "#676767", @@ -8679,7 +8679,7 @@ exports[`buildRamps > background ramp snapshots at lightness 40 1`] = ` "fgFillInverted": "#1e1e1e", "fgSurface2": "#aaa", "fgSurface3": "#d0d0d0", - "fgSurface4": "#e8e8e8", + "fgSurface4": "#eee", "fgSurface5": "#fefefe", "stroke1": "#5b5b5b", "stroke2": "#676767", @@ -8712,7 +8712,7 @@ exports[`buildRamps > background ramp snapshots at lightness 40 1`] = ` "fgFillInverted": "#1e1e1e", "fgSurface2": "#aaa", "fgSurface3": "#d0d0d0", - "fgSurface4": "#e8e8e8", + "fgSurface4": "#eee", "fgSurface5": "#fefefe", "stroke1": "#5b5b5b", "stroke2": "#676767", @@ -8745,7 +8745,7 @@ exports[`buildRamps > background ramp snapshots at lightness 40 1`] = ` "fgFillInverted": "#1e1e1e", "fgSurface2": "#aaa", "fgSurface3": "#d0d0d0", - "fgSurface4": "#e8e8e8", + "fgSurface4": "#eee", "fgSurface5": "#fefefe", "stroke1": "#5b5b5b", "stroke2": "#676767", @@ -8816,7 +8816,7 @@ exports[`buildRamps > background ramp snapshots at lightness 50 1`] = ` "fgFillInverted": "#f5f893", "fgSurface2": "#898a7f", "fgSurface3": "#6b6c63", - "fgSurface4": "#494a43", + "fgSurface4": "#3f3f39", "fgSurface5": "#141412", "stroke1": "#ecefa2", "stroke2": "#d9db90", @@ -8882,7 +8882,7 @@ exports[`buildRamps > background ramp snapshots at lightness 50 1`] = ` "fgFillInverted": "#c8fcfb", "fgSurface2": "#757f7f", "fgSurface3": "#5a6262", - "fgSurface4": "#515959", + "fgSurface4": "#3a3f3f", "fgSurface5": "#121515", "stroke1": "#9de7e6", "stroke2": "#8bd4d3", @@ -8915,7 +8915,7 @@ exports[`buildRamps > background ramp snapshots at lightness 50 1`] = ` "fgFillInverted": "#191e28", "fgSurface2": "#a1a8b4", "fgSurface3": "#c9cdd3", - "fgSurface4": "#e6e8eb", + "fgSurface4": "#edeef0", "fgSurface5": "#fdfefe", "stroke1": "#1f4aba", "stroke2": "#3b68d1", @@ -8981,8 +8981,8 @@ exports[`buildRamps > background ramp snapshots at lightness 50 1`] = ` "fgFillInverted": "#241c1b", "fgSurface2": "#baa9a6", "fgSurface3": "#d9d1cf", - "fgSurface4": "#ebe7e6", - "fgSurface5": "#fefefd", + "fgSurface4": "#f1edec", + "fgSurface5": "#fefdfd", "stroke1": "#8c473d", "stroke2": "#9d554a", "stroke3": "#de9d93", @@ -9014,7 +9014,7 @@ exports[`buildRamps > background ramp snapshots at lightness 50 1`] = ` "fgFillInverted": "#f4f7a5", "fgSurface2": "#79796f", "fgSurface3": "#5d5e56", - "fgSurface4": "#54544d", + "fgSurface4": "#3e3f39", "fgSurface5": "#141412", "stroke1": "#d5d792", "stroke2": "#c3c582", @@ -9048,7 +9048,7 @@ exports[`buildRamps > background ramp snapshots at lightness 50 1`] = ` "fgSurface2": "#616b60", "fgSurface3": "#474f46", "fgSurface4": "#3f463e", - "fgSurface5": "#000", + "fgSurface5": "#121512", "stroke1": "#87cd81", "stroke2": "#77bb72", "stroke3": "#477044", @@ -9113,7 +9113,7 @@ exports[`buildRamps > background ramp snapshots at lightness 50 1`] = ` "fgFillInverted": "#1a1e27", "fgSurface2": "#9ca2ae", "fgSurface3": "#c3c6cd", - "fgSurface4": "#e6e8eb", + "fgSurface4": "#edeef0", "fgSurface5": "#fdfefe", "stroke1": "#2b49a7", "stroke2": "#3958b6", @@ -9179,8 +9179,8 @@ exports[`buildRamps > background ramp snapshots at lightness 50 1`] = ` "fgFillInverted": "#241c1b", "fgSurface2": "#baa9a7", "fgSurface3": "#d9d1d0", - "fgSurface4": "#ebe7e6", - "fgSurface5": "#fefefd", + "fgSurface4": "#f1eded", + "fgSurface5": "#fefdfd", "stroke1": "#8c4741", "stroke2": "#9c544e", "stroke3": "#de9d96", @@ -9213,7 +9213,7 @@ exports[`buildRamps > background ramp snapshots at lightness 50 1`] = ` "fgSurface2": "#686860", "fgSurface3": "#4e4e47", "fgSurface4": "#44443e", - "fgSurface5": "#000", + "fgSurface5": "#141412", "stroke1": "#bdbf84", "stroke2": "#acae74", "stroke3": "#686946", @@ -9311,7 +9311,7 @@ exports[`buildRamps > background ramp snapshots at lightness 50 1`] = ` "fgFillInverted": "#1b1e26", "fgSurface2": "#a0a4af", "fgSurface3": "#c7c9cf", - "fgSurface4": "#e7e8ea", + "fgSurface4": "#edeef0", "fgSurface5": "#fdfefe", "stroke1": "#3f4e9c", "stroke2": "#4b5baa", @@ -9377,8 +9377,8 @@ exports[`buildRamps > background ramp snapshots at lightness 50 1`] = ` "fgFillInverted": "#231c1c", "fgSurface2": "#b6a8a6", "fgSurface3": "#d6cfce", - "fgSurface4": "#ebe7e6", - "fgSurface5": "#fefefe", + "fgSurface4": "#f0eded", + "fgSurface5": "#fefdfd", "stroke1": "#854a49", "stroke2": "#945755", "stroke3": "#d59d9a", @@ -9509,8 +9509,8 @@ exports[`buildRamps > background ramp snapshots at lightness 50 1`] = ` "fgFillInverted": "#1c1e25", "fgSurface2": "#a8aab2", "fgSurface3": "#cecfd4", - "fgSurface4": "#e7e7ea", - "fgSurface5": "#fdfdfe", + "fgSurface4": "#edeeef", + "fgSurface5": "#fefefe", "stroke1": "#505597", "stroke2": "#5d61a4", "stroke3": "#a3a8ca", @@ -9575,8 +9575,8 @@ exports[`buildRamps > background ramp snapshots at lightness 50 1`] = ` "fgFillInverted": "#211d1d", "fgSurface2": "#b3a7a7", "fgSurface3": "#d4cece", - "fgSurface4": "#eae7e7", - "fgSurface5": "#fefefe", + "fgSurface4": "#efeded", + "fgSurface5": "#fefdfd", "stroke1": "#795050", "stroke2": "#875d5d", "stroke3": "#c9a09f", @@ -9707,8 +9707,8 @@ exports[`buildRamps > background ramp snapshots at lightness 50 1`] = ` "fgFillInverted": "#1d1e23", "fgSurface2": "#a9a9b0", "fgSurface3": "#cdced1", - "fgSurface4": "#e7e7e9", - "fgSurface5": "#fdfdfe", + "fgSurface4": "#eeeeef", + "fgSurface5": "#fefefe", "stroke1": "#575685", "stroke2": "#636393", "stroke3": "#a7a8c2", @@ -10009,7 +10009,7 @@ exports[`buildRamps > background ramp snapshots at lightness 60 1`] = ` "fgFillInverted": "#f5f896", "fgSurface2": "#898a7f", "fgSurface3": "#6c6c64", - "fgSurface4": "#494a44", + "fgSurface4": "#3f3f3a", "fgSurface5": "#141412", "stroke1": "#edefa4", "stroke2": "#d9db92", @@ -10042,8 +10042,8 @@ exports[`buildRamps > background ramp snapshots at lightness 60 1`] = ` "fgFillInverted": "#dff9dc", "fgSurface2": "#707b6f", "fgSurface3": "#555d54", - "fgSurface4": "#4c554c", - "fgSurface5": "#121512", + "fgSurface4": "#373d37", + "fgSurface5": "#070807", "stroke1": "#97e592", "stroke2": "#86d180", "stroke3": "#53814f", @@ -10075,7 +10075,7 @@ exports[`buildRamps > background ramp snapshots at lightness 60 1`] = ` "fgFillInverted": "#c8fcfb", "fgSurface2": "#768080", "fgSurface3": "#5a6362", - "fgSurface4": "#525a5a", + "fgSurface4": "#3a3f3f", "fgSurface5": "#121515", "stroke1": "#9fe7e6", "stroke2": "#8ed4d4", @@ -10108,8 +10108,8 @@ exports[`buildRamps > background ramp snapshots at lightness 60 1`] = ` "fgFillInverted": "#1a1e27", "fgSurface2": "#acb0bb", "fgSurface3": "#d4d6db", - "fgSurface4": "#e0e1e5", - "fgSurface5": "#fdfdfe", + "fgSurface4": "#edeef0", + "fgSurface5": "#fdfefe", "stroke1": "#3c53b5", "stroke2": "#5169c8", "stroke3": "#a1afdb", @@ -10207,7 +10207,7 @@ exports[`buildRamps > background ramp snapshots at lightness 60 1`] = ` "fgFillInverted": "#f5f7a2", "fgSurface2": "#7c7d73", "fgSurface3": "#61615a", - "fgSurface4": "#575750", + "fgSurface4": "#3e3f39", "fgSurface5": "#141412", "stroke1": "#dadc9a", "stroke2": "#c9c989", @@ -10241,7 +10241,7 @@ exports[`buildRamps > background ramp snapshots at lightness 60 1`] = ` "fgSurface2": "#656f65", "fgSurface3": "#4c534b", "fgSurface4": "#434a43", - "fgSurface5": "#111310", + "fgSurface5": "#121512", "stroke1": "#8ed28a", "stroke2": "#7ec07a", "stroke3": "#4d754a", @@ -10306,8 +10306,8 @@ exports[`buildRamps > background ramp snapshots at lightness 60 1`] = ` "fgFillInverted": "#1b1e26", "fgSurface2": "#abaeb8", "fgSurface3": "#d2d4d9", - "fgSurface4": "#e7e7ea", - "fgSurface5": "#fdfdfe", + "fgSurface4": "#edeef0", + "fgSurface5": "#fdfefe", "stroke1": "#4854a4", "stroke2": "#5562b3", "stroke3": "#a3add5", @@ -10439,7 +10439,7 @@ exports[`buildRamps > background ramp snapshots at lightness 60 1`] = ` "fgSurface2": "#5c645b", "fgSurface3": "#424942", "fgSurface4": "#3a3f39", - "fgSurface5": "#000", + "fgSurface5": "#121411", "stroke1": "#86c284", "stroke2": "#77b174", "stroke3": "#476945", @@ -10472,7 +10472,7 @@ exports[`buildRamps > background ramp snapshots at lightness 60 1`] = ` "fgSurface2": "#616868", "fgSurface3": "#474d4d", "fgSurface4": "#3e4444", - "fgSurface5": "#000", + "fgSurface5": "#121515", "stroke1": "#8dc4c3", "stroke2": "#7eb3b3", "stroke3": "#4b6c6b", @@ -10504,8 +10504,8 @@ exports[`buildRamps > background ramp snapshots at lightness 60 1`] = ` "fgFillInverted": "#1c1d25", "fgSurface2": "#a9abb4", "fgSurface3": "#d0d1d6", - "fgSurface4": "#e7e7ea", - "fgSurface5": "#fdfdfe", + "fgSurface4": "#edeef0", + "fgSurface5": "#fefefe", "stroke1": "#50549b", "stroke2": "#5c61a9", "stroke3": "#a5aacf", @@ -10604,7 +10604,7 @@ exports[`buildRamps > background ramp snapshots at lightness 60 1`] = ` "fgSurface2": "#62625c", "fgSurface3": "#484843", "fgSurface4": "#3f3f3a", - "fgSurface5": "#000", + "fgSurface5": "#141412", "stroke1": "#b6b689", "stroke2": "#a6a77b", "stroke3": "#626348", @@ -11202,7 +11202,7 @@ exports[`buildRamps > background ramp snapshots at lightness 70 1`] = ` "fgFillInverted": "#f5f79e", "fgSurface2": "#8a8a80", "fgSurface3": "#6d6d65", - "fgSurface4": "#4b4b45", + "fgSurface4": "#40403b", "fgSurface5": "#141412", "stroke1": "#eef0ab", "stroke2": "#dadc99", @@ -11235,7 +11235,7 @@ exports[`buildRamps > background ramp snapshots at lightness 70 1`] = ` "fgFillInverted": "#def9dc", "fgSurface2": "#737d72", "fgSurface3": "#576057", - "fgSurface4": "#4f574f", + "fgSurface4": "#3a4039", "fgSurface5": "#121512", "stroke1": "#9de79a", "stroke2": "#8bd388", @@ -11268,7 +11268,7 @@ exports[`buildRamps > background ramp snapshots at lightness 70 1`] = ` "fgFillInverted": "#c9fcfb", "fgSurface2": "#788282", "fgSurface3": "#5d6464", - "fgSurface4": "#545b5b", + "fgSurface4": "#3a3f3f", "fgSurface5": "#121515", "stroke1": "#a6e9e9", "stroke2": "#94d6d6", @@ -11400,8 +11400,8 @@ exports[`buildRamps > background ramp snapshots at lightness 70 1`] = ` "fgFillInverted": "#f5f6a5", "fgSurface2": "#808178", "fgSurface3": "#64655e", - "fgSurface4": "#464741", - "fgSurface5": "#050504", + "fgSurface4": "#3e3e3a", + "fgSurface5": "#141412", "stroke1": "#e0e1a5", "stroke2": "#cdce93", "stroke3": "#82825e", @@ -11466,8 +11466,8 @@ exports[`buildRamps > background ramp snapshots at lightness 70 1`] = ` "fgFillInverted": "#d3f9f8", "fgSurface2": "#717979", "fgSurface3": "#575e5e", - "fgSurface4": "#4e5454", - "fgSurface5": "#121515", + "fgSurface4": "#393e3e", + "fgSurface5": "#101212", "stroke1": "#a1dcdc", "stroke2": "#90caca", "stroke3": "#5b7e7e", @@ -11598,8 +11598,8 @@ exports[`buildRamps > background ramp snapshots at lightness 70 1`] = ` "fgFillInverted": "#f4f5b9", "fgSurface2": "#777770", "fgSurface3": "#5c5c56", - "fgSurface4": "#52524d", - "fgSurface5": "#141412", + "fgSurface4": "#3d3d38", + "fgSurface5": "#0d0d0c", "stroke1": "#d3d4a0", "stroke2": "#c0c18e", "stroke3": "#78795a", @@ -11632,7 +11632,7 @@ exports[`buildRamps > background ramp snapshots at lightness 70 1`] = ` "fgSurface2": "#666e66", "fgSurface3": "#4d534c", "fgSurface4": "#444944", - "fgSurface5": "#10120f", + "fgSurface5": "#121512", "stroke1": "#98cf96", "stroke2": "#87bd86", "stroke3": "#537453", @@ -11830,7 +11830,7 @@ exports[`buildRamps > background ramp snapshots at lightness 70 1`] = ` "fgSurface2": "#616761", "fgSurface3": "#484d48", "fgSurface4": "#3f433f", - "fgSurface5": "#000", + "fgSurface5": "#131513", "stroke1": "#97c396", "stroke2": "#88b287", "stroke3": "#526c52", @@ -11863,7 +11863,7 @@ exports[`buildRamps > background ramp snapshots at lightness 70 1`] = ` "fgSurface2": "#646969", "fgSurface3": "#4a4f4f", "fgSurface4": "#414545", - "fgSurface5": "#000", + "fgSurface5": "#131514", "stroke1": "#9bc3c3", "stroke2": "#8bb2b2", "stroke3": "#546c6c", @@ -11995,7 +11995,7 @@ exports[`buildRamps > background ramp snapshots at lightness 70 1`] = ` "fgSurface2": "#646460", "fgSurface3": "#4a4a47", "fgSurface4": "#40403d", - "fgSurface5": "#000", + "fgSurface5": "#141413", "stroke1": "#b8b89b", "stroke2": "#a7a78b", "stroke3": "#646453", @@ -12028,7 +12028,7 @@ exports[`buildRamps > background ramp snapshots at lightness 70 1`] = ` "fgSurface2": "#5c615c", "fgSurface3": "#444744", "fgSurface4": "#3a3d3a", - "fgSurface5": "#000", + "fgSurface5": "#0c0d0c", "stroke1": "#99b899", "stroke2": "#89a789", "stroke3": "#526452", @@ -12061,7 +12061,7 @@ exports[`buildRamps > background ramp snapshots at lightness 70 1`] = ` "fgSurface2": "#5e6262", "fgSurface3": "#454848", "fgSurface4": "#3b3e3e", - "fgSurface5": "#000", + "fgSurface5": "#101212", "stroke1": "#9bb7b7", "stroke2": "#8ca7a7", "stroke3": "#546464", @@ -12395,7 +12395,7 @@ exports[`buildRamps > background ramp snapshots at lightness 80 1`] = ` "fgFillInverted": "#f5f6ad", "fgSurface2": "#8b8c83", "fgSurface3": "#6f6f68", - "fgSurface4": "#4d4d48", + "fgSurface4": "#42433e", "fgSurface5": "#141412", "stroke1": "#f0f1b6", "stroke2": "#dcdda4", @@ -12428,7 +12428,7 @@ exports[`buildRamps > background ramp snapshots at lightness 80 1`] = ` "fgFillInverted": "#dbfbd9", "fgSurface2": "#778277", "fgSurface3": "#5c655c", - "fgSurface4": "#545c53", + "fgSurface4": "#3a4039", "fgSurface5": "#121512", "stroke1": "#a5eea4", "stroke2": "#93da92", @@ -12461,8 +12461,8 @@ exports[`buildRamps > background ramp snapshots at lightness 80 1`] = ` "fgFillInverted": "#cafbfb", "fgSurface2": "#7c8585", "fgSurface3": "#606868", - "fgSurface4": "#444a4a", - "fgSurface5": "#121414", + "fgSurface4": "#3a3f3f", + "fgSurface5": "#121515", "stroke1": "#aeeeed", "stroke2": "#9cdada", "stroke3": "#628989", @@ -12528,7 +12528,7 @@ exports[`buildRamps > background ramp snapshots at lightness 80 1`] = ` "fgSurface2": "#6c5e6b", "fgSurface3": "#514651", "fgSurface4": "#463c45", - "fgSurface5": "#000", + "fgSurface5": "#171317", "stroke1": "#de9fdd", "stroke2": "#c887c8", "stroke3": "#874d87", @@ -12561,7 +12561,7 @@ exports[`buildRamps > background ramp snapshots at lightness 80 1`] = ` "fgSurface2": "#6b5b5b", "fgSurface3": "#4f4343", "fgSurface4": "#443939", - "fgSurface5": "#000", + "fgSurface5": "#060404", "stroke1": "#d8a1a1", "stroke2": "#c78e8e", "stroke3": "#8c4d4e", @@ -12593,7 +12593,7 @@ exports[`buildRamps > background ramp snapshots at lightness 80 1`] = ` "fgFillInverted": "#f5f5b1", "fgSurface2": "#85857e", "fgSurface3": "#686962", - "fgSurface4": "#494945", + "fgSurface4": "#3e3e3a", "fgSurface5": "#141413", "stroke1": "#e6e7b4", "stroke2": "#d3d4a2", @@ -12626,7 +12626,7 @@ exports[`buildRamps > background ramp snapshots at lightness 80 1`] = ` "fgFillInverted": "#e0f8df", "fgSurface2": "#747d74", "fgSurface3": "#5a615a", - "fgSurface4": "#515751", + "fgSurface4": "#3a403a", "fgSurface5": "#121512", "stroke1": "#a8e5a7", "stroke2": "#96d195", @@ -12659,8 +12659,8 @@ exports[`buildRamps > background ramp snapshots at lightness 80 1`] = ` "fgFillInverted": "#d3f9f9", "fgSurface2": "#788080", "fgSurface3": "#5d6363", - "fgSurface4": "#545959", - "fgSurface5": "#131514", + "fgSurface4": "#3b3f3f", + "fgSurface5": "#131515", "stroke1": "#b1e4e4", "stroke2": "#a0d1d2", "stroke3": "#648383", @@ -12726,7 +12726,7 @@ exports[`buildRamps > background ramp snapshots at lightness 80 1`] = ` "fgSurface2": "#6c606c", "fgSurface3": "#514751", "fgSurface4": "#463d46", - "fgSurface5": "#000", + "fgSurface5": "#171317", "stroke1": "#daa3da", "stroke2": "#c68cc5", "stroke3": "#855185", @@ -12759,7 +12759,7 @@ exports[`buildRamps > background ramp snapshots at lightness 80 1`] = ` "fgSurface2": "#6d5e5e", "fgSurface3": "#504545", "fgSurface4": "#463b3b", - "fgSurface5": "#000", + "fgSurface5": "#171212", "stroke1": "#d5a8a7", "stroke2": "#c49595", "stroke3": "#8a5152", @@ -12791,7 +12791,7 @@ exports[`buildRamps > background ramp snapshots at lightness 80 1`] = ` "fgFillInverted": "#f5f5bb", "fgSurface2": "#7f7f78", "fgSurface3": "#63635e", - "fgSurface4": "#595954", + "fgSurface4": "#3e3e3b", "fgSurface5": "#141413", "stroke1": "#dedeb2", "stroke2": "#cbcba0", @@ -12824,8 +12824,8 @@ exports[`buildRamps > background ramp snapshots at lightness 80 1`] = ` "fgFillInverted": "#e4f6e4", "fgSurface2": "#727971", "fgSurface3": "#585d57", - "fgSurface4": "#4e534e", - "fgSurface5": "#131513", + "fgSurface4": "#393e39", + "fgSurface5": "#0c0e0c", "stroke1": "#acddab", "stroke2": "#9aca9a", "stroke3": "#617e61", @@ -12857,8 +12857,8 @@ exports[`buildRamps > background ramp snapshots at lightness 80 1`] = ` "fgFillInverted": "#d9f7f7", "fgSurface2": "#747b7b", "fgSurface3": "#5a5f5f", - "fgSurface4": "#515555", - "fgSurface5": "#131414", + "fgSurface4": "#3b3f3f", + "fgSurface5": "#131514", "stroke1": "#b1dcdc", "stroke2": "#9fc9c9", "stroke3": "#647e7e", @@ -12924,7 +12924,7 @@ exports[`buildRamps > background ramp snapshots at lightness 80 1`] = ` "fgSurface2": "#6d626d", "fgSurface3": "#514851", "fgSurface4": "#473f47", - "fgSurface5": "#000", + "fgSurface5": "#171316", "stroke1": "#d7a8d7", "stroke2": "#c493c3", "stroke3": "#835583", @@ -12957,7 +12957,7 @@ exports[`buildRamps > background ramp snapshots at lightness 80 1`] = ` "fgSurface2": "#6e6262", "fgSurface3": "#524848", "fgSurface4": "#473f3f", - "fgSurface5": "#000", + "fgSurface5": "#171313", "stroke1": "#d3aeae", "stroke2": "#c29c9c", "stroke3": "#885758", @@ -12989,7 +12989,7 @@ exports[`buildRamps > background ramp snapshots at lightness 80 1`] = ` "fgFillInverted": "#f4f4c9", "fgSurface2": "#797973", "fgSurface3": "#5d5d59", - "fgSurface4": "#535450", + "fgSurface4": "#3e3e3b", "fgSurface5": "#141413", "stroke1": "#d5d5b2", "stroke2": "#c2c2a0", @@ -13089,7 +13089,7 @@ exports[`buildRamps > background ramp snapshots at lightness 80 1`] = ` "fgSurface2": "#5f5f6f", "fgSurface3": "#464652", "fgSurface4": "#3c3c47", - "fgSurface5": "#000", + "fgSurface5": "#101015", "stroke1": "#aeaece", "stroke2": "#9e9ebf", "stroke3": "#5d5999", @@ -13122,7 +13122,7 @@ exports[`buildRamps > background ramp snapshots at lightness 80 1`] = ` "fgSurface2": "#6d646d", "fgSurface3": "#514a51", "fgSurface4": "#474147", - "fgSurface5": "#000", + "fgSurface5": "#161316", "stroke1": "#d4aed4", "stroke2": "#c29ac1", "stroke3": "#7f5980", @@ -13155,7 +13155,7 @@ exports[`buildRamps > background ramp snapshots at lightness 80 1`] = ` "fgSurface2": "#706666", "fgSurface3": "#544c4c", "fgSurface4": "#494343", - "fgSurface5": "#000", + "fgSurface5": "#161313", "stroke1": "#cfb6b6", "stroke2": "#bfa5a5", "stroke3": "#835f60", @@ -13287,7 +13287,7 @@ exports[`buildRamps > background ramp snapshots at lightness 80 1`] = ` "fgSurface2": "#656571", "fgSurface3": "#4b4b55", "fgSurface4": "#42424b", - "fgSurface5": "#000", + "fgSurface5": "#141418", "stroke1": "#b7b7cd", "stroke2": "#a6a6bd", "stroke3": "#646192", @@ -13320,7 +13320,7 @@ exports[`buildRamps > background ramp snapshots at lightness 80 1`] = ` "fgSurface2": "#6e676e", "fgSurface3": "#524d52", "fgSurface4": "#484348", - "fgSurface5": "#000", + "fgSurface5": "#161416", "stroke1": "#cfb5cf", "stroke2": "#bea3be", "stroke3": "#7b5f7c", @@ -13353,7 +13353,7 @@ exports[`buildRamps > background ramp snapshots at lightness 80 1`] = ` "fgSurface2": "#6c6c6c", "fgSurface3": "#525252", "fgSurface4": "#484848", - "fgSurface5": "#131313", + "fgSurface5": "#141414", "stroke1": "#c1c1c1", "stroke2": "#b0b0b0", "stroke3": "#6c6c6c", @@ -13386,7 +13386,7 @@ exports[`buildRamps > background ramp snapshots at lightness 80 1`] = ` "fgSurface2": "#6c6c6c", "fgSurface3": "#525252", "fgSurface4": "#484848", - "fgSurface5": "#131313", + "fgSurface5": "#141414", "stroke1": "#c1c1c1", "stroke2": "#b0b0b0", "stroke3": "#6c6c6c", @@ -13419,7 +13419,7 @@ exports[`buildRamps > background ramp snapshots at lightness 80 1`] = ` "fgSurface2": "#6c6c6c", "fgSurface3": "#525252", "fgSurface4": "#484848", - "fgSurface5": "#131313", + "fgSurface5": "#141414", "stroke1": "#c1c1c1", "stroke2": "#b0b0b0", "stroke3": "#6c6c6c", @@ -13452,7 +13452,7 @@ exports[`buildRamps > background ramp snapshots at lightness 80 1`] = ` "fgSurface2": "#6c6c6c", "fgSurface3": "#525252", "fgSurface4": "#484848", - "fgSurface5": "#131313", + "fgSurface5": "#141414", "stroke1": "#c1c1c1", "stroke2": "#b0b0b0", "stroke3": "#6c6c6c", @@ -13485,7 +13485,7 @@ exports[`buildRamps > background ramp snapshots at lightness 80 1`] = ` "fgSurface2": "#6c6c6c", "fgSurface3": "#525252", "fgSurface4": "#484848", - "fgSurface5": "#131313", + "fgSurface5": "#141414", "stroke1": "#c1c1c1", "stroke2": "#b0b0b0", "stroke3": "#6c6c6c", @@ -13518,7 +13518,7 @@ exports[`buildRamps > background ramp snapshots at lightness 80 1`] = ` "fgSurface2": "#6c6c6c", "fgSurface3": "#525252", "fgSurface4": "#484848", - "fgSurface5": "#131313", + "fgSurface5": "#141414", "stroke1": "#c1c1c1", "stroke2": "#b0b0b0", "stroke3": "#6c6c6c", @@ -13588,7 +13588,7 @@ exports[`buildRamps > background ramp snapshots at lightness 90 1`] = ` "fgFillInverted": "#f4f4c0", "fgSurface2": "#8d8d86", "fgSurface3": "#71716b", - "fgSurface4": "#50504c", + "fgSurface4": "#464643", "fgSurface5": "#141413", "stroke1": "#f2f2c7", "stroke2": "#ddddb3", @@ -13621,7 +13621,7 @@ exports[`buildRamps > background ramp snapshots at lightness 90 1`] = ` "fgFillInverted": "#dafbd9", "fgSurface2": "#7e897d", "fgSurface3": "#626b62", - "fgSurface4": "#444b44", + "fgSurface4": "#3a403a", "fgSurface5": "#121512", "stroke1": "#c2f2c2", "stroke2": "#9ed49e", @@ -13654,7 +13654,7 @@ exports[`buildRamps > background ramp snapshots at lightness 90 1`] = ` "fgFillInverted": "#cdfbfb", "fgSurface2": "#818a8a", "fgSurface3": "#656c6c", - "fgSurface4": "#454a4a", + "fgSurface4": "#3b3f3f", "fgSurface5": "#131515", "stroke1": "#bcf4f4", "stroke2": "#a8e0e0", @@ -13720,8 +13720,8 @@ exports[`buildRamps > background ramp snapshots at lightness 90 1`] = ` "fgFillInverted": "#faebf9", "fgSurface2": "#827282", "fgSurface3": "#665965", - "fgSurface4": "#5b4f5b", - "fgSurface5": "#171317", + "fgSurface4": "#443b44", + "fgSurface5": "#151115", "stroke1": "#eac6ea", "stroke2": "#d5aed4", "stroke3": "#a15fa3", @@ -13753,8 +13753,8 @@ exports[`buildRamps > background ramp snapshots at lightness 90 1`] = ` "fgFillInverted": "#f8eded", "fgSurface2": "#857272", "fgSurface3": "#675757", - "fgSurface4": "#5c4e4e", - "fgSurface5": "#181313", + "fgSurface4": "#443939", + "fgSurface5": "#0b0808", "stroke1": "#e7c8c8", "stroke2": "#d5b4b4", "stroke3": "#ab6063", @@ -13786,7 +13786,7 @@ exports[`buildRamps > background ramp snapshots at lightness 90 1`] = ` "fgFillInverted": "#f4f4c5", "fgSurface2": "#8a8a84", "fgSurface3": "#6d6d68", - "fgSurface4": "#4a4a46", + "fgSurface4": "#3f3f3c", "fgSurface5": "#141413", "stroke1": "#ededc8", "stroke2": "#d9d9b5", @@ -13819,7 +13819,7 @@ exports[`buildRamps > background ramp snapshots at lightness 90 1`] = ` "fgFillInverted": "#ddf9dd", "fgSurface2": "#7d877d", "fgSurface3": "#626962", - "fgSurface4": "#454a45", + "fgSurface4": "#3a3f3a", "fgSurface5": "#121512", "stroke1": "#c2efc3", "stroke2": "#a7d6a7", @@ -13852,8 +13852,8 @@ exports[`buildRamps > background ramp snapshots at lightness 90 1`] = ` "fgFillInverted": "#d3f9f9", "fgSurface2": "#808787", "fgSurface3": "#656b6a", - "fgSurface4": "#454a4a", - "fgSurface5": "#131414", + "fgSurface4": "#3b3f3f", + "fgSurface5": "#131515", "stroke1": "#bfefef", "stroke2": "#acdbdb", "stroke3": "#6e8b8c", @@ -13918,7 +13918,7 @@ exports[`buildRamps > background ramp snapshots at lightness 90 1`] = ` "fgFillInverted": "#f9ebf9", "fgSurface2": "#837482", "fgSurface3": "#655a65", - "fgSurface4": "#5b505b", + "fgSurface4": "#443c44", "fgSurface5": "#171317", "stroke1": "#e8c9e8", "stroke2": "#d2afd2", @@ -13951,8 +13951,8 @@ exports[`buildRamps > background ramp snapshots at lightness 90 1`] = ` "fgFillInverted": "#f8eded", "fgSurface2": "#857474", "fgSurface3": "#675959", - "fgSurface4": "#5d5050", - "fgSurface5": "#181313", + "fgSurface4": "#453b3b", + "fgSurface5": "#161111", "stroke1": "#e6cccc", "stroke2": "#d4b8b8", "stroke3": "#a96568", @@ -13984,7 +13984,7 @@ exports[`buildRamps > background ramp snapshots at lightness 90 1`] = ` "fgFillInverted": "#f4f3ca", "fgSurface2": "#878782", "fgSurface3": "#6a6a66", - "fgSurface4": "#494946", + "fgSurface4": "#3e3e3c", "fgSurface5": "#141413", "stroke1": "#e8e8c9", "stroke2": "#d5d5b7", @@ -14017,8 +14017,8 @@ exports[`buildRamps > background ramp snapshots at lightness 90 1`] = ` "fgFillInverted": "#e1f8e0", "fgSurface2": "#7d857d", "fgSurface3": "#626862", - "fgSurface4": "#454a45", - "fgSurface5": "#121412", + "fgSurface4": "#3b3f3b", + "fgSurface5": "#131513", "stroke1": "#c4ebc4", "stroke2": "#add5ad", "stroke3": "#6a8a6b", @@ -14050,7 +14050,7 @@ exports[`buildRamps > background ramp snapshots at lightness 90 1`] = ` "fgFillInverted": "#d9f7f7", "fgSurface2": "#7f8585", "fgSurface3": "#646868", - "fgSurface4": "#464949", + "fgSurface4": "#3c3f3f", "fgSurface5": "#131414", "stroke1": "#c4eaea", "stroke2": "#b1d6d6", @@ -14116,8 +14116,8 @@ exports[`buildRamps > background ramp snapshots at lightness 90 1`] = ` "fgFillInverted": "#f8ecf8", "fgSurface2": "#837683", "fgSurface3": "#665b66", - "fgSurface4": "#5b515b", - "fgSurface5": "#171316", + "fgSurface4": "#433c43", + "fgSurface5": "#171317", "stroke1": "#e7cce7", "stroke2": "#d2b4d2", "stroke3": "#9c679d", @@ -14149,7 +14149,7 @@ exports[`buildRamps > background ramp snapshots at lightness 90 1`] = ` "fgFillInverted": "#f7eeee", "fgSurface2": "#857676", "fgSurface3": "#675b5b", - "fgSurface4": "#5d5252", + "fgSurface4": "#453c3c", "fgSurface5": "#171313", "stroke1": "#e5d0d0", "stroke2": "#d3bcbc", @@ -14182,7 +14182,7 @@ exports[`buildRamps > background ramp snapshots at lightness 90 1`] = ` "fgFillInverted": "#f3f3d1", "fgSurface2": "#848480", "fgSurface3": "#686865", - "fgSurface4": "#494947", + "fgSurface4": "#3e3e3c", "fgSurface5": "#141413", "stroke1": "#e4e4ca", "stroke2": "#d1d1b8", @@ -14215,8 +14215,8 @@ exports[`buildRamps > background ramp snapshots at lightness 90 1`] = ` "fgFillInverted": "#e4f6e4", "fgSurface2": "#7c827c", "fgSurface3": "#616761", - "fgSurface4": "#454945", - "fgSurface5": "#0e0f0e", + "fgSurface4": "#3c3f3c", + "fgSurface5": "#131513", "stroke1": "#c6e7c6", "stroke2": "#b2d3b2", "stroke3": "#6e876f", @@ -14248,8 +14248,8 @@ exports[`buildRamps > background ramp snapshots at lightness 90 1`] = ` "fgFillInverted": "#dff6f6", "fgSurface2": "#7e8282", "fgSurface3": "#636767", - "fgSurface4": "#464949", - "fgSurface5": "#121313", + "fgSurface4": "#3c3f3f", + "fgSurface5": "#131414", "stroke1": "#c8e5e5", "stroke2": "#b6d2d2", "stroke3": "#738585", @@ -14281,8 +14281,8 @@ exports[`buildRamps > background ramp snapshots at lightness 90 1`] = ` "fgFillInverted": "#efeff6", "fgSurface2": "#777785", "fgSurface3": "#5b5b6b", - "fgSurface4": "#515160", - "fgSurface5": "#141419", + "fgSurface4": "#3d3c48", + "fgSurface5": "#111116", "stroke1": "#d0d0e3", "stroke2": "#bdbdd1", "stroke3": "#7471aa", @@ -14314,7 +14314,7 @@ exports[`buildRamps > background ramp snapshots at lightness 90 1`] = ` "fgFillInverted": "#f7ecf7", "fgSurface2": "#827882", "fgSurface3": "#655c65", - "fgSurface4": "#5b535b", + "fgSurface4": "#423c42", "fgSurface5": "#161316", "stroke1": "#e5cfe5", "stroke2": "#d1b9d1", @@ -14347,8 +14347,8 @@ exports[`buildRamps > background ramp snapshots at lightness 90 1`] = ` "fgFillInverted": "#f5eeee", "fgSurface2": "#847979", "fgSurface3": "#665d5d", - "fgSurface4": "#5d5454", - "fgSurface5": "#161313", + "fgSurface4": "#433d3d", + "fgSurface5": "#161314", "stroke1": "#e2d4d4", "stroke2": "#d0c1c1", "stroke3": "#9a6f70", @@ -14380,8 +14380,8 @@ exports[`buildRamps > background ramp snapshots at lightness 90 1`] = ` "fgFillInverted": "#f2f2da", "fgSurface2": "#81817e", "fgSurface3": "#656563", - "fgSurface4": "#474746", - "fgSurface5": "#0d0d0d", + "fgSurface4": "#3e3e3d", + "fgSurface5": "#141414", "stroke1": "#e0e0ce", "stroke2": "#ccb", "stroke3": "#818177", @@ -14413,7 +14413,7 @@ exports[`buildRamps > background ramp snapshots at lightness 90 1`] = ` "fgFillInverted": "#e8f4e8", "fgSurface2": "#7c807c", "fgSurface3": "#626561", - "fgSurface4": "#575b57", + "fgSurface4": "#3c3f3c", "fgSurface5": "#131413", "stroke1": "#cbe3cb", "stroke2": "#b8d0b8", @@ -14446,7 +14446,7 @@ exports[`buildRamps > background ramp snapshots at lightness 90 1`] = ` "fgFillInverted": "#e5f4f4", "fgSurface2": "#7d8080", "fgSurface3": "#626464", - "fgSurface4": "#585a5a", + "fgSurface4": "#3d3e3e", "fgSurface5": "#141414", "stroke1": "#cde1e1", "stroke2": "#bacdcd", @@ -14479,7 +14479,7 @@ exports[`buildRamps > background ramp snapshots at lightness 90 1`] = ` "fgFillInverted": "#efeff5", "fgSurface2": "#7a7a84", "fgSurface3": "#5e5e6a", - "fgSurface4": "#54545f", + "fgSurface4": "#3d3d46", "fgSurface5": "#141418", "stroke1": "#d4d4e1", "stroke2": "#c2c1cf", @@ -14512,7 +14512,7 @@ exports[`buildRamps > background ramp snapshots at lightness 90 1`] = ` "fgFillInverted": "#f5edf5", "fgSurface2": "#827a82", "fgSurface3": "#655e65", - "fgSurface4": "#5b555b", + "fgSurface4": "#413d41", "fgSurface5": "#161416", "stroke1": "#e2d3e2", "stroke2": "#cfbecf", @@ -14545,7 +14545,7 @@ exports[`buildRamps > background ramp snapshots at lightness 90 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface2": "#7d7d7d", "fgSurface3": "#626262", - "fgSurface4": "#585858", + "fgSurface4": "#3e3e3e", "fgSurface5": "#141414", "stroke1": "#dadada", "stroke2": "#c7c7c7", @@ -14578,7 +14578,7 @@ exports[`buildRamps > background ramp snapshots at lightness 90 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface2": "#7d7d7d", "fgSurface3": "#626262", - "fgSurface4": "#585858", + "fgSurface4": "#3e3e3e", "fgSurface5": "#141414", "stroke1": "#dadada", "stroke2": "#c7c7c7", @@ -14611,7 +14611,7 @@ exports[`buildRamps > background ramp snapshots at lightness 90 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface2": "#7d7d7d", "fgSurface3": "#626262", - "fgSurface4": "#585858", + "fgSurface4": "#3e3e3e", "fgSurface5": "#141414", "stroke1": "#dadada", "stroke2": "#c7c7c7", @@ -14644,7 +14644,7 @@ exports[`buildRamps > background ramp snapshots at lightness 90 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface2": "#7d7d7d", "fgSurface3": "#626262", - "fgSurface4": "#585858", + "fgSurface4": "#3e3e3e", "fgSurface5": "#141414", "stroke1": "#dadada", "stroke2": "#c7c7c7", @@ -14677,7 +14677,7 @@ exports[`buildRamps > background ramp snapshots at lightness 90 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface2": "#7d7d7d", "fgSurface3": "#626262", - "fgSurface4": "#585858", + "fgSurface4": "#3e3e3e", "fgSurface5": "#141414", "stroke1": "#dadada", "stroke2": "#c7c7c7", @@ -14710,7 +14710,7 @@ exports[`buildRamps > background ramp snapshots at lightness 90 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface2": "#7d7d7d", "fgSurface3": "#626262", - "fgSurface4": "#585858", + "fgSurface4": "#3e3e3e", "fgSurface5": "#141414", "stroke1": "#dadada", "stroke2": "#c7c7c7", @@ -14748,7 +14748,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", - "fgSurface4": "#555", + "fgSurface4": "#4b4b4b", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", @@ -14781,7 +14781,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", - "fgSurface4": "#555", + "fgSurface4": "#4b4b4b", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", @@ -14814,7 +14814,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", - "fgSurface4": "#555", + "fgSurface4": "#4b4b4b", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", @@ -14847,7 +14847,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", - "fgSurface4": "#555", + "fgSurface4": "#4b4b4b", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", @@ -14880,7 +14880,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", - "fgSurface4": "#555", + "fgSurface4": "#4b4b4b", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", @@ -14913,7 +14913,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", - "fgSurface4": "#555", + "fgSurface4": "#4b4b4b", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", @@ -14946,7 +14946,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", - "fgSurface4": "#555", + "fgSurface4": "#4b4b4b", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", @@ -14979,7 +14979,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", - "fgSurface4": "#555", + "fgSurface4": "#4b4b4b", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", @@ -15012,7 +15012,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", - "fgSurface4": "#555", + "fgSurface4": "#4b4b4b", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", @@ -15045,7 +15045,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", - "fgSurface4": "#555", + "fgSurface4": "#4b4b4b", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", @@ -15078,7 +15078,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", - "fgSurface4": "#555", + "fgSurface4": "#4b4b4b", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", @@ -15111,7 +15111,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", - "fgSurface4": "#555", + "fgSurface4": "#4b4b4b", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", @@ -15144,7 +15144,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", - "fgSurface4": "#555", + "fgSurface4": "#4b4b4b", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", @@ -15177,7 +15177,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", - "fgSurface4": "#555", + "fgSurface4": "#4b4b4b", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", @@ -15210,7 +15210,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", - "fgSurface4": "#555", + "fgSurface4": "#4b4b4b", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", @@ -15243,7 +15243,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", - "fgSurface4": "#555", + "fgSurface4": "#4b4b4b", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", @@ -15276,7 +15276,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", - "fgSurface4": "#555", + "fgSurface4": "#4b4b4b", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", @@ -15309,7 +15309,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", - "fgSurface4": "#555", + "fgSurface4": "#4b4b4b", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", @@ -15342,7 +15342,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", - "fgSurface4": "#555", + "fgSurface4": "#4b4b4b", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", @@ -15375,7 +15375,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", - "fgSurface4": "#555", + "fgSurface4": "#4b4b4b", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", @@ -15408,7 +15408,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", - "fgSurface4": "#555", + "fgSurface4": "#4b4b4b", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", @@ -15441,7 +15441,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", - "fgSurface4": "#555", + "fgSurface4": "#4b4b4b", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", @@ -15474,7 +15474,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", - "fgSurface4": "#555", + "fgSurface4": "#4b4b4b", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", @@ -15507,7 +15507,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", - "fgSurface4": "#555", + "fgSurface4": "#4b4b4b", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", @@ -15540,7 +15540,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", - "fgSurface4": "#555", + "fgSurface4": "#4b4b4b", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", @@ -15573,7 +15573,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", - "fgSurface4": "#555", + "fgSurface4": "#4b4b4b", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", @@ -15606,7 +15606,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", - "fgSurface4": "#555", + "fgSurface4": "#4b4b4b", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", @@ -15639,7 +15639,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", - "fgSurface4": "#555", + "fgSurface4": "#4b4b4b", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", @@ -15672,7 +15672,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", - "fgSurface4": "#555", + "fgSurface4": "#4b4b4b", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", @@ -15705,7 +15705,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", - "fgSurface4": "#555", + "fgSurface4": "#4b4b4b", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", @@ -15738,7 +15738,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", - "fgSurface4": "#555", + "fgSurface4": "#4b4b4b", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", @@ -15771,7 +15771,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", - "fgSurface4": "#555", + "fgSurface4": "#4b4b4b", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", @@ -15804,7 +15804,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", - "fgSurface4": "#555", + "fgSurface4": "#4b4b4b", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", @@ -15837,7 +15837,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", - "fgSurface4": "#555", + "fgSurface4": "#4b4b4b", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", @@ -15870,7 +15870,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", - "fgSurface4": "#555", + "fgSurface4": "#4b4b4b", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", @@ -15903,7 +15903,7 @@ exports[`buildRamps > background ramp snapshots at lightness 100 1`] = ` "fgFillInverted": "#f0f0f0", "fgSurface2": "#8f8f8f", "fgSurface3": "#747474", - "fgSurface4": "#555", + "fgSurface4": "#4b4b4b", "fgSurface5": "#141414", "stroke1": "#f3f3f3", "stroke2": "#dfdfdf", diff --git a/packages/theme/src/color-ramps/test/index.test.ts b/packages/theme/src/color-ramps/test/index.test.ts index 61e185ffa13664..34b31650c33bab 100644 --- a/packages/theme/src/color-ramps/test/index.test.ts +++ b/packages/theme/src/color-ramps/test/index.test.ts @@ -70,7 +70,7 @@ const foregroundSteps = [ 'fgSurface5', ] as const; -const MINIMUM_INTERACTION_STATE_APCA_INTERVAL = 14; +const MINIMUM_INTERACTION_STATE_APCA_INTERVAL = 10; const MAXIMUM_ALTERNATE_POLARITY_DELTA_E_DRIFT = 0.02; const perceptualSampleCombinations = [ @@ -504,7 +504,7 @@ describe( 'buildRamps', () => { 'surface4', 'surface5', 'surface6', - ] + ] : [ 'surface6', 'surface5', @@ -512,7 +512,7 @@ describe( 'buildRamps', () => { 'surface1', 'surface2', 'surface3', - ]; + ]; const lightnesses = orderedSteps.map( ( step ) => lightness( step as keyof typeof ramp.ramp ) ); diff --git a/packages/theme/src/prebuilt/ts/color-tokens.ts b/packages/theme/src/prebuilt/ts/color-tokens.ts index 8d2cc9e91163b8..ffe19485664631 100644 --- a/packages/theme/src/prebuilt/ts/color-tokens.ts +++ b/packages/theme/src/prebuilt/ts/color-tokens.ts @@ -140,6 +140,7 @@ export default { 'foreground-interactive-neutral', ], 'bg-fgSurface5': [ + 'foreground-content-neutral-strong', 'foreground-interactive-neutral-active', 'foreground-interactive-neutral-weak-active', ], diff --git a/packages/theme/src/prebuilt/ts/token-types.ts b/packages/theme/src/prebuilt/ts/token-types.ts index a5bf77a4ee8077..456328df962187 100644 --- a/packages/theme/src/prebuilt/ts/token-types.ts +++ b/packages/theme/src/prebuilt/ts/token-types.ts @@ -86,6 +86,7 @@ export type InteractiveBackgroundColor = */ export type ContentForegroundColor = | 'neutral' + | 'neutral-strong' | 'neutral-weak' | 'success' | 'success-weak' @@ -141,6 +142,7 @@ export type InteractiveStrokeColor = */ export type ForegroundColor = | 'neutral' + | 'neutral-strong' | 'neutral-weak' | 'success' | 'success-weak' @@ -154,7 +156,6 @@ export type ForegroundColor = | 'error-weak' | 'neutral-active' | 'neutral-disabled' - | 'neutral-strong' | 'neutral-strong-active' | 'neutral-strong-disabled' | 'neutral-weak-active' diff --git a/packages/theme/src/semantic-color-contrast-pairs.ts b/packages/theme/src/semantic-color-contrast-pairs.ts index 3dcc6f818c5ee7..79451093bb2b76 100644 --- a/packages/theme/src/semantic-color-contrast-pairs.ts +++ b/packages/theme/src/semantic-color-contrast-pairs.ts @@ -39,6 +39,18 @@ export const SEMANTIC_COLOR_CONTRAST_PAIRS = [ background: 'background.surface.neutral-weak', foreground: 'foreground.content.neutral', }, + { + background: 'background.surface.neutral', + foreground: 'foreground.content.neutral-strong', + }, + { + background: 'background.surface.neutral-strong', + foreground: 'foreground.content.neutral-strong', + }, + { + background: 'background.surface.neutral-weak', + foreground: 'foreground.content.neutral-strong', + }, { background: 'background.surface.neutral', foreground: 'foreground.content.neutral-weak', diff --git a/packages/theme/src/test/__snapshots__/build-plugin-parity.test.ts.snap b/packages/theme/src/test/__snapshots__/build-plugin-parity.test.ts.snap index 7d667ec18bf154..dd446cb1f64357 100644 --- a/packages/theme/src/test/__snapshots__/build-plugin-parity.test.ts.snap +++ b/packages/theme/src/test/__snapshots__/build-plugin-parity.test.ts.snap @@ -2,7 +2,7 @@ exports[`design token fallback build plugin parity > keeps esbuild and Vite source-text transforms aligned 1`] = ` "// Source text is transformed consistently: var(--wpds-border-radius-sm). -export const templateStyles = \`color: var(--wpds-color-foreground-content-neutral, #505050);\`; +export const templateStyles = \`color: var(--wpds-color-foreground-content-neutral, #464646);\`; export const doubleQuotedStyles = "gap: var(--wpds-dimension-gap-sm, 8px); content: 'fixture';"; export const singleQuotedStyles = "radius: var(--wpds-border-radius-sm, 2px);"; @@ -13,7 +13,7 @@ export const manualFallback = 'radius: var(--wpds-border-radius-sm, 99px);'; exports[`design token fallback build plugin parity > transforms supported CSS in styles.css 1`] = ` "/* Token references in CSS comments are not transformed: var(--wpds-border-radius-sm). */ .fixture { - color: var(--wpds-color-foreground-content-neutral, #505050); + color: var(--wpds-color-foreground-content-neutral, #464646); content: "var(--wpds-border-radius-sm, 2px)"; } " @@ -28,7 +28,7 @@ exports[`design token fallback build plugin parity > transforms supported CSS in exports[`design token fallback build plugin parity > transforms supported CSS with Lightning CSS in styles.css 1`] = ` ".fixture { - color: var(--wpds-color-foreground-content-neutral, #505050); + color: var(--wpds-color-foreground-content-neutral, #464646); content: "var(--wpds-border-radius-sm)"; } " diff --git a/packages/theme/tokens/color.json b/packages/theme/tokens/color.json index 224a422d093fc3..6f7b8897cfd2d5 100644 --- a/packages/theme/tokens/color.json +++ b/packages/theme/tokens/color.json @@ -139,8 +139,8 @@ "fgSurface4": { "$value": { "colorSpace": "srgb", - "components": [ 0.133, 0.18, 0.855 ], - "hex": "#222eda" + "components": [ 0.114, 0.153, 0.749 ], + "hex": "#1d27bf" } }, "fgSurface5": { @@ -281,8 +281,8 @@ "fgSurface4": { "$value": { "colorSpace": "srgb", - "components": [ 0, 0.314, 0.573 ], - "hex": "#005092" + "components": [ 0, 0.271, 0.502 ], + "hex": "#004580" } }, "fgSurface5": { @@ -423,8 +423,8 @@ "fgSurface4": { "$value": { "colorSpace": "srgb", - "components": [ 0.129, 0.357, 0.184 ], - "hex": "#215b2f" + "components": [ 0.106, 0.31, 0.161 ], + "hex": "#1b4f29" } }, "fgSurface5": { @@ -565,8 +565,8 @@ "fgSurface4": { "$value": { "colorSpace": "srgb", - "components": [ 0.396, 0.298, 0.098 ], - "hex": "#654c19" + "components": [ 0.349, 0.259, 0.082 ], + "hex": "#594215" } }, "fgSurface5": { @@ -707,8 +707,8 @@ "fgSurface4": { "$value": { "colorSpace": "srgb", - "components": [ 0.596, 0.059, 0.059 ], - "hex": "#980f0f" + "components": [ 0.522, 0.047, 0.047 ], + "hex": "#850c0c" } }, "fgSurface5": { @@ -849,8 +849,8 @@ "fgSurface4": { "$value": { "colorSpace": "srgb", - "components": [ 0.314, 0.314, 0.314 ], - "hex": "#505050" + "components": [ 0.275, 0.275, 0.275 ], + "hex": "#464646" } }, "fgSurface5": { @@ -991,8 +991,8 @@ "fgSurface4": { "$value": { "colorSpace": "srgb", - "components": [ 0.361, 0.31, 0.086 ], - "hex": "#5c4f16" + "components": [ 0.318, 0.271, 0.075 ], + "hex": "#514513" } }, "fgSurface5": { @@ -1211,6 +1211,10 @@ "$value": "{wpds-color.primitive.bg.fgSurface4}", "$description": "Foreground color for content like text with normal emphasis." }, + "neutral-strong": { + "$value": "{wpds-color.primitive.bg.fgSurface5}", + "$description": "Foreground color for content like headings with strong emphasis." + }, "neutral-weak": { "$value": "{wpds-color.primitive.bg.fgSurface3}", "$description": "Foreground color for content like text with weak emphasis." diff --git a/packages/ui/CHANGELOG.md b/packages/ui/CHANGELOG.md index 04fdaf2a8a3368..d7d838a168cc15 100644 --- a/packages/ui/CHANGELOG.md +++ b/packages/ui/CHANGELOG.md @@ -49,6 +49,7 @@ - `Switch`, `SwitchControl`: Mark as recommended for use in a WordPress environment ([#84097](https://github.com/WordPress/gutenberg/pull/84097)). - `Progress`: Default to a 160px width, overridable through `className` or `style` ([#84021](https://github.com/WordPress/gutenberg/pull/84021)). - `Progress`: Mark as recommended for use in a WordPress environment. ([#84021](https://github.com/WordPress/gutenberg/pull/84021)) +- Use strong neutral text for headings and overlay titles, highlight tabs on interaction, and thicken link underlines on hover, press, and keyboard focus. ([#82294](https://github.com/WordPress/gutenberg/pull/82294)) - `Menu`: Keep parent items highlighted with a neutral background while their submenus are open ([#84031](https://github.com/WordPress/gutenberg/pull/84031)). - `Menu`: Align group labels and separators with item labels when a radio or checkbox column indents those labels ([#83993](https://github.com/WordPress/gutenberg/pull/83993)). - `Menu`: Tighten item spacing and cap menus at 320px wide ([#84028](https://github.com/WordPress/gutenberg/pull/84028)). diff --git a/packages/ui/src/link/style.module.css b/packages/ui/src/link/style.module.css index 945e7b83220115..5ec4fc23e4428d 100644 --- a/packages/ui/src/link/style.module.css +++ b/packages/ui/src/link/style.module.css @@ -5,6 +5,10 @@ .link { text-underline-offset: 0.2em; text-decoration-thickness: from-font; + + &:is(:hover, :active, :focus-visible) { + text-decoration-thickness: max(0.12em, var(--wpds-border-width-sm)); + } } /* Brand tone */ @@ -16,7 +20,8 @@ } .is-brand:hover, - .is-brand:active { + .is-brand:active, + .is-brand:focus-visible { --_gcd-a-color: var(--wpds-color-foreground-interactive-brand-active); color: var(--wpds-color-foreground-interactive-brand-active); @@ -32,7 +37,8 @@ } .is-neutral:hover, - .is-neutral:active { + .is-neutral:active, + .is-neutral:focus-visible { --_gcd-a-color: var(--wpds-color-foreground-interactive-neutral-active); color: var(--wpds-color-foreground-interactive-neutral-active); diff --git a/packages/ui/src/popover/style.module.css b/packages/ui/src/popover/style.module.css index f0b9ba02746940..a1037b44cc5806 100644 --- a/packages/ui/src/popover/style.module.css +++ b/packages/ui/src/popover/style.module.css @@ -112,8 +112,8 @@ } .title { - color: var(--wpds-color-foreground-content-neutral); - --_gcd-heading-color: var(--wpds-color-foreground-content-neutral); + color: var(--wpds-color-foreground-content-neutral-strong); + --_gcd-heading-color: var(--wpds-color-foreground-content-neutral-strong); } .backdrop { diff --git a/packages/ui/src/tabs/style.module.css b/packages/ui/src/tabs/style.module.css index 460311eb4f8e85..392cdf718b5481 100644 --- a/packages/ui/src/tabs/style.module.css +++ b/packages/ui/src/tabs/style.module.css @@ -140,8 +140,9 @@ } } - &:not([data-disabled]):is(:hover, :focus-visible) { + &:not([data-disabled]):is(:hover, :active, :focus-visible) { color: var(--wpds-color-foreground-interactive-neutral-active); + background-color: var(--wpds-color-background-interactive-neutral-weak-active); } /* Focus indicator. */ diff --git a/packages/ui/src/text/style.module.css b/packages/ui/src/text/style.module.css index b09771d893aac5..2ddd494d74f1eb 100644 --- a/packages/ui/src/text/style.module.css +++ b/packages/ui/src/text/style.module.css @@ -9,6 +9,16 @@ /* Text variants can render as either paragraphs or headings, so each variant provides values for both element-specific CSS defenses. */ + .heading-2xl, + .heading-xl, + .heading-lg, + .heading-md, + .heading-sm { + --_gcd-heading-color: var(--wpds-color-foreground-content-neutral-strong); + + color: var(--wpds-color-foreground-content-neutral-strong); + } + .heading-2xl { --_gcd-heading-font-size: var(--wpds-typography-font-size-2xl); --_gcd-heading-font-weight: var(--wpds-typography-font-weight-emphasis); diff --git a/packages/ui/src/text/types.ts b/packages/ui/src/text/types.ts index 163e75d73b2a07..e79758cb33501e 100644 --- a/packages/ui/src/text/types.ts +++ b/packages/ui/src/text/types.ts @@ -4,6 +4,7 @@ export interface TextProps extends ComponentProps< 'span' > { /** * The typographic variant to apply, controlling font family, size, * line height, and weight. + * Heading variants also use the strong neutral content color. * * @default "body-md" */ diff --git a/packages/ui/src/utils/css/overlay-chrome.module.css b/packages/ui/src/utils/css/overlay-chrome.module.css index 5cb57852d558ac..8246ddebe82f3d 100644 --- a/packages/ui/src/utils/css/overlay-chrome.module.css +++ b/packages/ui/src/utils/css/overlay-chrome.module.css @@ -61,8 +61,8 @@ * `

` rendered via `Text`. */ .title { - color: var(--wpds-color-foreground-content-neutral); - --_gcd-heading-color: var(--wpds-color-foreground-content-neutral); + color: var(--wpds-color-foreground-content-neutral-strong); + --_gcd-heading-color: var(--wpds-color-foreground-content-neutral-strong); margin-inline-end: auto; --_gcd-heading-margin: 0 auto 0 0; diff --git a/storybook/package-styles/config.js b/storybook/package-styles/config.js index 98e250b817b860..2ff6126cbc7e6b 100644 --- a/storybook/package-styles/config.js +++ b/storybook/package-styles/config.js @@ -57,7 +57,8 @@ const CONFIG = [ rtl: [ componentsRtl, editSiteRtl ], }, { - componentIdMatcher: /^components-/, + componentIdMatcher: + /^(components-|design-system-theme-foreground-emphasis$)/, ltr: [ componentsLtr ], rtl: [ componentsRtl ], }, diff --git a/storybook/stories/design-system/foreground-emphasis.story.tsx b/storybook/stories/design-system/foreground-emphasis.story.tsx new file mode 100644 index 00000000000000..79e82bf7452af0 --- /dev/null +++ b/storybook/stories/design-system/foreground-emphasis.story.tsx @@ -0,0 +1,131 @@ +import type { Meta, StoryObj } from '@storybook/react-vite'; +import { ExternalLink, Notice, TabPanel } from '@wordpress/components'; +import { ThemeProvider } from '@wordpress/theme'; +import { Button, Link, Stack, Tabs, Text } from '@wordpress/ui'; + +const meta: Meta = { + title: 'Design System/Theme/Foreground Emphasis', + parameters: { layout: 'padded' }, +}; +export default meta; + +const themes = [ + { name: 'Light', background: '#fcfcfc', primary: '#3858e9' }, + { name: 'Dark', background: '#1e1e1e', primary: '#3858e9' }, + { name: 'Ectoplasm', background: '#4f386e', primary: '#646c3e' }, +]; + +/** + * Compare content emphasis and interaction cues across light, dark, and tinted + * surfaces. Hover the controls and move between them with the keyboard. + */ +export const Comparison: StoryObj = { + render: () => ( +
+ { themes.map( ( { name, ...color } ) => ( + + + + }> + { name } + + }> + Body text uses normal emphasis. Headings use + strong emphasis. + + + Supporting text uses weak emphasis. + + + + + + + }> + + Neutral link + { ' ' } + and brand link + + { ( [ 'default', 'minimal' ] as const ).map( + ( variant ) => ( + + + + Content + + + Settings + + + Disabled + + + + Content panel + + + Settings panel + + + ) + ) } + + Legacy components + + { ( tab ) => { tab.title } panel } + + + Visit WordPress.org + + {} }> + Changes saved. + + + + + ) ) } +
+ ), +}; From b6df5e783ecc3e7a58baf669e52ef3a5209c7ed8 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Tue, 6 Oct 2026 15:50:14 +0200 Subject: [PATCH 29/44] Tabs: Inset interaction backgrounds within focus rings --- packages/components/CHANGELOG.md | 2 +- packages/components/src/tab-panel/style.scss | 9 ++++++-- packages/ui/CHANGELOG.md | 2 +- packages/ui/src/tabs/style.module.css | 24 +++++++++++++------- 4 files changed, 25 insertions(+), 12 deletions(-) diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 933cc4f81bdc48..9560e5543795c4 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -41,9 +41,9 @@ ### Enhancements +- Add interaction backgrounds within `TabPanel` tabs' focus-ring bounds and on `Notice` dismiss buttons, and thicken `ExternalLink` underlines on hover, press, and keyboard focus. ([#82294](https://github.com/WordPress/gutenberg/pull/82294)) - `FormToggle`, `ToggleControl`: Mark as not recommended for use in a WordPress environment, in favour of `Switch` and `SwitchControl` from `@wordpress/ui` ([#84097](https://github.com/WordPress/gutenberg/pull/84097)). - `ProgressBar`: Mark as not recommended and direct consumers to `Progress` from `@wordpress/ui` ([#84021](https://github.com/WordPress/gutenberg/pull/84021)). -- Add interaction backgrounds to `TabPanel` tabs and `Notice` dismiss buttons, and thicken `ExternalLink` underlines on hover, press, and keyboard focus. ([#82294](https://github.com/WordPress/gutenberg/pull/82294)) - `CheckboxControl`: Mark as not recommended for use in a WordPress environment, in favour of `CheckboxControl` from `@wordpress/ui` ([#83771](https://github.com/WordPress/gutenberg/pull/83771)). - `RadioControl`: Mark as not recommended for use in a WordPress environment, in favour of `RadioGroupControl` from `@wordpress/ui` ([#83872](https://github.com/WordPress/gutenberg/pull/83872)). - `RadioControl`: Match the `@wordpress/ui` Radio colors ([#83270](https://github.com/WordPress/gutenberg/pull/83270)). diff --git a/packages/components/src/tab-panel/style.scss b/packages/components/src/tab-panel/style.scss index b9eb7856fa5b36..0a5a845c364604 100644 --- a/packages/components/src/tab-panel/style.scss +++ b/packages/components/src/tab-panel/style.scss @@ -13,6 +13,7 @@ .components-tab-panel__tabs-item { position: relative; + isolation: isolate; border-radius: 0; height: $grid-unit-60 !important; // https://github.com/WordPress/gutenberg/pull/65075#discussion_r1746282734 background: transparent; @@ -30,7 +31,10 @@ &:not(:disabled, [aria-disabled="true"]):is(:hover, :active, :focus-visible) { color: var(--wpds-color-foreground-interactive-neutral-active); - background-color: var(--wpds-color-background-interactive-neutral-weak-active); + + &::before { + background-color: var(--wpds-color-background-interactive-neutral-weak-active); + } } &:focus:not(:disabled) { @@ -67,10 +71,11 @@ outline-offset: -1px; } - // Focus. + // Interaction background and focus ring. &::before { content: ""; position: absolute; + z-index: -1; inset: var(--wpds-dimension-padding-md); pointer-events: none; border-radius: var(--wpds-border-radius-sm); diff --git a/packages/ui/CHANGELOG.md b/packages/ui/CHANGELOG.md index d7d838a168cc15..b31b4eeaee9605 100644 --- a/packages/ui/CHANGELOG.md +++ b/packages/ui/CHANGELOG.md @@ -46,10 +46,10 @@ ### Enhancements +- Use strong neutral text for headings and overlay titles, highlight tabs within their focus-ring bounds on interaction, and thicken link underlines on hover, press, and keyboard focus. ([#82294](https://github.com/WordPress/gutenberg/pull/82294)) - `Switch`, `SwitchControl`: Mark as recommended for use in a WordPress environment ([#84097](https://github.com/WordPress/gutenberg/pull/84097)). - `Progress`: Default to a 160px width, overridable through `className` or `style` ([#84021](https://github.com/WordPress/gutenberg/pull/84021)). - `Progress`: Mark as recommended for use in a WordPress environment. ([#84021](https://github.com/WordPress/gutenberg/pull/84021)) -- Use strong neutral text for headings and overlay titles, highlight tabs on interaction, and thicken link underlines on hover, press, and keyboard focus. ([#82294](https://github.com/WordPress/gutenberg/pull/82294)) - `Menu`: Keep parent items highlighted with a neutral background while their submenus are open ([#84031](https://github.com/WordPress/gutenberg/pull/84031)). - `Menu`: Align group labels and separators with item labels when a radio or checkbox column indents those labels ([#83993](https://github.com/WordPress/gutenberg/pull/83993)). - `Menu`: Tighten item spacing and cap menus at 320px wide ([#84028](https://github.com/WordPress/gutenberg/pull/84028)). diff --git a/packages/ui/src/tabs/style.module.css b/packages/ui/src/tabs/style.module.css index 392cdf718b5481..bf9e7cc14b78db 100644 --- a/packages/ui/src/tabs/style.module.css +++ b/packages/ui/src/tabs/style.module.css @@ -142,19 +142,21 @@ &:not([data-disabled]):is(:hover, :active, :focus-visible) { color: var(--wpds-color-foreground-interactive-neutral-active); - background-color: var(--wpds-color-background-interactive-neutral-weak-active); + + &::after { + background-color: var(--wpds-color-background-interactive-neutral-weak-active); + opacity: 1; + } } - /* Focus indicator. */ + /* Interaction background and focus indicator. */ &::after { position: absolute; z-index: -1; pointer-events: none; /* Outline works for Windows high contrast mode as well. */ - outline: - var(--wpds-border-width-focus) solid - var(--wpds-color-stroke-focus); + outline: var(--wpds-border-width-focus) solid transparent; border-radius: var(--wpds-border-radius-sm); /* Animation */ @@ -166,6 +168,7 @@ } &:focus-visible::after { + outline-color: var(--wpds-color-stroke-focus); opacity: 1; } @@ -196,11 +199,16 @@ [data-orientation="vertical"] & { padding: var(--wpds-dimension-padding-sm) var(--wpds-dimension-padding-md); min-height: var(--wpds-dimension-size-lg); + + &::after { + content: ""; + inset: var(--wpds-border-width-focus); /* TODO: Use or create new control/interactive padding token */ + } } - [data-orientation="vertical"][data-select-on-move="false"] &::after { - content: ""; - inset: var(--wpds-border-width-focus); /* TODO: Use or create new control/interactive padding token */ + [data-orientation="vertical"][data-select-on-move="true"] &::after { + /* The shared selection indicator supplies the focus ring. */ + outline: none; } } From 6f47044e8642c3596dc030989a91ddea010c21ce Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Tue, 6 Oct 2026 17:34:59 +0200 Subject: [PATCH 30/44] Notice: Match dismiss controls to notice tones --- packages/components/CHANGELOG.md | 2 +- packages/components/src/notice/style.scss | 7 ++-- packages/ui/CHANGELOG.md | 2 +- packages/ui/src/notice/style.module.css | 11 ++++-- .../foreground-emphasis.story.tsx | 34 ++++++++++++++++--- 5 files changed, 45 insertions(+), 11 deletions(-) diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 9560e5543795c4..900f442eb66ad9 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -41,7 +41,7 @@ ### Enhancements -- Add interaction backgrounds within `TabPanel` tabs' focus-ring bounds and on `Notice` dismiss buttons, and thicken `ExternalLink` underlines on hover, press, and keyboard focus. ([#82294](https://github.com/WordPress/gutenberg/pull/82294)) +- Add interaction backgrounds within `TabPanel` tabs' focus-ring bounds, match `Notice` dismiss controls to the notice's tone with a border and tinted interaction background, and thicken `ExternalLink` underlines on hover, press, and keyboard focus. ([#82294](https://github.com/WordPress/gutenberg/pull/82294)) - `FormToggle`, `ToggleControl`: Mark as not recommended for use in a WordPress environment, in favour of `Switch` and `SwitchControl` from `@wordpress/ui` ([#84097](https://github.com/WordPress/gutenberg/pull/84097)). - `ProgressBar`: Mark as not recommended and direct consumers to `Progress` from `@wordpress/ui` ([#84021](https://github.com/WordPress/gutenberg/pull/84021)). - `CheckboxControl`: Mark as not recommended for use in a WordPress environment, in favour of `CheckboxControl` from `@wordpress/ui` ([#83771](https://github.com/WordPress/gutenberg/pull/83771)). diff --git a/packages/components/src/notice/style.scss b/packages/components/src/notice/style.scss index 52a88a528e0c43..8e2761c4e1e978 100644 --- a/packages/components/src/notice/style.scss +++ b/packages/components/src/notice/style.scss @@ -61,14 +61,15 @@ .components-notice__dismiss { grid-column: 2; grid-row: 1; - color: var(--wpds-color-foreground-interactive-neutral); + color: var(--wp-components-notice-text-color); margin-inline-start: var(--wpds-dimension-gap-xs); &:not(:disabled):not([aria-disabled="true"]):not(.is-secondary):hover, &:not(:disabled):not([aria-disabled="true"]):not(.is-secondary):active, &:not(:disabled):not([aria-disabled="true"]):focus { - color: var(--wpds-color-foreground-interactive-neutral-active); - background-color: var(--wpds-color-background-interactive-neutral-weak-active); + color: var(--wp-components-notice-text-color); + border-color: var(--wp-components-notice-border-color); + background-color: color-mix(in srgb, var(--wp-components-notice-text-color) 10%, transparent); } } diff --git a/packages/ui/CHANGELOG.md b/packages/ui/CHANGELOG.md index b31b4eeaee9605..8151e93f8f14e0 100644 --- a/packages/ui/CHANGELOG.md +++ b/packages/ui/CHANGELOG.md @@ -46,7 +46,7 @@ ### Enhancements -- Use strong neutral text for headings and overlay titles, highlight tabs within their focus-ring bounds on interaction, and thicken link underlines on hover, press, and keyboard focus. ([#82294](https://github.com/WordPress/gutenberg/pull/82294)) +- Use strong neutral text for headings and overlay titles, highlight tabs within their focus-ring bounds on interaction, match Notice dismiss controls to the notice's tone, and thicken link underlines on hover, press, and keyboard focus. ([#82294](https://github.com/WordPress/gutenberg/pull/82294)) - `Switch`, `SwitchControl`: Mark as recommended for use in a WordPress environment ([#84097](https://github.com/WordPress/gutenberg/pull/84097)). - `Progress`: Default to a 160px width, overridable through `className` or `style` ([#84021](https://github.com/WordPress/gutenberg/pull/84021)). - `Progress`: Mark as recommended for use in a WordPress environment. ([#84021](https://github.com/WordPress/gutenberg/pull/84021)) diff --git a/packages/ui/src/notice/style.module.css b/packages/ui/src/notice/style.module.css index 357d74320413fa..007a7fa102a114 100644 --- a/packages/ui/src/notice/style.module.css +++ b/packages/ui/src/notice/style.module.css @@ -125,9 +125,16 @@ margin-block: auto; } - /* Add partial transparency to CloseIconButton and outline/minimal ActionButton + /* Keep the dismiss control in the notice's color palette. */ + .close-icon { + --wp-ui-button-foreground-color: var(--wp-ui-notice-text-color); + --wp-ui-button-foreground-color-active: var(--wp-ui-notice-text-color); + --wp-ui-button-border-color-active: var(--wp-ui-notice-border-color); + --wp-ui-button-background-color-active: color-mix(in srgb, var(--wp-ui-notice-text-color) 10%, transparent); + } + + /* Add partial transparency to outline/minimal ActionButton * for a better look over tinted backgrounds */ - .close-icon, .action-button:is(.is-action-button-outline, .is-action-button-minimal) { --wp-ui-button-background-color-active: color-mix(in srgb, transparent 50%, var(--wpds-color-background-interactive-neutral-weak-active)); } diff --git a/storybook/stories/design-system/foreground-emphasis.story.tsx b/storybook/stories/design-system/foreground-emphasis.story.tsx index 79e82bf7452af0..a60fd107a07eff 100644 --- a/storybook/stories/design-system/foreground-emphasis.story.tsx +++ b/storybook/stories/design-system/foreground-emphasis.story.tsx @@ -1,7 +1,14 @@ import type { Meta, StoryObj } from '@storybook/react-vite'; import { ExternalLink, Notice, TabPanel } from '@wordpress/components'; import { ThemeProvider } from '@wordpress/theme'; -import { Button, Link, Stack, Tabs, Text } from '@wordpress/ui'; +import { + Button, + Link, + Notice as UINotice, + Stack, + Tabs, + Text, +} from '@wordpress/ui'; const meta: Meta = { title: 'Design System/Theme/Foreground Emphasis', @@ -15,6 +22,8 @@ const themes = [ { name: 'Ectoplasm', background: '#4f386e', primary: '#646c3e' }, ]; +const noticeIntents = [ 'info', 'success', 'warning', 'error' ] as const; + /** * Compare content emphasis and interaction cues across light, dark, and tinted * surfaces. Hover the controls and move between them with the keyboard. @@ -101,6 +110,17 @@ export const Comparison: StoryObj = { ) ) } + + Notices + { noticeIntents.map( ( intent ) => ( + + + { intent } notice + + + + ) ) } + Legacy components Visit WordPress.org - {} }> - Changes saved. - + { noticeIntents.map( ( status ) => ( + {} } + > + { status } notice + + ) ) } From f251db03a2619a215a5cca837206931e682a35c3 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Wed, 7 Oct 2026 18:14:11 +0200 Subject: [PATCH 31/44] Link: Match underline colors to foreground states --- packages/components/CHANGELOG.md | 2 +- packages/components/src/external-link/style.scss | 1 + packages/ui/CHANGELOG.md | 2 +- packages/ui/src/link/style.module.css | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 900f442eb66ad9..707718444e1e35 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -41,7 +41,7 @@ ### Enhancements -- Add interaction backgrounds within `TabPanel` tabs' focus-ring bounds, match `Notice` dismiss controls to the notice's tone with a border and tinted interaction background, and thicken `ExternalLink` underlines on hover, press, and keyboard focus. ([#82294](https://github.com/WordPress/gutenberg/pull/82294)) +- Add interaction backgrounds within `TabPanel` tabs' focus-ring bounds, match `Notice` dismiss controls to the notice's tone with a border and tinted interaction background, and make `ExternalLink` underlines follow the text color and thicken on hover, press, and keyboard focus. ([#82294](https://github.com/WordPress/gutenberg/pull/82294)) - `FormToggle`, `ToggleControl`: Mark as not recommended for use in a WordPress environment, in favour of `Switch` and `SwitchControl` from `@wordpress/ui` ([#84097](https://github.com/WordPress/gutenberg/pull/84097)). - `ProgressBar`: Mark as not recommended and direct consumers to `Progress` from `@wordpress/ui` ([#84021](https://github.com/WordPress/gutenberg/pull/84021)). - `CheckboxControl`: Mark as not recommended for use in a WordPress environment, in favour of `CheckboxControl` from `@wordpress/ui` ([#83771](https://github.com/WordPress/gutenberg/pull/83771)). diff --git a/packages/components/src/external-link/style.scss b/packages/components/src/external-link/style.scss index 31210697ce4003..6cdfa039323fa2 100644 --- a/packages/components/src/external-link/style.scss +++ b/packages/components/src/external-link/style.scss @@ -31,6 +31,7 @@ .components-external-link__contents { text-decoration: underline; + text-decoration-color: currentColor; text-underline-offset: 0.2em; text-decoration-thickness: from-font; } diff --git a/packages/ui/CHANGELOG.md b/packages/ui/CHANGELOG.md index 8151e93f8f14e0..60d1ace79ffddf 100644 --- a/packages/ui/CHANGELOG.md +++ b/packages/ui/CHANGELOG.md @@ -46,7 +46,7 @@ ### Enhancements -- Use strong neutral text for headings and overlay titles, highlight tabs within their focus-ring bounds on interaction, match Notice dismiss controls to the notice's tone, and thicken link underlines on hover, press, and keyboard focus. ([#82294](https://github.com/WordPress/gutenberg/pull/82294)) +- Use strong neutral text for headings and overlay titles, highlight tabs within their focus-ring bounds on interaction, match Notice dismiss controls to the notice's tone, and make link underlines follow the text color and thicken on hover, press, and keyboard focus. ([#82294](https://github.com/WordPress/gutenberg/pull/82294)) - `Switch`, `SwitchControl`: Mark as recommended for use in a WordPress environment ([#84097](https://github.com/WordPress/gutenberg/pull/84097)). - `Progress`: Default to a 160px width, overridable through `className` or `style` ([#84021](https://github.com/WordPress/gutenberg/pull/84021)). - `Progress`: Mark as recommended for use in a WordPress environment. ([#84021](https://github.com/WordPress/gutenberg/pull/84021)) diff --git a/packages/ui/src/link/style.module.css b/packages/ui/src/link/style.module.css index 5ec4fc23e4428d..de598d2feaa02a 100644 --- a/packages/ui/src/link/style.module.css +++ b/packages/ui/src/link/style.module.css @@ -3,6 +3,7 @@ @layer components { .link { + text-decoration-color: currentColor; text-underline-offset: 0.2em; text-decoration-thickness: from-font; @@ -33,7 +34,6 @@ --_gcd-a-color: var(--wpds-color-foreground-interactive-neutral); color: var(--wpds-color-foreground-interactive-neutral); - text-decoration-color: var(--wpds-color-stroke-interactive-neutral); } .is-neutral:hover, From 7a92719ef366cbcfeede7da251bcd7dad64f7e78 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Wed, 7 Oct 2026 18:15:22 +0200 Subject: [PATCH 32/44] Notice: Remove dismiss button borders --- packages/components/CHANGELOG.md | 2 +- packages/components/src/notice/style.scss | 1 - packages/ui/src/notice/style.module.css | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 707718444e1e35..7cd6e3b0904f42 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -41,7 +41,7 @@ ### Enhancements -- Add interaction backgrounds within `TabPanel` tabs' focus-ring bounds, match `Notice` dismiss controls to the notice's tone with a border and tinted interaction background, and make `ExternalLink` underlines follow the text color and thicken on hover, press, and keyboard focus. ([#82294](https://github.com/WordPress/gutenberg/pull/82294)) +- Add interaction backgrounds within `TabPanel` tabs' focus-ring bounds, match `Notice` dismiss controls to the notice's tone with a tinted interaction background, and make `ExternalLink` underlines follow the text color and thicken on hover, press, and keyboard focus. ([#82294](https://github.com/WordPress/gutenberg/pull/82294)) - `FormToggle`, `ToggleControl`: Mark as not recommended for use in a WordPress environment, in favour of `Switch` and `SwitchControl` from `@wordpress/ui` ([#84097](https://github.com/WordPress/gutenberg/pull/84097)). - `ProgressBar`: Mark as not recommended and direct consumers to `Progress` from `@wordpress/ui` ([#84021](https://github.com/WordPress/gutenberg/pull/84021)). - `CheckboxControl`: Mark as not recommended for use in a WordPress environment, in favour of `CheckboxControl` from `@wordpress/ui` ([#83771](https://github.com/WordPress/gutenberg/pull/83771)). diff --git a/packages/components/src/notice/style.scss b/packages/components/src/notice/style.scss index 8e2761c4e1e978..a429464af73fed 100644 --- a/packages/components/src/notice/style.scss +++ b/packages/components/src/notice/style.scss @@ -68,7 +68,6 @@ &:not(:disabled):not([aria-disabled="true"]):not(.is-secondary):active, &:not(:disabled):not([aria-disabled="true"]):focus { color: var(--wp-components-notice-text-color); - border-color: var(--wp-components-notice-border-color); background-color: color-mix(in srgb, var(--wp-components-notice-text-color) 10%, transparent); } } diff --git a/packages/ui/src/notice/style.module.css b/packages/ui/src/notice/style.module.css index 007a7fa102a114..5bc2526f40b50f 100644 --- a/packages/ui/src/notice/style.module.css +++ b/packages/ui/src/notice/style.module.css @@ -129,7 +129,7 @@ .close-icon { --wp-ui-button-foreground-color: var(--wp-ui-notice-text-color); --wp-ui-button-foreground-color-active: var(--wp-ui-notice-text-color); - --wp-ui-button-border-color-active: var(--wp-ui-notice-border-color); + --wp-ui-button-border-color-active: transparent; --wp-ui-button-background-color-active: color-mix(in srgb, var(--wp-ui-notice-text-color) 10%, transparent); } From e2d85fad782c9708aa55c84fa02736d72f6a5dac Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Wed, 7 Oct 2026 18:15:57 +0200 Subject: [PATCH 33/44] Tabs: Paint padded interaction states without widening lists --- packages/components/CHANGELOG.md | 2 +- packages/components/src/tab-panel/style.scss | 2 +- packages/ui/CHANGELOG.md | 2 +- packages/ui/src/tabs/list.tsx | 1 + packages/ui/src/tabs/style.module.css | 29 ++++++++++++++------ 5 files changed, 25 insertions(+), 11 deletions(-) diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 7cd6e3b0904f42..b56099901a1458 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -41,7 +41,7 @@ ### Enhancements -- Add interaction backgrounds within `TabPanel` tabs' focus-ring bounds, match `Notice` dismiss controls to the notice's tone with a tinted interaction background, and make `ExternalLink` underlines follow the text color and thicken on hover, press, and keyboard focus. ([#82294](https://github.com/WordPress/gutenberg/pull/82294)) +- Add interaction backgrounds within `TabPanel` tabs' focus-ring bounds and match their indicators to the text color, match `Notice` dismiss controls to the notice's tone with a tinted interaction background, and make `ExternalLink` underlines follow the text color and thicken on hover, press, and keyboard focus. ([#82294](https://github.com/WordPress/gutenberg/pull/82294)) - `FormToggle`, `ToggleControl`: Mark as not recommended for use in a WordPress environment, in favour of `Switch` and `SwitchControl` from `@wordpress/ui` ([#84097](https://github.com/WordPress/gutenberg/pull/84097)). - `ProgressBar`: Mark as not recommended and direct consumers to `Progress` from `@wordpress/ui` ([#84021](https://github.com/WordPress/gutenberg/pull/84021)). - `CheckboxControl`: Mark as not recommended for use in a WordPress environment, in favour of `CheckboxControl` from `@wordpress/ui` ([#83771](https://github.com/WordPress/gutenberg/pull/83771)). diff --git a/packages/components/src/tab-panel/style.scss b/packages/components/src/tab-panel/style.scss index 0a5a845c364604..0c56c41b936844 100644 --- a/packages/components/src/tab-panel/style.scss +++ b/packages/components/src/tab-panel/style.scss @@ -52,7 +52,7 @@ pointer-events: none; // Draw the indicator. - background: var(--wpds-color-stroke-interactive-neutral-strong); + background: currentColor; height: calc(0 * var(--wpds-border-width-focus)); border-radius: 0; diff --git a/packages/ui/CHANGELOG.md b/packages/ui/CHANGELOG.md index 60d1ace79ffddf..4405fd16ea0140 100644 --- a/packages/ui/CHANGELOG.md +++ b/packages/ui/CHANGELOG.md @@ -46,7 +46,7 @@ ### Enhancements -- Use strong neutral text for headings and overlay titles, highlight tabs within their focus-ring bounds on interaction, match Notice dismiss controls to the notice's tone, and make link underlines follow the text color and thicken on hover, press, and keyboard focus. ([#82294](https://github.com/WordPress/gutenberg/pull/82294)) +- Use strong neutral text for headings and overlay titles, give tabs padded interaction backgrounds and focus rings without widening minimal tab lists and match their indicators to the text color, match Notice dismiss controls to the notice's tone, and make link underlines follow the text color and thicken on hover, press, and keyboard focus. ([#82294](https://github.com/WordPress/gutenberg/pull/82294)) - `Switch`, `SwitchControl`: Mark as recommended for use in a WordPress environment ([#84097](https://github.com/WordPress/gutenberg/pull/84097)). - `Progress`: Default to a 160px width, overridable through `className` or `style` ([#84021](https://github.com/WordPress/gutenberg/pull/84021)). - `Progress`: Mark as recommended for use in a WordPress environment. ([#84021](https://github.com/WordPress/gutenberg/pull/84021)) diff --git a/packages/ui/src/tabs/list.tsx b/packages/ui/src/tabs/list.tsx index 33cc6a718e31b0..4632a92813e600 100644 --- a/packages/ui/src/tabs/list.tsx +++ b/packages/ui/src/tabs/list.tsx @@ -130,6 +130,7 @@ export const List = forwardRef< HTMLDivElement, TabListProps >( data-select-on-move={ activateOnFocus ? 'true' : 'false' } className={ clsx( styles.tablist, + overflow.isScrolling && styles[ 'is-scrolling' ], overflow.first && styles[ 'is-overflowing-first' ], overflow.last && styles[ 'is-overflowing-last' ], styles[ `is-${ variant }-variant` ], diff --git a/packages/ui/src/tabs/style.module.css b/packages/ui/src/tabs/style.module.css index bf9e7cc14b78db..c99766f45e992b 100644 --- a/packages/ui/src/tabs/style.module.css +++ b/packages/ui/src/tabs/style.module.css @@ -25,6 +25,12 @@ width: fit-content; + /* Paint outside the list when the tabs fit. Shadows and outlines + * do not contribute to the measured scrollable width. */ + &:not(.is-scrolling) { + overflow: visible; + } + &.is-overflowing-first { mask-image: linear-gradient(to var(--direction-end), var(--fade-gradient-base)); } @@ -77,7 +83,11 @@ width: var(--active-tab-width); translate: var(--active-tab-left) 0; - background-color: var(--wpds-color-stroke-interactive-neutral-strong); + background-color: var(--wpds-color-foreground-interactive-neutral); + + .tablist:has(> .tab[aria-selected="true"]:not([data-disabled]):is(:hover, :active, :focus-visible)) & { + background-color: var(--wpds-color-foreground-interactive-neutral-active); + } } &[data-orientation="vertical"] { @@ -179,7 +189,15 @@ &::after { content: ""; - inset: var(--wpds-dimension-padding-md); + inset-block: calc(var(--wpds-dimension-padding-md) + var(--wpds-dimension-padding-xs)); + inset-inline: var(--wpds-dimension-padding-lg); + outline-offset: var(--wpds-dimension-padding-xs); + } + + /* The shadow paints padding around the smaller pseudo-element + * without increasing the tab's box or scrollable overflow. */ + &:not([data-disabled]):is(:hover, :active, :focus-visible)::after { + box-shadow: 0 0 0 var(--wpds-dimension-padding-xs) var(--wpds-color-background-interactive-neutral-weak-active); } } @@ -187,12 +205,7 @@ padding-inline: 0; &::after { - /* - * Prevent clipping the focus ring at the tablist edge. - * Use rounding, as the focus ring width may have sub-pixel values - * depending on the screen pixel ratio. - */ - inset-inline: round(up, var(--wpds-border-width-focus), 1px); + inset-inline: 0; } } From 6d9d101e96631c740bf5fc10ca0bba63b3e4979b Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Wed, 7 Oct 2026 18:16:47 +0200 Subject: [PATCH 34/44] Storybook: Expand foreground interaction demos --- .../foreground-emphasis.story.tsx | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/storybook/stories/design-system/foreground-emphasis.story.tsx b/storybook/stories/design-system/foreground-emphasis.story.tsx index a60fd107a07eff..d1dcf13ee82737 100644 --- a/storybook/stories/design-system/foreground-emphasis.story.tsx +++ b/storybook/stories/design-system/foreground-emphasis.story.tsx @@ -1,13 +1,16 @@ import type { Meta, StoryObj } from '@storybook/react-vite'; import { ExternalLink, Notice, TabPanel } from '@wordpress/components'; +import { cog, pencil, trash } from '@wordpress/icons'; import { ThemeProvider } from '@wordpress/theme'; import { Button, + IconButton, Link, Notice as UINotice, Stack, Tabs, Text, + Tooltip, } from '@wordpress/ui'; const meta: Meta = { @@ -67,6 +70,28 @@ export const Comparison: StoryObj = { Supporting text uses weak emphasis. + + + + + + +