diff --git a/packages/block-editor/src/components/color-palette/with-color-context.js b/packages/block-editor/src/components/color-palette/with-color-context.js
index 98c61ec3f71861..e941f19ee56770 100644
--- a/packages/block-editor/src/components/color-palette/with-color-context.js
+++ b/packages/block-editor/src/components/color-palette/with-color-context.js
@@ -7,6 +7,7 @@ import { createHigherOrderComponent } from '@wordpress/compose';
* Internal dependencies
*/
import { useSettings } from '../use-settings';
+import useColorSchemePresets from '../colors-gradients/use-color-scheme-presets';
export default createHigherOrderComponent( ( WrappedComponent ) => {
return function WithColorContext( props ) {
@@ -24,14 +25,18 @@ export default createHigherOrderComponent( ( WrappedComponent ) => {
'color.custom',
'color.defaultPalette'
);
+ const { presets: currentThemeColors } = useColorSchemePresets(
+ 'palette',
+ themeColors
+ );
const _colors = enableDefaultColors
? [
- ...( themeColors || [] ),
+ ...( currentThemeColors || [] ),
...( defaultColors || [] ),
...( customColors || [] ),
]
- : [ ...( themeColors || [] ), ...( customColors || [] ) ];
+ : [ ...( currentThemeColors || [] ), ...( customColors || [] ) ];
const { colors = _colors, disableCustomColors = ! enableCustomColors } =
props;
diff --git a/packages/block-editor/src/components/colors-gradients/test/use-color-scheme-presets.js b/packages/block-editor/src/components/colors-gradients/test/use-color-scheme-presets.js
new file mode 100644
index 00000000000000..0caf52309164f8
--- /dev/null
+++ b/packages/block-editor/src/components/colors-gradients/test/use-color-scheme-presets.js
@@ -0,0 +1,78 @@
+/**
+ * WordPress dependencies
+ */
+import { useMediaQuery } from '@wordpress/compose';
+import { renderHook } from '@testing-library/react';
+
+/**
+ * Internal dependencies
+ */
+import { useSettings } from '../../use-settings';
+import useColorSchemePresets from '../use-color-scheme-presets';
+
+jest.mock( '@wordpress/compose', () => ( {
+ ...jest.requireActual( '@wordpress/compose' ),
+ useMediaQuery: jest.fn(),
+} ) );
+
+jest.mock( '../../use-settings', () => ( {
+ useSettings: jest.fn(),
+} ) );
+
+const baseColors = [
+ { slug: 'base', name: 'Base', color: '#fff' },
+ { slug: 'accent', name: 'Accent', color: '#f00' },
+];
+
+describe( 'useColorSchemePresets', () => {
+ beforeEach( () => {
+ useSettings.mockReset();
+ useMediaQuery.mockReset();
+ } );
+
+ it( 'returns a complete dark palette for a partial alternative', () => {
+ useSettings.mockReturnValue( [
+ undefined,
+ [
+ { slug: 'base', color: '#111' },
+ { slug: 'unknown', color: '#f0f' },
+ ],
+ ] );
+ useMediaQuery.mockImplementation( ( query ) =>
+ query.includes( 'dark' )
+ );
+
+ const { result } = renderHook( () =>
+ useColorSchemePresets( 'palette', baseColors )
+ );
+
+ expect( result.current ).toEqual( {
+ colorScheme: 'dark',
+ hasColorSchemes: true,
+ presets: [
+ { slug: 'base', name: 'Base', color: '#111' },
+ { slug: 'accent', name: 'Accent', color: '#f00' },
+ ],
+ } );
+ } );
+
+ it( 'uses the base palette when the available scheme does not match', () => {
+ useSettings.mockReturnValue( [
+ undefined,
+ [ { slug: 'base', color: '#111' } ],
+ ] );
+ useMediaQuery.mockImplementation( ( query ) =>
+ query.includes( 'light' )
+ );
+
+ const { result } = renderHook( () =>
+ useColorSchemePresets( 'palette', baseColors )
+ );
+
+ expect( result.current ).toEqual( {
+ colorScheme: undefined,
+ hasColorSchemes: true,
+ presets: baseColors,
+ } );
+ } );
+} );
diff --git a/packages/block-editor/src/components/colors-gradients/use-color-scheme-presets.js b/packages/block-editor/src/components/colors-gradients/use-color-scheme-presets.js
new file mode 100644
index 00000000000000..b84f7a4ed2657b
--- /dev/null
+++ b/packages/block-editor/src/components/colors-gradients/use-color-scheme-presets.js
@@ -0,0 +1,59 @@
+/**
+ * WordPress dependencies
+ */
+import { useMediaQuery } from '@wordpress/compose';
+import { normalizeColorSchemePresets } from '@wordpress/global-styles-engine';
+import { useMemo } from '@wordpress/element';
+
+/**
+ * Internal dependencies
+ */
+import { useSettings } from '../use-settings';
+
+/**
+ * Returns the effective preset list for the current color scheme.
+ *
+ * Alternative presets are normalized against the base list by slug. Missing
+ * alternative values use the base value and unmatched alternative slugs are
+ * ignored.
+ *
+ * @param {'palette'|'gradients'|'duotone'} presetType Color preset type.
+ * @param {Array} basePresets Complete base presets.
+ * @return {{ presets: Array, hasColorSchemes: boolean, colorScheme: string|undefined }} Effective presets and scheme metadata.
+ */
+export default function useColorSchemePresets( presetType, basePresets = [] ) {
+ const [ lightPresets, darkPresets ] = useSettings(
+ `color.light.${ presetType }`,
+ `color.dark.${ presetType }`
+ );
+ const prefersLight = useMediaQuery( '(prefers-color-scheme: light)' );
+ const prefersDark = useMediaQuery( '(prefers-color-scheme: dark)' );
+
+ let colorScheme;
+ let alternativePresets;
+ if ( prefersDark && darkPresets !== undefined ) {
+ colorScheme = 'dark';
+ alternativePresets = darkPresets;
+ } else if ( prefersLight && lightPresets !== undefined ) {
+ colorScheme = 'light';
+ alternativePresets = lightPresets;
+ }
+
+ const presets = useMemo(
+ () =>
+ alternativePresets === undefined
+ ? basePresets
+ : normalizeColorSchemePresets(
+ basePresets,
+ alternativePresets
+ ),
+ [ alternativePresets, basePresets ]
+ );
+
+ return {
+ presets,
+ hasColorSchemes:
+ lightPresets !== undefined || darkPresets !== undefined,
+ colorScheme,
+ };
+}
diff --git a/packages/block-editor/src/components/colors-gradients/use-multiple-origin-colors-and-gradients.js b/packages/block-editor/src/components/colors-gradients/use-multiple-origin-colors-and-gradients.js
index cfa5930ba7034c..102624f406e927 100644
--- a/packages/block-editor/src/components/colors-gradients/use-multiple-origin-colors-and-gradients.js
+++ b/packages/block-editor/src/components/colors-gradients/use-multiple-origin-colors-and-gradients.js
@@ -8,6 +8,7 @@ import { _x } from '@wordpress/i18n';
* Internal dependencies
*/
import { useSettings } from '../use-settings';
+import useColorSchemePresets from './use-color-scheme-presets';
/**
* Retrieves color and gradient related settings.
@@ -46,17 +47,24 @@ export default function useMultipleOriginColorsAndGradients() {
disableCustomColors: ! enableCustomColors,
disableCustomGradients: ! enableCustomGradients,
};
+ const { presets: currentThemeColors, hasColorSchemes } =
+ useColorSchemePresets( 'palette', themeColors );
+ const { presets: currentThemeGradients } = useColorSchemePresets(
+ 'gradients',
+ themeGradients
+ );
+ colorGradientSettings.hasColorSchemes = hasColorSchemes;
colorGradientSettings.colors = useMemo( () => {
const result = [];
- if ( themeColors && themeColors.length ) {
+ if ( currentThemeColors && currentThemeColors.length ) {
result.push( {
name: _x(
'Theme',
'Indicates this palette comes from the theme.'
),
slug: 'theme',
- colors: themeColors,
+ colors: currentThemeColors,
} );
}
if (
@@ -86,21 +94,21 @@ export default function useMultipleOriginColorsAndGradients() {
return result;
}, [
customColors,
- themeColors,
+ currentThemeColors,
defaultColors,
shouldDisplayDefaultColors,
] );
colorGradientSettings.gradients = useMemo( () => {
const result = [];
- if ( themeGradients && themeGradients.length ) {
+ if ( currentThemeGradients && currentThemeGradients.length ) {
result.push( {
name: _x(
'Theme',
'Indicates this palette comes from the theme.'
),
slug: 'theme',
- gradients: themeGradients,
+ gradients: currentThemeGradients,
} );
}
if (
@@ -130,7 +138,7 @@ export default function useMultipleOriginColorsAndGradients() {
return result;
}, [
customGradients,
- themeGradients,
+ currentThemeGradients,
defaultGradients,
shouldDisplayDefaultGradients,
] );
diff --git a/packages/block-editor/src/components/colors/with-colors.js b/packages/block-editor/src/components/colors/with-colors.js
index e2298e08b33d3a..781bb3ac258276 100644
--- a/packages/block-editor/src/components/colors/with-colors.js
+++ b/packages/block-editor/src/components/colors/with-colors.js
@@ -16,6 +16,7 @@ import {
} from './utils';
import { useSettings } from '../use-settings';
import { unlock } from '../../lock-unlock';
+import useColorSchemePresets from '../colors-gradients/use-color-scheme-presets';
const { kebabCase } = unlock( componentsPrivateApis );
@@ -62,13 +63,17 @@ const withEditorColorPalette = () =>
'color.palette.theme',
'color.palette.default'
);
+ const { presets: currentThemePalette } = useColorSchemePresets(
+ 'palette',
+ themePalette
+ );
const allColors = useMemo(
() => [
...( userPalette || [] ),
- ...( themePalette || [] ),
+ ...( currentThemePalette || [] ),
...( defaultPalette || [] ),
],
- [ userPalette, themePalette, defaultPalette ]
+ [ userPalette, currentThemePalette, defaultPalette ]
);
return ;
},
diff --git a/packages/block-editor/src/components/global-styles/border-panel.js b/packages/block-editor/src/components/global-styles/border-panel.js
index fff3c070303c79..b4c83fedb7711e 100644
--- a/packages/block-editor/src/components/global-styles/border-panel.js
+++ b/packages/block-editor/src/components/global-styles/border-panel.js
@@ -20,6 +20,7 @@ import { useColorsPerOrigin } from './hooks';
import { useToolsPanelDropdownMenuProps } from './utils';
import { setImmutably } from '../../utils/object';
import { useBorderPanelLabel } from '../../hooks/border';
+import { extractPresetSlug } from '../../utils/color-values';
import { ShadowPopover, useShadowPresets } from './shadow-panel-components';
import {
getInheritanceProps,
@@ -111,10 +112,24 @@ export default function BorderPanel( {
} ) {
const colors = useColorsPerOrigin( settings );
const areCustomSolidsEnabled = settings?.color?.custom;
+ const allColors = useMemo(
+ () => colors.flatMap( ( { colors: originColors } ) => originColors ),
+ [ colors ]
+ );
const decodeValue = useCallback(
( rawValue ) => getValueFromVariable( { settings }, '', rawValue ),
[ settings ]
);
+ const decodeColorValue = useCallback(
+ ( rawValue ) => {
+ const slug = extractPresetSlug( rawValue, 'color' );
+ return (
+ allColors.find( ( color ) => color.slug === slug )?.color ??
+ decodeValue( rawValue )
+ );
+ },
+ [ allColors, decodeValue ]
+ );
// Always keep the layout className (e.g. `single-column`); only the
// inheritance treatment is gated on `showInheritanceLabelIndicators`.
const inheritanceProps = ( isInherited, hasLocalOverride, className ) =>
@@ -124,9 +139,6 @@ export default function BorderPanel( {
className
);
const encodeColorValue = ( colorValue ) => {
- const allColors = colors.flatMap(
- ( { colors: originColors } ) => originColors
- );
const colorObject = allColors.find(
( { color } ) => color === colorValue
);
@@ -145,17 +157,19 @@ export default function BorderPanel( {
[ 'top', 'right', 'bottom', 'left' ].forEach( ( side ) => {
out[ side ] = {
...out[ side ],
- color: decodeValue( out[ side ]?.color ),
+ color: decodeColorValue( out[ side ]?.color ),
};
} );
return out;
}
return {
...source,
- color: source.color ? decodeValue( source.color ) : undefined,
+ color: source.color
+ ? decodeColorValue( source.color )
+ : undefined,
};
},
- [ decodeValue ]
+ [ decodeColorValue ]
);
// Local-then-inherited: prefer the user's locally-set border (whether
// flat or split) when defined, otherwise fall back to the inherited
diff --git a/packages/block-editor/src/components/global-styles/filters-panel.js b/packages/block-editor/src/components/global-styles/filters-panel.js
index ef3d97ed6dbdc8..5ad1556c499feb 100644
--- a/packages/block-editor/src/components/global-styles/filters-panel.js
+++ b/packages/block-editor/src/components/global-styles/filters-panel.js
@@ -22,8 +22,12 @@ import {
} from '@wordpress/components';
import { __, _x } from '@wordpress/i18n';
import { useCallback, useMemo, useRef } from '@wordpress/element';
+import { useMediaQuery } from '@wordpress/compose';
import { reset as resetIcon } from '@wordpress/icons';
-import { getValueFromVariable } from '@wordpress/global-styles-engine';
+import {
+ getValueFromVariable,
+ normalizeColorSchemePresets,
+} from '@wordpress/global-styles-engine';
/**
* Internal dependencies
@@ -49,13 +53,26 @@ function useMultiOriginColorPresets(
settings?.color?.[ presetSetting ]?.theme || EMPTY_ARRAY;
const defaultPresets =
settings?.color?.[ presetSetting ]?.default || EMPTY_ARRAY;
+ const lightPresets = settings?.color?.light?.[ presetSetting ];
+ const darkPresets = settings?.color?.dark?.[ presetSetting ];
+ const prefersLight = useMediaQuery( '(prefers-color-scheme: light)' );
+ const prefersDark = useMediaQuery( '(prefers-color-scheme: dark)' );
+ const currentThemePresets = useMemo( () => {
+ if ( prefersDark && darkPresets !== undefined ) {
+ return normalizeColorSchemePresets( themePresets, darkPresets );
+ }
+ if ( prefersLight && lightPresets !== undefined ) {
+ return normalizeColorSchemePresets( themePresets, lightPresets );
+ }
+ return themePresets;
+ }, [ darkPresets, lightPresets, prefersDark, prefersLight, themePresets ] );
return useMemo(
() => [
...userPresets,
- ...themePresets,
+ ...currentThemePresets,
...( disableDefault ? EMPTY_ARRAY : defaultPresets ),
],
- [ disableDefault, userPresets, themePresets, defaultPresets ]
+ [ currentThemePresets, defaultPresets, disableDefault, userPresets ]
);
}
@@ -189,8 +206,19 @@ export default function FiltersPanel( {
defaultControls = DEFAULT_CONTROLS,
showInheritanceLabelIndicators = ENABLE_GLOBAL_STYLES_INHERITANCE,
} ) {
- const decodeValue = ( rawValue ) =>
- getValueFromVariable( { settings }, '', rawValue );
+ const decodeValue = ( rawValue ) => {
+ const duotonePrefix = 'var:preset|duotone|';
+ if ( rawValue?.startsWith?.( duotonePrefix ) ) {
+ const slug = rawValue.slice( duotonePrefix.length );
+ const preset = duotonePalette.find(
+ ( duotone ) => duotone.slug === slug
+ );
+ if ( preset ) {
+ return preset.colors;
+ }
+ }
+ return getValueFromVariable( { settings }, '', rawValue );
+ };
// Always keep the layout className (e.g. `single-column`); only the
// inheritance treatment is gated on `showInheritanceLabelIndicators`.
const inheritanceProps = ( isInherited, hasLocalOverride, className ) =>
diff --git a/packages/block-editor/src/components/global-styles/hooks.js b/packages/block-editor/src/components/global-styles/hooks.js
index baba0cb53d4fc0..9ab1c7e23c54b2 100644
--- a/packages/block-editor/src/components/global-styles/hooks.js
+++ b/packages/block-editor/src/components/global-styles/hooks.js
@@ -2,15 +2,20 @@
* WordPress dependencies
*/
import { useMemo } from '@wordpress/element';
+import { useMediaQuery } from '@wordpress/compose';
import { useSelect } from '@wordpress/data';
import { store as blocksStore } from '@wordpress/blocks';
import { _x } from '@wordpress/i18n';
-import { getValueFromVariable } from '@wordpress/global-styles-engine';
+import {
+ getValueFromVariable,
+ normalizeColorSchemePresets,
+} from '@wordpress/global-styles-engine';
/**
* Internal dependencies
*/
import { unlock } from '../../lock-unlock';
+import { extractPresetSlug } from '../../utils/color-values';
/**
* React hook that overrides a global settings object with block and element specific settings.
@@ -217,16 +222,29 @@ export function useColorsPerOrigin( settings ) {
const themeColors = settings?.color?.palette?.theme;
const defaultColors = settings?.color?.palette?.default;
const shouldDisplayDefaultColors = settings?.color?.defaultPalette;
+ const prefersLight = useMediaQuery( '(prefers-color-scheme: light)' );
+ const prefersDark = useMediaQuery( '(prefers-color-scheme: dark)' );
+ const lightColors = settings?.color?.light?.palette;
+ const darkColors = settings?.color?.dark?.palette;
+ const currentThemeColors = useMemo( () => {
+ if ( prefersDark && darkColors !== undefined ) {
+ return normalizeColorSchemePresets( themeColors, darkColors );
+ }
+ if ( prefersLight && lightColors !== undefined ) {
+ return normalizeColorSchemePresets( themeColors, lightColors );
+ }
+ return themeColors;
+ }, [ darkColors, lightColors, prefersDark, prefersLight, themeColors ] );
return useMemo( () => {
const result = [];
- if ( themeColors && themeColors.length ) {
+ if ( currentThemeColors && currentThemeColors.length ) {
result.push( {
name: _x(
'Theme',
'Indicates this palette comes from the theme.'
),
- colors: themeColors,
+ colors: currentThemeColors,
} );
}
if (
@@ -254,7 +272,7 @@ export function useColorsPerOrigin( settings ) {
return result;
}, [
customColors,
- themeColors,
+ currentThemeColors,
defaultColors,
shouldDisplayDefaultColors,
] );
@@ -265,16 +283,38 @@ export function useGradientsPerOrigin( settings ) {
const themeGradients = settings?.color?.gradients?.theme;
const defaultGradients = settings?.color?.gradients?.default;
const shouldDisplayDefaultGradients = settings?.color?.defaultGradients;
+ const prefersLight = useMediaQuery( '(prefers-color-scheme: light)' );
+ const prefersDark = useMediaQuery( '(prefers-color-scheme: dark)' );
+ const lightGradients = settings?.color?.light?.gradients;
+ const darkGradients = settings?.color?.dark?.gradients;
+ const currentThemeGradients = useMemo( () => {
+ if ( prefersDark && darkGradients !== undefined ) {
+ return normalizeColorSchemePresets( themeGradients, darkGradients );
+ }
+ if ( prefersLight && lightGradients !== undefined ) {
+ return normalizeColorSchemePresets(
+ themeGradients,
+ lightGradients
+ );
+ }
+ return themeGradients;
+ }, [
+ darkGradients,
+ lightGradients,
+ prefersDark,
+ prefersLight,
+ themeGradients,
+ ] );
return useMemo( () => {
const result = [];
- if ( themeGradients && themeGradients.length ) {
+ if ( currentThemeGradients && currentThemeGradients.length ) {
result.push( {
name: _x(
'Theme',
'Indicates this palette comes from the theme.'
),
- gradients: themeGradients,
+ gradients: currentThemeGradients,
} );
}
if (
@@ -302,7 +342,7 @@ export function useGradientsPerOrigin( settings ) {
return result;
}, [
customGradients,
- themeGradients,
+ currentThemeGradients,
defaultGradients,
shouldDisplayDefaultGradients,
] );
@@ -326,8 +366,34 @@ export function useColorGradientSettings( settings ) {
() => colors.flatMap( ( { colors: originColors } ) => originColors ),
[ colors ]
);
- const decodeValue = ( rawValue ) =>
- getValueFromVariable( { settings }, '', rawValue );
+ const allGradients = useMemo(
+ () =>
+ gradients.flatMap(
+ ( { gradients: originGradients } ) => originGradients
+ ),
+ [ gradients ]
+ );
+ const decodeValue = ( rawValue ) => {
+ const colorSlug = extractPresetSlug( rawValue, 'color' );
+ if ( colorSlug ) {
+ const colorObject = allColors.find(
+ ( { slug } ) => slug === colorSlug
+ );
+ if ( colorObject ) {
+ return colorObject.color;
+ }
+ }
+ const gradientSlug = extractPresetSlug( rawValue, 'gradient' );
+ if ( gradientSlug ) {
+ const gradientObject = allGradients.find(
+ ( { slug } ) => slug === gradientSlug
+ );
+ if ( gradientObject ) {
+ return gradientObject.gradient;
+ }
+ }
+ return getValueFromVariable( { settings }, '', rawValue );
+ };
// When a slug is provided it is used directly: two presets can share the
// same gradient string, and matching by string alone would collapse them
// onto whichever entry appears first. Without a slug, fall back to
@@ -336,9 +402,6 @@ export function useColorGradientSettings( settings ) {
if ( slug ) {
return 'var:preset|gradient|' + slug;
}
- const allGradients = gradients.flatMap(
- ( { gradients: originGradients } ) => originGradients
- );
const gradientObject = allGradients.find(
( { gradient } ) => gradient === gradientValue
);
diff --git a/packages/block-editor/src/components/gradients/use-gradient.js b/packages/block-editor/src/components/gradients/use-gradient.js
index 18a63a96e492ec..48af9a1376f29c 100644
--- a/packages/block-editor/src/components/gradients/use-gradient.js
+++ b/packages/block-editor/src/components/gradients/use-gradient.js
@@ -10,6 +10,7 @@ import { useSelect, useDispatch } from '@wordpress/data';
import { useBlockEditContext } from '../block-edit';
import { useSettings } from '../use-settings';
import { store as blockEditorStore } from '../../store';
+import useColorSchemePresets from '../colors-gradients/use-color-scheme-presets';
export function __experimentalGetGradientClass( gradientSlug ) {
if ( ! gradientSlug ) {
@@ -69,13 +70,21 @@ function useGradient( {
'color.gradients.theme',
'color.gradients.default'
);
+ const { presets: currentThemeGradientPalette } = useColorSchemePresets(
+ 'gradients',
+ themeGradientPalette
+ );
const allGradients = useMemo(
() => [
...( userGradientPalette || [] ),
- ...( themeGradientPalette || [] ),
+ ...( currentThemeGradientPalette || [] ),
...( defaultGradientPalette || [] ),
],
- [ userGradientPalette, themeGradientPalette, defaultGradientPalette ]
+ [
+ userGradientPalette,
+ currentThemeGradientPalette,
+ defaultGradientPalette,
+ ]
);
const { gradient, customGradient } = useSelect(
( select ) => {
@@ -108,7 +117,13 @@ function useGradient( {
[ customGradientAttribute ]: newGradientValue,
} );
},
- [ allGradients, clientId, updateBlockAttributes ]
+ [
+ allGradients,
+ clientId,
+ customGradientAttribute,
+ gradientAttribute,
+ updateBlockAttributes,
+ ]
);
const gradientClass = __experimentalGetGradientClass( gradient );
diff --git a/packages/block-editor/src/hooks/color.js b/packages/block-editor/src/hooks/color.js
index f38f48ac93fa0e..6f603790976688 100644
--- a/packages/block-editor/src/hooks/color.js
+++ b/packages/block-editor/src/hooks/color.js
@@ -21,6 +21,8 @@ import { __experimentalGetGradientClass } from '../components/gradients';
import { transformStyles, shouldSkipSerialization } from './utils';
import { getBackgroundImageClasses } from './background';
import { useSettings } from '../components/use-settings';
+import useColorSchemePresets from '../components/colors-gradients/use-color-scheme-presets';
+import { getNamedPresetStyleValue } from '../utils/color-values';
export const COLOR_SUPPORT_KEY = 'color';
@@ -185,14 +187,16 @@ function useBlockProps( {
'color.palette.theme',
'color.palette.default'
);
+ const { presets: currentThemePalette, hasColorSchemes } =
+ useColorSchemePresets( 'palette', themePalette );
const colors = useMemo(
() => [
...( userPalette || [] ),
- ...( themePalette || [] ),
+ ...( currentThemePalette || [] ),
...( defaultPalette || [] ),
],
- [ userPalette, themePalette, defaultPalette ]
+ [ userPalette, currentThemePalette, defaultPalette ]
);
if (
! hasColorSupport( name ) ||
@@ -206,19 +210,23 @@ function useBlockProps( {
textColor &&
! shouldSkipSerialization( name, COLOR_SUPPORT_KEY, 'text' )
) {
- extraStyles.color = getColorObjectByAttributeValues(
- colors,
- textColor
- )?.color;
+ extraStyles.color = getNamedPresetStyleValue(
+ 'color',
+ textColor,
+ getColorObjectByAttributeValues( colors, textColor )?.color,
+ hasColorSchemes
+ );
}
if (
backgroundColor &&
! shouldSkipSerialization( name, COLOR_SUPPORT_KEY, 'background' )
) {
- extraStyles.backgroundColor = getColorObjectByAttributeValues(
- colors,
- backgroundColor
- )?.color;
+ extraStyles.backgroundColor = getNamedPresetStyleValue(
+ 'color',
+ backgroundColor,
+ getColorObjectByAttributeValues( colors, backgroundColor )?.color,
+ hasColorSchemes
+ );
}
const saveProps = addSaveProps( { style: extraStyles }, name, {
diff --git a/packages/block-editor/src/hooks/duotone.js b/packages/block-editor/src/hooks/duotone.js
index 4b15e117cf7443..2205f07220055b 100644
--- a/packages/block-editor/src/hooks/duotone.js
+++ b/packages/block-editor/src/hooks/duotone.js
@@ -42,6 +42,7 @@ import { useResolvedStyle } from '../components/global-styles/inherited-value-co
import { useBlockEditingMode } from '../components/block-editing-mode';
import { useBlockElement } from '../components/block-list/use-block-props/use-block-refs';
import { store as blockEditorStore } from '../store';
+import useColorSchemePresets from '../components/colors-gradients/use-color-scheme-presets';
const { getDuotoneFilter, getDuotoneStylesheet, getDuotoneUnsetStylesheet } =
unlock( globalStylesEnginePrivateApis );
@@ -68,13 +69,18 @@ function useMultiOriginPresets( { presetSetting, defaultSetting } ) {
`${ presetSetting }.theme`,
`${ presetSetting }.default`
);
+ const presetType = presetSetting.slice( 'color.'.length );
+ const { presets: currentThemePresets } = useColorSchemePresets(
+ presetType,
+ themePresets
+ );
return useMemo(
() => [
...( userPresets || EMPTY_ARRAY ),
- ...( themePresets || EMPTY_ARRAY ),
+ ...( currentThemePresets || EMPTY_ARRAY ),
...( ( enableDefault && defaultPresets ) || EMPTY_ARRAY ),
],
- [ enableDefault, userPresets, themePresets, defaultPresets ]
+ [ enableDefault, userPresets, currentThemePresets, defaultPresets ]
);
}
diff --git a/packages/block-editor/src/hooks/use-border-props.js b/packages/block-editor/src/hooks/use-border-props.js
index 0afe1bb70cb5a8..9ca87226809484 100644
--- a/packages/block-editor/src/hooks/use-border-props.js
+++ b/packages/block-editor/src/hooks/use-border-props.js
@@ -4,6 +4,7 @@
import { getInlineStyles } from './style';
import { getBorderClasses, getMultiOriginColor } from './border';
import useMultipleOriginColorsAndGradients from '../components/colors-gradients/use-multiple-origin-colors-and-gradients';
+import { getNamedPresetStyleValue } from '../utils/color-values';
// This utility is intended to assist where the serialization of the border
// block support is being skipped for a block but the border related CSS classes
@@ -38,7 +39,7 @@ export function getBorderClassesAndStyles( attributes ) {
* @return {Object} ClassName & style props from border block support.
*/
export function useBorderProps( attributes ) {
- const { colors } = useMultipleOriginColorsAndGradients();
+ const { colors, hasColorSchemes } = useMultipleOriginColorsAndGradients();
const borderProps = getBorderClassesAndStyles( attributes );
const { borderColor } = attributes;
@@ -50,7 +51,12 @@ export function useBorderProps( attributes ) {
namedColor: borderColor,
} );
- borderProps.style.borderColor = borderColorObject.color;
+ borderProps.style.borderColor = getNamedPresetStyleValue(
+ 'color',
+ borderColor,
+ borderColorObject.color,
+ hasColorSchemes
+ );
}
return borderProps;
diff --git a/packages/block-editor/src/hooks/use-color-props.js b/packages/block-editor/src/hooks/use-color-props.js
index 5e52b3eb6841fa..dba23c9c640400 100644
--- a/packages/block-editor/src/hooks/use-color-props.js
+++ b/packages/block-editor/src/hooks/use-color-props.js
@@ -21,6 +21,8 @@ import {
getGradientValueBySlug,
} from '../components/gradients';
import { useSettings } from '../components/use-settings';
+import useColorSchemePresets from '../components/colors-gradients/use-color-scheme-presets';
+import { getNamedPresetStyleValue } from '../utils/color-values';
// The code in this file has largely been lifted from the color block support
// hook.
@@ -102,22 +104,28 @@ export function useColorProps( attributes ) {
'color.gradients.theme',
'color.gradients.default'
);
+ const { presets: currentThemePalette, hasColorSchemes } =
+ useColorSchemePresets( 'palette', themePalette );
+ const {
+ presets: currentThemeGradients,
+ hasColorSchemes: hasGradientColorSchemes,
+ } = useColorSchemePresets( 'gradients', themeGradients );
const colors = useMemo(
() => [
...( userPalette || [] ),
- ...( themePalette || [] ),
+ ...( currentThemePalette || [] ),
...( defaultPalette || [] ),
],
- [ userPalette, themePalette, defaultPalette ]
+ [ userPalette, currentThemePalette, defaultPalette ]
);
const gradients = useMemo(
() => [
...( userGradients || [] ),
- ...( themeGradients || [] ),
+ ...( currentThemeGradients || [] ),
...( defaultGradients || [] ),
],
- [ userGradients, themeGradients, defaultGradients ]
+ [ userGradients, currentThemeGradients, defaultGradients ]
);
const colorProps = getColorClassesAndStyles( attributes );
@@ -130,13 +138,20 @@ export function useColorProps( attributes ) {
backgroundColor
);
- colorProps.style.backgroundColor = backgroundColorObject.color;
+ colorProps.style.backgroundColor = getNamedPresetStyleValue(
+ 'color',
+ backgroundColor,
+ backgroundColorObject.color,
+ hasColorSchemes
+ );
}
if ( gradient ) {
- colorProps.style.background = getGradientValueBySlug(
- gradients,
- gradient
+ colorProps.style.background = getNamedPresetStyleValue(
+ 'gradient',
+ gradient,
+ getGradientValueBySlug( gradients, gradient ),
+ hasGradientColorSchemes
);
}
@@ -146,7 +161,12 @@ export function useColorProps( attributes ) {
textColor
);
- colorProps.style.color = textColorObject.color;
+ colorProps.style.color = getNamedPresetStyleValue(
+ 'color',
+ textColor,
+ textColorObject.color,
+ hasColorSchemes
+ );
}
return colorProps;
diff --git a/packages/block-editor/src/hooks/utils.js b/packages/block-editor/src/hooks/utils.js
index 434a35ba6d2bdb..2c0f64b5b8b303 100644
--- a/packages/block-editor/src/hooks/utils.js
+++ b/packages/block-editor/src/hooks/utils.js
@@ -293,6 +293,8 @@ export function useBlockSettings( name, parentLayout ) {
defaultGradientPalette,
defaultGradients,
areCustomGradientsEnabled,
+ lightColorScheme,
+ darkColorScheme,
isBackgroundEnabled,
isLinkEnabled,
isTextEnabled,
@@ -357,6 +359,8 @@ export function useBlockSettings( name, parentLayout ) {
'color.gradients.default',
'color.defaultGradients',
'color.customGradient',
+ 'color.light',
+ 'color.dark',
'color.background',
'color.link',
'color.text',
@@ -394,6 +398,8 @@ export function useBlockSettings( name, parentLayout ) {
custom: customColorsEnabled,
customGradient: areCustomGradientsEnabled,
customDuotone,
+ light: lightColorScheme,
+ dark: darkColorScheme,
background: isBackgroundEnabled,
link: isLinkEnabled,
heading: isHeadingEnabled,
@@ -515,6 +521,8 @@ export function useBlockSettings( name, parentLayout ) {
defaultGradientPalette,
defaultGradients,
areCustomGradientsEnabled,
+ lightColorScheme,
+ darkColorScheme,
isBackgroundEnabled,
isLinkEnabled,
isTextEnabled,
diff --git a/packages/block-editor/src/utils/color-values.ts b/packages/block-editor/src/utils/color-values.ts
index 4b9706bc6b2ad7..572da57a9f5759 100644
--- a/packages/block-editor/src/utils/color-values.ts
+++ b/packages/block-editor/src/utils/color-values.ts
@@ -61,3 +61,36 @@ export function encodeColorValueWithPalette(
const colorObject = allColors.find( ( { color } ) => color === colorValue );
return colorObject ? 'var:preset|color|' + colorObject.slug : colorValue;
}
+
+/**
+ * Returns the CSS custom property reference for a named preset.
+ *
+ * @param type Preset type.
+ * @param slug Preset slug.
+ * @return CSS custom property reference.
+ */
+export function getPresetCSSVar( type: 'color' | 'gradient', slug: string ) {
+ return `var(--wp--preset--${ type }--${ slug })`;
+}
+
+/**
+ * Returns the value used to render a named preset in the editor.
+ *
+ * Color scheme presets must retain their CSS custom property reference so the
+ * browser can apply the current scheme. Existing themes keep the resolved
+ * value used by the editor today.
+ *
+ * @param type Preset type.
+ * @param slug Preset slug.
+ * @param resolvedValue Resolved base or active preset value.
+ * @param hasColorSchemes Whether the preset type has color scheme alternatives.
+ * @return Editor style value.
+ */
+export function getNamedPresetStyleValue(
+ type: 'color' | 'gradient',
+ slug: string,
+ resolvedValue: string | undefined,
+ hasColorSchemes: boolean
+) {
+ return hasColorSchemes ? getPresetCSSVar( type, slug ) : resolvedValue;
+}
diff --git a/packages/block-editor/src/utils/test/color-values.js b/packages/block-editor/src/utils/test/color-values.js
index 0e15730db485cc..87df856e4c6246 100644
--- a/packages/block-editor/src/utils/test/color-values.js
+++ b/packages/block-editor/src/utils/test/color-values.js
@@ -4,6 +4,8 @@
import {
extractPresetSlug,
encodeColorValueWithPalette,
+ getNamedPresetStyleValue,
+ getPresetCSSVar,
} from '../color-values';
describe( 'extractPresetSlug', () => {
@@ -119,3 +121,31 @@ describe( 'encodeColorValueWithPalette', () => {
);
} );
} );
+
+describe( 'getPresetCSSVar', () => {
+ it( 'returns a color preset CSS custom property reference', () => {
+ expect( getPresetCSSVar( 'color', 'accent' ) ).toBe(
+ 'var(--wp--preset--color--accent)'
+ );
+ } );
+
+ it( 'returns a gradient preset CSS custom property reference', () => {
+ expect( getPresetCSSVar( 'gradient', 'signal' ) ).toBe(
+ 'var(--wp--preset--gradient--signal)'
+ );
+ } );
+} );
+
+describe( 'getNamedPresetStyleValue', () => {
+ it( 'preserves the CSS custom property for color scheme presets', () => {
+ expect(
+ getNamedPresetStyleValue( 'color', 'accent', '#f00', true )
+ ).toBe( 'var(--wp--preset--color--accent)' );
+ } );
+
+ it( 'keeps the resolved value when color schemes are absent', () => {
+ expect(
+ getNamedPresetStyleValue( 'color', 'accent', '#f00', false )
+ ).toBe( '#f00' );
+ } );
+} );
diff --git a/packages/components/src/palette-edit/index.tsx b/packages/components/src/palette-edit/index.tsx
index 3999b6bb9f7b24..878d13d0fce68e 100644
--- a/packages/components/src/palette-edit/index.tsx
+++ b/packages/components/src/palette-edit/index.tsx
@@ -48,13 +48,12 @@ import CustomGradientPicker from '../custom-gradient-picker';
import { kebabCase } from '../utils/strings';
import type {
Color,
+ Gradient,
ColorPickerPopoverProps,
NameInputProps,
OptionProps,
PaletteEditListViewProps,
PaletteEditProps,
- PaletteEditColorVariation,
- PaletteEditGradientVariation,
PaletteElement,
} from './types';
@@ -356,10 +355,7 @@ function PaletteEditListView< T extends PaletteElement >( {
}
const EMPTY_ARRAY: Color[] = [];
-const EMPTY_VARIATIONS: (
- | PaletteEditColorVariation
- | PaletteEditGradientVariation
-)[] = [];
+const EMPTY_VARIATIONS: never[] = [];
/**
* Allows editing a palette of colors or gradients.
@@ -397,10 +393,15 @@ export function PaletteEdit( {
}: PaletteEditProps ) {
const isGradient = !! gradients;
const elements = isGradient ? gradients : colors;
- const variations = paletteVariations.map( ( variation ) => ( {
- ...variation,
- elements: isGradient ? variation.gradients : variation.colors,
- } ) );
+ const variations = paletteVariations.map( ( variation ) => {
+ const variationElements = isGradient
+ ? variation.gradients
+ : variation.colors;
+ return {
+ ...variation,
+ elements: variationElements ?? EMPTY_ARRAY,
+ };
+ } );
const [ isEditing, setIsEditing ] = useState( false );
const [ editingElement, setEditingElement ] = useState<
number | null | undefined
@@ -663,9 +664,7 @@ export function PaletteEdit( {
{ isEditing && (
-
+
canOnlyChangeValues={
canOnlyChangeValues
}
diff --git a/packages/components/src/palette-edit/stories/index.story.tsx b/packages/components/src/palette-edit/stories/index.story.tsx
index dab12164ddf166..20ea62ecf52a78 100644
--- a/packages/components/src/palette-edit/stories/index.story.tsx
+++ b/packages/components/src/palette-edit/stories/index.story.tsx
@@ -13,7 +13,12 @@ import { useState } from '@wordpress/element';
* Internal dependencies
*/
import PaletteEdit from '..';
-import type { Color, Gradient } from '../types';
+import type {
+ Color,
+ Gradient,
+ PaletteEditColorVariation,
+ PaletteEditGradientVariation,
+} from '../types';
const meta: Meta< typeof PaletteEdit > = {
title: 'Components/PaletteEdit',
@@ -33,7 +38,7 @@ const meta: Meta< typeof PaletteEdit > = {
export default meta;
const Template: StoryFn< typeof PaletteEdit > = ( args ) => {
- const { colors, gradients, onChange, ...props } = args;
+ const { colors, gradients, onChange, paletteVariations, ...props } = args;
const [ value, setValue ] = useState( gradients || colors );
return (
@@ -41,6 +46,8 @@ const Template: StoryFn< typeof PaletteEdit > = ( args ) => {
{ ...( gradients
? {
gradients: value as Gradient[],
+ paletteVariations:
+ paletteVariations as PaletteEditGradientVariation[],
onChange: ( newValue?: Gradient[] ) => {
setValue( newValue );
onChange( newValue );
@@ -48,6 +55,8 @@ const Template: StoryFn< typeof PaletteEdit > = ( args ) => {
}
: {
colors: value as Color[],
+ paletteVariations:
+ paletteVariations as PaletteEditColorVariation[],
onChange: ( newValue?: Color[] ) => {
setValue( newValue );
onChange( newValue );
diff --git a/packages/global-styles-engine/src/index.ts b/packages/global-styles-engine/src/index.ts
index 9ddf6b7cfc4866..181836ad55df1d 100644
--- a/packages/global-styles-engine/src/index.ts
+++ b/packages/global-styles-engine/src/index.ts
@@ -29,6 +29,11 @@ export {
getResolvedValue,
splitSelectorList,
} from './utils/common';
+export {
+ flattenColorSchemePresets,
+ normalizeColorSchemePresets,
+} from './utils/color-schemes';
+export type { ColorSchemePresetCollection } from './utils/color-schemes';
export { privateApis } from './private-apis';
// Types
diff --git a/packages/global-styles-engine/src/utils/color-schemes.ts b/packages/global-styles-engine/src/utils/color-schemes.ts
new file mode 100644
index 00000000000000..43466882a6edae
--- /dev/null
+++ b/packages/global-styles-engine/src/utils/color-schemes.ts
@@ -0,0 +1,74 @@
+/**
+ * Internal dependencies
+ */
+import type { BasePreset, ColorSchemePreset } from '../types';
+
+type PresetCollection< T extends { slug: string } > =
+ | T[]
+ | {
+ theme?: T[];
+ custom?: T[];
+ default?: T[];
+ };
+
+export type ColorSchemePresetCollection< T extends BasePreset > =
+ PresetCollection< ColorSchemePreset< T > >;
+
+/**
+ * Flattens a color scheme preset collection after settings from different
+ * origins have been merged.
+ *
+ * @param presets Color scheme presets in authored or origin-keyed form.
+ * @return A flat list of color scheme presets.
+ */
+export function flattenColorSchemePresets< T extends { slug: string } >(
+ presets?: PresetCollection< T >
+): T[] {
+ if ( ! presets ) {
+ return [];
+ }
+ if ( Array.isArray( presets ) ) {
+ return presets;
+ }
+ return [
+ ...( presets.theme ?? [] ),
+ ...( presets.custom ?? [] ),
+ ...( presets.default ?? [] ),
+ ];
+}
+
+/**
+ * Creates the effective 1:1 preset list for a color scheme.
+ *
+ * The base preset list owns identity, names, and ordering. Alternative values
+ * replace matching base presets by slug. Missing alternatives use the base
+ * value, while alternatives without a matching base slug are ignored.
+ *
+ * @param basePresets The complete base preset list.
+ * @param alternativePresets The alternative scheme preset overrides.
+ * @return A complete alternative preset list matching the base presets 1:1.
+ */
+export function normalizeColorSchemePresets< T extends BasePreset >(
+ basePresets: T[] | undefined,
+ alternativePresets?: ColorSchemePresetCollection< T >
+): T[] {
+ const alternativesBySlug = new Map(
+ flattenColorSchemePresets( alternativePresets ).map( ( preset ) => [
+ preset.slug,
+ preset,
+ ] )
+ );
+
+ return ( basePresets ?? [] ).map( ( basePreset ) => {
+ const alternative = alternativesBySlug.get( basePreset.slug );
+ if ( ! alternative ) {
+ return { ...basePreset };
+ }
+ return {
+ ...basePreset,
+ ...alternative,
+ name: basePreset.name,
+ slug: basePreset.slug,
+ } as T;
+ } );
+}
diff --git a/packages/global-styles-ui/src/color-palette-panel.tsx b/packages/global-styles-ui/src/color-palette-panel.tsx
index 2d85734bf2b564..b8e470d71a40f5 100644
--- a/packages/global-styles-ui/src/color-palette-panel.tsx
+++ b/packages/global-styles-ui/src/color-palette-panel.tsx
@@ -1,7 +1,10 @@
/**
* WordPress dependencies
*/
-import type { Color, ColorSchemePreset } from '@wordpress/global-styles-engine';
+import type {
+ Color,
+ ColorSchemeSettings,
+} from '@wordpress/global-styles-engine';
import { useViewportMatch } from '@wordpress/compose';
import {
__experimentalPaletteEdit as PaletteEdit,
@@ -17,8 +20,7 @@ import { shuffle } from '@wordpress/icons';
import { useSetting, useColorRandomizer } from './hooks';
import ColorVariations from './variations/variations-color';
import {
- addBasePresetNames,
- flattenSchemePresets,
+ normalizeColorSchemePresets,
SchemePaletteIcon,
type SchemePresetCollection,
} from './color-scheme-palette';
@@ -52,29 +54,43 @@ export default function ColorPalettePanel( { name }: ColorPalettePanelProps ) {
'color.palette.custom',
name
);
+ const [ lightScheme ] = useSetting< ColorSchemeSettings >(
+ 'color.light',
+ name
+ );
const [ lightColors, setLightColors ] = useSetting<
- SchemePresetCollection< ColorSchemePreset< Color > >
+ SchemePresetCollection< Color >
>( 'color.light.palette', name );
- const [ userLightColors ] = useSetting<
- SchemePresetCollection< ColorSchemePreset< Color > >
- >( 'color.light.palette', name, 'user' );
+ const [ userLightColors ] = useSetting< SchemePresetCollection< Color > >(
+ 'color.light.palette',
+ name,
+ 'user'
+ );
+ const [ darkScheme ] = useSetting< ColorSchemeSettings >(
+ 'color.dark',
+ name
+ );
const [ darkColors, setDarkColors ] = useSetting<
- SchemePresetCollection< ColorSchemePreset< Color > >
+ SchemePresetCollection< Color >
>( 'color.dark.palette', name );
- const [ userDarkColors ] = useSetting<
- SchemePresetCollection< ColorSchemePreset< Color > >
- >( 'color.dark.palette', name, 'user' );
+ const [ userDarkColors ] = useSetting< SchemePresetCollection< Color > >(
+ 'color.dark.palette',
+ name,
+ 'user'
+ );
- const namedLightColors = addBasePresetNames(
- flattenSchemePresets( lightColors ),
- themeColors
+ const normalizedLightColors = normalizeColorSchemePresets(
+ themeColors,
+ lightColors
);
- const namedDarkColors = addBasePresetNames(
- flattenSchemePresets( darkColors ),
- themeColors
+ const normalizedDarkColors = normalizeColorSchemePresets(
+ themeColors,
+ darkColors
);
- const hasLightColors = namedLightColors.length > 0;
- const hasDarkColors = namedDarkColors.length > 0;
+ const hasLightColors =
+ lightScheme !== undefined && normalizedLightColors.length > 0;
+ const hasDarkColors =
+ darkScheme !== undefined && normalizedDarkColors.length > 0;
const [ defaultPaletteEnabled ] = useSetting< boolean >(
'color.defaultPalette',
@@ -103,7 +119,7 @@ export default function ColorPalettePanel( { name }: ColorPalettePanelProps ) {
{
canReset:
userLightColors !== undefined,
- colors: namedLightColors,
+ colors: normalizedLightColors,
onChange: setLightColors,
paletteIcon: (
@@ -117,7 +133,7 @@ export default function ColorPalettePanel( { name }: ColorPalettePanelProps ) {
{
canReset:
userDarkColors !== undefined,
- colors: namedDarkColors,
+ colors: normalizedDarkColors,
onChange: setDarkColors,
paletteIcon: (
diff --git a/packages/global-styles-ui/src/color-scheme-palette.tsx b/packages/global-styles-ui/src/color-scheme-palette.tsx
index bd3054c9c22e47..22f71ba44db97d 100644
--- a/packages/global-styles-ui/src/color-scheme-palette.tsx
+++ b/packages/global-styles-ui/src/color-scheme-palette.tsx
@@ -2,52 +2,14 @@
* WordPress dependencies
*/
import { Icon, moon, sun } from '@wordpress/icons';
+export {
+ flattenColorSchemePresets as flattenSchemePresets,
+ normalizeColorSchemePresets,
+} from '@wordpress/global-styles-engine';
+export type { ColorSchemePresetCollection as SchemePresetCollection } from '@wordpress/global-styles-engine';
export type ColorScheme = 'light' | 'dark';
-type PresetWithOptionalName = {
- name?: string;
- slug: string;
-};
-
-export type SchemePresetCollection< T > =
- | T[]
- | {
- theme?: T[];
- custom?: T[];
- default?: T[];
- };
-
export function SchemePaletteIcon( { scheme }: { scheme: ColorScheme } ) {
return ;
}
-
-export function flattenSchemePresets< T >(
- presets?: SchemePresetCollection< T >
-): T[] {
- if ( ! presets ) {
- return [];
- }
- if ( Array.isArray( presets ) ) {
- return presets;
- }
- return [
- ...( presets.theme ?? [] ),
- ...( presets.custom ?? [] ),
- ...( presets.default ?? [] ),
- ];
-}
-
-export function addBasePresetNames<
- T extends PresetWithOptionalName,
- U extends { name: string; slug: string },
->( presets: T[], basePresets?: U[] ): ( T & { name: string } )[] {
- const baseNames = new Map(
- basePresets?.map( ( { name, slug } ) => [ slug, name ] )
- );
-
- return presets.map( ( preset ) => ( {
- ...preset,
- name: preset.name ?? baseNames.get( preset.slug ) ?? preset.slug,
- } ) );
-}
diff --git a/packages/global-styles-ui/src/duotone-palette-panel.tsx b/packages/global-styles-ui/src/duotone-palette-panel.tsx
index 3f289f09f061e5..ceab58c0be3c60 100644
--- a/packages/global-styles-ui/src/duotone-palette-panel.tsx
+++ b/packages/global-styles-ui/src/duotone-palette-panel.tsx
@@ -2,7 +2,7 @@
* WordPress dependencies
*/
import type {
- ColorSchemePreset,
+ ColorSchemeSettings,
Duotone,
} from '@wordpress/global-styles-engine';
import { DuotonePicker } from '@wordpress/components';
@@ -15,8 +15,7 @@ import { Stack } from '@wordpress/ui';
import { useSetting } from './hooks';
import { Subtitle } from './subtitle';
import {
- addBasePresetNames,
- flattenSchemePresets,
+ normalizeColorSchemePresets,
SchemePaletteIcon,
type ColorScheme,
type SchemePresetCollection,
@@ -86,23 +85,35 @@ export default function DuotonePalettePanel( {
...( defaultDuotones && defaultDuotoneEnabled ? defaultDuotones : [] ),
];
- const [ lightDuotones ] = useSetting<
- SchemePresetCollection< ColorSchemePreset< Duotone > >
- >( 'color.light.duotone', name );
- const [ darkDuotones ] = useSetting<
- SchemePresetCollection< ColorSchemePreset< Duotone > >
- >( 'color.dark.duotone', name );
+ const [ lightScheme ] = useSetting< ColorSchemeSettings >(
+ 'color.light',
+ name
+ );
+ const [ lightDuotones ] = useSetting< SchemePresetCollection< Duotone > >(
+ 'color.light.duotone',
+ name
+ );
+ const [ darkScheme ] = useSetting< ColorSchemeSettings >(
+ 'color.dark',
+ name
+ );
+ const [ darkDuotones ] = useSetting< SchemePresetCollection< Duotone > >(
+ 'color.dark.duotone',
+ name
+ );
- const namedLightDuotones = addBasePresetNames(
- flattenSchemePresets( lightDuotones ),
- themeDuotones
+ const normalizedLightDuotones = normalizeColorSchemePresets(
+ themeDuotones,
+ lightDuotones
);
- const namedDarkDuotones = addBasePresetNames(
- flattenSchemePresets( darkDuotones ),
- themeDuotones
+ const normalizedDarkDuotones = normalizeColorSchemePresets(
+ themeDuotones,
+ darkDuotones
);
- const hasLightDuotones = namedLightDuotones.length > 0;
- const hasDarkDuotones = namedDarkDuotones.length > 0;
+ const hasLightDuotones =
+ lightScheme !== undefined && normalizedLightDuotones.length > 0;
+ const hasDarkDuotones =
+ darkScheme !== undefined && normalizedDarkDuotones.length > 0;
return (
) }
{ hasDarkDuotones && (
diff --git a/packages/global-styles-ui/src/gradients-palette-panel.tsx b/packages/global-styles-ui/src/gradients-palette-panel.tsx
index 981a970f23a705..a3970cc0048ce5 100644
--- a/packages/global-styles-ui/src/gradients-palette-panel.tsx
+++ b/packages/global-styles-ui/src/gradients-palette-panel.tsx
@@ -8,7 +8,7 @@ import {
} from '@wordpress/components';
import { __ } from '@wordpress/i18n';
import type {
- ColorSchemePreset,
+ ColorSchemeSettings,
Gradient,
} from '@wordpress/global-styles-engine';
@@ -17,8 +17,7 @@ import type {
*/
import { useSetting } from './hooks';
import {
- addBasePresetNames,
- flattenSchemePresets,
+ normalizeColorSchemePresets,
SchemePaletteIcon,
type SchemePresetCollection,
} from './color-scheme-palette';
@@ -54,29 +53,39 @@ export default function GradientPalettePanel( {
'color.gradients.custom',
name
);
+ const [ lightScheme ] = useSetting< ColorSchemeSettings >(
+ 'color.light',
+ name
+ );
const [ lightGradients, setLightGradients ] = useSetting<
- SchemePresetCollection< ColorSchemePreset< Gradient > >
+ SchemePresetCollection< Gradient >
>( 'color.light.gradients', name );
const [ userLightGradients ] = useSetting<
- SchemePresetCollection< ColorSchemePreset< Gradient > >
+ SchemePresetCollection< Gradient >
>( 'color.light.gradients', name, 'user' );
+ const [ darkScheme ] = useSetting< ColorSchemeSettings >(
+ 'color.dark',
+ name
+ );
const [ darkGradients, setDarkGradients ] = useSetting<
- SchemePresetCollection< ColorSchemePreset< Gradient > >
+ SchemePresetCollection< Gradient >
>( 'color.dark.gradients', name );
const [ userDarkGradients ] = useSetting<
- SchemePresetCollection< ColorSchemePreset< Gradient > >
+ SchemePresetCollection< Gradient >
>( 'color.dark.gradients', name, 'user' );
- const namedLightGradients = addBasePresetNames(
- flattenSchemePresets( lightGradients ),
- themeGradients
+ const normalizedLightGradients = normalizeColorSchemePresets(
+ themeGradients,
+ lightGradients
);
- const namedDarkGradients = addBasePresetNames(
- flattenSchemePresets( darkGradients ),
- themeGradients
+ const normalizedDarkGradients = normalizeColorSchemePresets(
+ themeGradients,
+ darkGradients
);
- const hasLightGradients = namedLightGradients.length > 0;
- const hasDarkGradients = namedDarkGradients.length > 0;
+ const hasLightGradients =
+ lightScheme !== undefined && normalizedLightGradients.length > 0;
+ const hasDarkGradients =
+ darkScheme !== undefined && normalizedDarkGradients.length > 0;
const [ defaultPaletteEnabled ] = useSetting< boolean >(
'color.defaultGradients',
@@ -105,7 +114,7 @@ export default function GradientPalettePanel( {
{
canReset:
userLightGradients !== undefined,
- gradients: namedLightGradients,
+ gradients: normalizedLightGradients,
onChange: setLightGradients,
paletteIcon: (
@@ -119,7 +128,7 @@ export default function GradientPalettePanel( {
{
canReset:
userDarkGradients !== undefined,
- gradients: namedDarkGradients,
+ gradients: normalizedDarkGradients,
onChange: setDarkGradients,
paletteIcon: (
diff --git a/packages/global-styles-ui/src/scheme-preview-indicator.tsx b/packages/global-styles-ui/src/scheme-preview-indicator.tsx
index 794a5fcdfc477f..4b0421401b5110 100644
--- a/packages/global-styles-ui/src/scheme-preview-indicator.tsx
+++ b/packages/global-styles-ui/src/scheme-preview-indicator.tsx
@@ -3,6 +3,7 @@
*/
import { __ } from '@wordpress/i18n';
import { Icon, moon, sun } from '@wordpress/icons';
+import type { BasePreset } from '@wordpress/global-styles-engine';
/**
* Internal dependencies
@@ -16,7 +17,7 @@ import {
type SchemeSettings = Partial<
Record<
'palette' | 'gradients' | 'duotone',
- SchemePresetCollection< unknown >
+ SchemePresetCollection< BasePreset >
>
>;
diff --git a/packages/global-styles-ui/src/test/color-palette-panel.spec.tsx b/packages/global-styles-ui/src/test/color-palette-panel.spec.tsx
new file mode 100644
index 00000000000000..8eb473e8a1836f
--- /dev/null
+++ b/packages/global-styles-ui/src/test/color-palette-panel.spec.tsx
@@ -0,0 +1,71 @@
+/**
+ * External dependencies
+ */
+import { fireEvent, render, screen } from '@testing-library/react';
+
+/**
+ * Internal dependencies
+ */
+import ColorPalettePanel from '../color-palette-panel';
+import { GlobalStylesProvider } from '../provider';
+
+describe( 'ColorPalettePanel color schemes', () => {
+ it( 'shows a complete alternative palette and omits unmatched slugs', async () => {
+ render(
+ {} }
+ >
+
+
+ );
+
+ fireEvent.click(
+ screen.getByRole( 'button', {
+ name: 'Color options',
+ } )
+ );
+ fireEvent.click(
+ screen.getByRole( 'button', {
+ name: 'Show details',
+ } )
+ );
+
+ expect(
+ screen.getAllByRole( 'button', { name: 'Edit: Base' } )
+ ).toHaveLength( 2 );
+ expect(
+ screen.getAllByRole( 'button', { name: 'Edit: Accent' } )
+ ).toHaveLength( 2 );
+ expect(
+ screen.queryByRole( 'button', { name: 'Edit: unknown' } )
+ ).not.toBeInTheDocument();
+ } );
+} );
diff --git a/packages/global-styles-ui/src/test/color-scheme-palette.spec.ts b/packages/global-styles-ui/src/test/color-scheme-palette.spec.ts
index d34bbb266fdb93..ab9ac3470de525 100644
--- a/packages/global-styles-ui/src/test/color-scheme-palette.spec.ts
+++ b/packages/global-styles-ui/src/test/color-scheme-palette.spec.ts
@@ -2,8 +2,8 @@
* Internal dependencies
*/
import {
- addBasePresetNames,
flattenSchemePresets,
+ normalizeColorSchemePresets,
} from '../color-scheme-palette';
describe( 'flattenSchemePresets', () => {
@@ -26,22 +26,31 @@ describe( 'flattenSchemePresets', () => {
} );
} );
-describe( 'addBasePresetNames', () => {
- it( 'uses the matching base preset name when a scheme omits it', () => {
+describe( 'normalizeColorSchemePresets', () => {
+ it( 'creates a complete alternative palette in base order', () => {
expect(
- addBasePresetNames(
- [ { slug: 'base', color: '#111' } ],
- [ { slug: 'base', name: 'Base', color: '#fff' } ]
+ normalizeColorSchemePresets(
+ [
+ { slug: 'base', name: 'Base', color: '#fff' },
+ { slug: 'accent', name: 'Accent', color: '#f00' },
+ ],
+ [ { slug: 'base', color: '#111' } ]
)
- ).toEqual( [ { slug: 'base', name: 'Base', color: '#111' } ] );
+ ).toEqual( [
+ { slug: 'base', name: 'Base', color: '#111' },
+ { slug: 'accent', name: 'Accent', color: '#f00' },
+ ] );
} );
- it( 'preserves a scheme-specific name', () => {
+ it( 'uses base identity and ignores unmatched alternative presets', () => {
expect(
- addBasePresetNames(
- [ { slug: 'base', name: 'Night base', color: '#111' } ],
- [ { slug: 'base', name: 'Base', color: '#fff' } ]
+ normalizeColorSchemePresets(
+ [ { slug: 'base', name: 'Base', color: '#fff' } ],
+ [
+ { slug: 'base', name: 'Night base', color: '#111' },
+ { slug: 'unknown', color: '#f0f' },
+ ]
)
- ).toEqual( [ { slug: 'base', name: 'Night base', color: '#111' } ] );
+ ).toEqual( [ { slug: 'base', name: 'Base', color: '#111' } ] );
} );
} );
diff --git a/packages/global-styles-ui/tsconfig.json b/packages/global-styles-ui/tsconfig.json
index 71ce10c14a3711..1db116b339f062 100644
--- a/packages/global-styles-ui/tsconfig.json
+++ b/packages/global-styles-ui/tsconfig.json
@@ -24,5 +24,5 @@
{ "path": "../ui" }
],
"include": [ "src/**/*" ],
- "exclude": [ "src/font-library/lib/**/*" ]
+ "exclude": [ "src/font-library/lib/**/*", "src/**/test/**/*" ]
}