diff --git a/lib/class-wp-duotone-gutenberg.php b/lib/class-wp-duotone-gutenberg.php
index 3544f580367101..45936e4b3e5473 100644
--- a/lib/class-wp-duotone-gutenberg.php
+++ b/lib/class-wp-duotone-gutenberg.php
@@ -115,7 +115,14 @@ public static function set_global_style_block_names() {
continue;
}
// If it has a duotone filter preset, save the block name and the preset slug.
- $slug = self::gutenberg_get_slug_from_attr( $duotone_attr );
+ if ( is_string( $duotone_attr ) ) {
+ $slug = self::gutenberg_get_slug_from_attr( $duotone_attr );
+ // If the block has custom colors, we generate a slug for it and save it.
+ } else {
+ $slug = self::custom_unique_slug( $duotone_attr );
+ self::$global_styles_presets[ $slug ]['slug'] = $slug;
+ self::$global_styles_presets[ $slug ]['colors'] = $duotone_attr;
+ }
if ( $slug && $slug !== $duotone_attr ) {
self::$global_styles_block_names[ $block_node['name'] ] = $slug;
@@ -247,6 +254,16 @@ public static function output_global_styles() {
}
}
+ /**
+ * Generate a unique slug for the filter based on the array of colors.
+ *
+ * @param string $duotone_attr The duotone attribute from a block.
+ * @return string A unique string that defines the custom filter.
+ */
+ public static function custom_unique_slug( $duotone_attr ) {
+ return wp_unique_id( sanitize_key( implode( '-', $duotone_attr ) . '-' ) );
+ }
+
/**
* Render out the duotone CSS styles and SVG.
*
@@ -304,8 +321,8 @@ public static function render_duotone_support( $block_content, $block ) {
// Pass through the CSS value.
$declaration_value = $duotone_attr;
} elseif ( $is_custom ) {
- // Build a unique slug for the filter based on the array of colors.
- $slug = wp_unique_id( sanitize_key( implode( '-', $duotone_attr ) . '-' ) );
+
+ $slug = self::custom_unique_slug( $duotone_attr );
$filter_data = array(
'slug' => $slug,
diff --git a/packages/block-editor/src/components/global-styles/use-global-styles-output.js b/packages/block-editor/src/components/global-styles/use-global-styles-output.js
index cce2b61f0786e7..887b2e01b6840a 100644
--- a/packages/block-editor/src/components/global-styles/use-global-styles-output.js
+++ b/packages/block-editor/src/components/global-styles/use-global-styles-output.js
@@ -15,6 +15,7 @@ import {
import { useSelect } from '@wordpress/data';
import { useContext, useMemo } from '@wordpress/element';
import { getCSSRules } from '@wordpress/style-engine';
+import { cleanForSlug } from '@wordpress/url';
/**
* Internal dependencies
@@ -23,7 +24,7 @@ import { PRESET_METADATA, ROOT_BLOCK_SELECTOR, scopeSelector } from './utils';
import { getTypographyFontSizeValue } from './typography-utils';
import { GlobalStylesContext } from './context';
import { useGlobalSetting } from './hooks';
-import { PresetDuotoneFilter } from '../duotone/components';
+import { PresetDuotoneFilter, DuotoneFilter } from '../duotone/components';
import { getGapCSSValue } from '../../hooks/gap';
import { store as blockEditorStore } from '../../store';
@@ -260,11 +261,20 @@ export function getStylesDeclarations(
const cssProperty = key.startsWith( '--' )
? key
: kebabCase( key );
- declarations.push(
- `${ cssProperty }: ${ compileStyleValue(
- get( blockStyles, pathToValue )
- ) }`
- );
+
+ //The duotone filter is not one of the presets so we need to build the svg and the filter ID
+ if ( Array.isArray( blockStyles.filter?.duotone ) ) {
+ const slug = duotoneSlug( blockStyles.filter.duotone );
+ declarations.push(
+ `${ cssProperty }: url('#wp-duotone-${ slug }')`
+ );
+ } else {
+ declarations.push(
+ `${ cssProperty }: ${ compileStyleValue(
+ get( blockStyles, pathToValue )
+ ) }`
+ );
+ }
}
return declarations;
@@ -900,6 +910,32 @@ export const toStyles = (
return ruleset;
};
+function duotoneSlug( colors ) {
+ return colors
+ .map( ( x ) => cleanForSlug( x ).replaceAll( '-', '' ) )
+ .join( '-' );
+}
+
+export function customSvgFilters( tree, blockSelectors ) {
+ const nodesWithStyles = getNodesWithStyles( tree, blockSelectors );
+
+ return nodesWithStyles
+ .filter(
+ ( { styles, duotoneSelector } ) =>
+ duotoneSelector && Array.isArray( styles.filter?.duotone )
+ )
+ .flatMap( ( node ) => {
+ const slug = duotoneSlug( node.styles.filter.duotone );
+ return (
+
+ );
+ } );
+}
+
export function toSvgFilters( tree, blockSelectors ) {
const nodesWithSettings = getNodesWithSettings( tree, blockSelectors );
return nodesWithSettings.flatMap( ( { presets } ) => {
@@ -1050,7 +1086,9 @@ export function useGlobalStylesOutput() {
disableLayoutStyles
);
- const filters = toSvgFilters( mergedConfig, blockSelectors );
+ const presetSvgs = toSvgFilters( mergedConfig, blockSelectors );
+ const customSvgs = customSvgFilters( mergedConfig, blockSelectors );
+
const stylesheets = [
{
css: customProperties,
@@ -1083,7 +1121,11 @@ export function useGlobalStylesOutput() {
}
} );
- return [ stylesheets, mergedConfig.settings, filters ];
+ return [
+ stylesheets,
+ mergedConfig.settings,
+ [ ...presetSvgs, ...customSvgs ],
+ ];
}, [
hasBlockGapSupport,
hasFallbackGapSupport,
diff --git a/packages/block-editor/src/components/global-styles/utils.js b/packages/block-editor/src/components/global-styles/utils.js
index 2ff2e732298044..3f34e1783930bb 100644
--- a/packages/block-editor/src/components/global-styles/utils.js
+++ b/packages/block-editor/src/components/global-styles/utils.js
@@ -15,6 +15,7 @@ export const ROOT_BLOCK_SUPPORTS = [
'background',
'backgroundColor',
'color',
+ 'filter',
'linkColor',
'captionColor',
'buttonColor',
diff --git a/packages/blocks/src/store/private-selectors.js b/packages/blocks/src/store/private-selectors.js
index a50c82241dd12d..590422ccff99cc 100644
--- a/packages/blocks/src/store/private-selectors.js
+++ b/packages/blocks/src/store/private-selectors.js
@@ -14,6 +14,7 @@ const ROOT_BLOCK_SUPPORTS = [
'background',
'backgroundColor',
'color',
+ 'filter',
'linkColor',
'captionColor',
'buttonColor',
diff --git a/packages/blocks/src/store/test/private-selectors.js b/packages/blocks/src/store/test/private-selectors.js
index 8eca5c2e859ea1..7e31e7c3e87621 100644
--- a/packages/blocks/src/store/test/private-selectors.js
+++ b/packages/blocks/src/store/test/private-selectors.js
@@ -29,6 +29,7 @@ describe( 'private selectors', () => {
'background',
'backgroundColor',
'color',
+ 'filter',
'linkColor',
'captionColor',
'buttonColor',
@@ -51,6 +52,7 @@ describe( 'private selectors', () => {
'background',
'backgroundColor',
'color',
+ 'filter',
'linkColor',
'captionColor',
'buttonColor',
@@ -78,6 +80,7 @@ describe( 'private selectors', () => {
'background',
'backgroundColor',
'color',
+ 'filter',
'linkColor',
'captionColor',
'buttonColor',
diff --git a/packages/edit-site/src/components/global-styles/duotone-panel.js b/packages/edit-site/src/components/global-styles/duotone-panel.js
index c8cf095df4fe24..e1ce0c99fdfdcc 100644
--- a/packages/edit-site/src/components/global-styles/duotone-panel.js
+++ b/packages/edit-site/src/components/global-styles/duotone-panel.js
@@ -53,6 +53,11 @@ function DuotonePanel( { name } ) {
defaultSetting: 'color.defaultPalette',
} );
+ const disableCustomColors = ! useSetting( 'color.custom' );
+ const disableCustomDuotone =
+ ! useSetting( 'color.customDuotone' ) ||
+ ( colorPalette?.length === 0 && disableCustomColors );
+
if ( duotonePalette?.length === 0 ) {
return null;
}
@@ -68,8 +73,8 @@ function DuotonePanel( { name } ) {