From f085408b5d76fe27173b8a37c3267c822e28284c Mon Sep 17 00:00:00 2001
From: Jorge Costa
Date: Wed, 19 May 2021 18:48:00 +0100
Subject: [PATCH] Update: Use elements mechanism for link color instead of a
CSS variable (#31524)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: André (+3 squashed commits)
---
lib/block-supports/colors.php | 18 ---
lib/block-supports/elements.php | 65 ++++++++++
lib/class-wp-theme-json.php | 101 +---------------
lib/load.php | 1 +
packages/block-editor/src/hooks/color.js | 34 +++---
packages/block-editor/src/hooks/style.js | 76 +++++++++++-
.../block-editor/src/hooks/use-color-props.js | 2 +-
packages/blocks/src/api/constants.js | 16 ++-
packages/blocks/src/api/index.js | 5 +-
.../editor/global-styles-provider.js | 10 +-
.../editor/global-styles-renderer.js | 92 +++-----------
.../editor/test/global-styles-renderer.js | 9 +-
.../edit-site/src/components/editor/utils.js | 15 +--
.../src/components/sidebar/color-panel.js | 13 +-
phpunit/class-elements-test.php | 112 ++++++++++++++++++
.../class-wp-theme-json-schema-v0-test.php | 4 +-
phpunit/class-wp-theme-json-test.php | 42 +++----
17 files changed, 347 insertions(+), 268 deletions(-)
create mode 100644 lib/block-supports/elements.php
create mode 100644 phpunit/class-elements-test.php
diff --git a/lib/block-supports/colors.php b/lib/block-supports/colors.php
index 06c6649a1cd4dc..afdc3500d31645 100644
--- a/lib/block-supports/colors.php
+++ b/lib/block-supports/colors.php
@@ -99,24 +99,6 @@ function gutenberg_apply_colors_support( $block_type, $block_attributes ) {
}
}
- // Link Colors.
- if ( $has_link_colors_support ) {
- $has_link_color = isset( $block_attributes['style']['color']['link'] );
- // Apply required class and style.
- if ( $has_link_color ) {
- $classes[] = 'has-link-color';
- // If link is a named color.
- if ( strpos( $block_attributes['style']['color']['link'], 'var:preset|color|' ) !== false ) {
- // Get the name from the string and add proper styles.
- $index_to_splice = strrpos( $block_attributes['style']['color']['link'], '|' ) + 1;
- $link_color_name = substr( $block_attributes['style']['color']['link'], $index_to_splice );
- $styles[] = sprintf( '--wp--style--color--link: var(--wp--preset--color--%s);', $link_color_name );
- } else {
- $styles[] = sprintf( '--wp--style--color--link: %s;', $block_attributes['style']['color']['link'] );
- }
- }
- }
-
// Background colors.
if ( $has_background_colors_support ) {
$has_named_background_color = array_key_exists( 'backgroundColor', $block_attributes );
diff --git a/lib/block-supports/elements.php b/lib/block-supports/elements.php
new file mode 100644
index 00000000000000..a55bffe1c2e849
--- /dev/null
+++ b/lib/block-supports/elements.php
@@ -0,0 +1,65 @@
+get_registered( $block['blockName'] );
+ $link_color = _wp_array_get( $block['attrs'], array( 'style', 'elements', 'link', 'color', 'text' ), null );
+
+ /*
+ * For now we only care about link color.
+ * This code in the future when we have a public API
+ * should take advantage of WP_Theme_JSON::compute_style_properties
+ * and work for any element and style.
+ */
+ if ( null === $link_color ) {
+ return $block_content;
+ }
+
+ $class_name = 'wp-elements-' . uniqid();
+
+ if ( strpos( $link_color, 'var:preset|color|' ) !== false ) {
+ // Get the name from the string and add proper styles.
+ $index_to_splice = strrpos( $link_color, '|' ) + 1;
+ $link_color_name = substr( $link_color, $index_to_splice );
+ $link_color = "var(--wp--preset--color--$link_color_name)";
+ }
+ $link_color_declaration = esc_html( safecss_filter_attr( "color: $link_color" ) );
+
+ $style = "\n";
+
+ // Like the layout hook this assumes the hook only applies to blocks with a single wrapper.
+ // Retrieve the opening tag of the first HTML element.
+ $html_element_matches;
+ preg_match( '/<[^>]+>/', $block_content, $html_element_matches, PREG_OFFSET_CAPTURE );
+ $first_element = $html_element_matches[0][0];
+ // If the first HTML element has a class attribute just add the new class
+ // as we do on layout and duotone.
+ if ( strpos( $first_element, 'class="' ) !== false ) {
+ $content = preg_replace(
+ '/' . preg_quote( 'class="', '/' ) . '/',
+ 'class="' . $class_name . ' ',
+ $block_content,
+ 1
+ );
+ } else {
+ // If the first HTML element has no class attribute we should inject the attribute before the attribute at the end.
+ $first_element_offset = $html_element_matches[0][1];
+ $content = substr_replace( $block_content, ' class="' . $class_name . '"', $first_element_offset + strlen( $first_element ) - 1, 0 );
+ }
+ return $content . $style;
+
+}
+
+
+add_filter( 'render_block', 'gutenberg_render_elements_support', 10, 2 );
diff --git a/lib/class-wp-theme-json.php b/lib/class-wp-theme-json.php
index 60d3f2eaabae40..6734d439b10f9b 100644
--- a/lib/class-wp-theme-json.php
+++ b/lib/class-wp-theme-json.php
@@ -205,9 +205,6 @@ class WP_Theme_JSON {
* - 'value': path to the value in theme.json and block attributes.
*/
const PROPERTIES_METADATA = array(
- '--wp--style--color--link' => array(
- 'value' => array( 'color', 'link' ),
- ),
'background' => array(
'value' => array( 'color', 'gradient' ),
),
@@ -836,7 +833,7 @@ private function get_css_variables( $nodes ) {
* @return string The new stylesheet.
*/
private function get_block_styles( $style_nodes, $setting_nodes ) {
- $block_rules = self::ELEMENTS['link'] . '{color: var(--wp--style--color--link, #00e);}';
+ $block_rules = '';
foreach ( $style_nodes as $metadata ) {
if ( null === $metadata['selector'] ) {
continue;
@@ -845,53 +842,7 @@ private function get_block_styles( $style_nodes, $setting_nodes ) {
$node = _wp_array_get( $this->theme_json, $metadata['path'], array() );
$selector = $metadata['selector'];
$declarations = self::compute_style_properties( $node );
-
- $is_link_element = self::is_link_element( $metadata['selector'] );
- if ( ! $is_link_element ) {
- $block_rules .= self::to_ruleset( $selector, $declarations );
- } else {
- /*
- * To be removed when the user provided styles for link color
- * no longer use the --wp--style--link-color variable.
- *
- * We need to:
- *
- * 1. For the color property, output:
- *
- * $selector_without_the_link_element_selector {
- * --wp--style--color--link: value
- * }
- *
- * 2. For the rest of the properties:
- *
- * $selector {
- * other-prop: value;
- * other-prop: value;
- * }
- *
- * The reason for 1 is that user styles are attached to the block wrapper.
- * If 1 targets the a element is going to have higher specificity
- * and will overwrite the user preferences.
- *
- * Once the user styles are updated to output an `a` element instead
- * this can be removed.
- */
- $declarations_color = array();
- $declarations_other = array();
- foreach ( $declarations as $declaration ) {
- if ( 'color' === $declaration['name'] ) {
- $declarations_color[] = array(
- 'name' => '--wp--style--color--link',
- 'value' => $declaration['value'],
- );
- } else {
- $declarations_other[] = $declaration;
- }
- }
-
- $block_rules .= self::to_ruleset( $selector, $declarations_other );
- $block_rules .= self::to_ruleset( self::without_link_selector( $selector ), $declarations_color );
- }
+ $block_rules .= self::to_ruleset( $selector, $declarations );
}
$preset_rules = '';
@@ -1219,15 +1170,6 @@ private static function remove_insecure_settings( $input ) {
private static function remove_insecure_styles( $input, $selector ) {
$output = array();
$declarations = self::compute_style_properties( $input );
- // To be removed once the user styles
- // no longer use the --wp--style--color--link.
- if ( self::is_link_element( $selector ) ) {
- foreach ( $declarations as $index => $declaration ) {
- if ( 'color' === $declaration['name'] ) {
- $declarations[ $index ]['name'] = '--wp--style--color--link';
- }
- }
- }
foreach ( $declarations as $declaration ) {
if ( self::is_safe_css_declaration( $declaration['name'], $declaration['value'] ) ) {
@@ -1256,45 +1198,6 @@ private static function is_safe_css_declaration( $property_name, $property_value
return ! empty( trim( $filtered ) );
}
- /**
- * Whether the selector contains a link element.
- *
- * @param string $selector The selector to check.
- *
- * @return boolean
- */
- private static function is_link_element( $selector ) {
- if ( self::ELEMENTS['link'] === $selector ) {
- return true;
- }
-
- $result = true;
- if ( false === stripos( $selector, ' ' . self::ELEMENTS['link'] ) ) {
- $result = false;
- }
-
- return $result;
- }
-
- /**
- * Remove the link selector from the input.
- *
- * @param string $selector CSS selector to process.
- *
- * @return string
- */
- private static function without_link_selector( $selector ) {
- if ( self::ELEMENTS['link'] === $selector ) {
- return $selector;
- }
-
- // The selector consist of " ".
- // It can be compounded as well: " , , etc".
- //
- // We want to return "" or ", , etc".
- return str_ireplace( ' ' . self::ELEMENTS['link'], '', $selector );
- }
-
/**
* Removes insecure data from theme.json.
*/
diff --git a/lib/load.php b/lib/load.php
index 7b65711eefe017..440a1389f5fd99 100644
--- a/lib/load.php
+++ b/lib/load.php
@@ -123,6 +123,7 @@ function gutenberg_is_experiment_enabled( $name ) {
require __DIR__ . '/global-styles.php';
require __DIR__ . '/block-supports/generated-classname.php';
+require __DIR__ . '/block-supports/elements.php';
require __DIR__ . '/block-supports/colors.php';
require __DIR__ . '/block-supports/align.php';
require __DIR__ . '/block-supports/typography.php';
diff --git a/packages/block-editor/src/hooks/color.js b/packages/block-editor/src/hooks/color.js
index 288db9d69909a8..f8159240a20b88 100644
--- a/packages/block-editor/src/hooks/color.js
+++ b/packages/block-editor/src/hooks/color.js
@@ -2,7 +2,7 @@
* External dependencies
*/
import classnames from 'classnames';
-import { isObject } from 'lodash';
+import { isObject, setWith, clone } from 'lodash';
/**
* WordPress dependencies
@@ -162,7 +162,7 @@ export function addSaveProps( props, blockType, attributes ) {
backgroundColor ||
style?.color?.background ||
( hasGradient && ( gradient || style?.color?.gradient ) ),
- 'has-link-color': style?.color?.link,
+ 'has-link-color': style?.elements?.link?.color,
}
);
props.className = newClassName ? newClassName : undefined;
@@ -205,6 +205,10 @@ const getLinkColorFromAttributeValue = ( colors, value ) => {
return value;
};
+function immutableSet( object, path, value ) {
+ return setWith( object ? clone( object ) : {}, path, value, clone );
+}
+
/**
* Inspector control panel containing the color related configuration
*
@@ -303,17 +307,15 @@ export function ColorEdit( props ) {
const onChangeLinkColor = ( value ) => {
const colorObject = getColorObjectByColorValue( colors, value );
- props.setAttributes( {
- style: {
- ...props.attributes.style,
- color: {
- ...props.attributes.style?.color,
- link: colorObject?.slug
- ? `var:preset|color|${ colorObject.slug }`
- : value,
- },
- },
- } );
+ const newLinkColorValue = colorObject?.slug
+ ? `var:preset|color|${ colorObject.slug }`
+ : value;
+ const newStyle = immutableSet(
+ style,
+ [ 'elements', 'link', 'color', 'text' ],
+ newLinkColorValue
+ );
+ props.setAttributes( { style: newStyle } );
};
return (
@@ -363,10 +365,10 @@ export function ColorEdit( props ) {
onColorChange: onChangeLinkColor,
colorValue: getLinkColorFromAttributeValue(
colors,
- style?.color?.link
+ style?.elements?.link?.color?.text
),
- clearable: !! props.attributes.style?.color
- ?.link,
+ clearable: !! style?.elements?.link?.color
+ ?.text,
},
]
: [] ),
diff --git a/packages/block-editor/src/hooks/style.js b/packages/block-editor/src/hooks/style.js
index de7ac89d601cca..c0410a7c7b92f9 100644
--- a/packages/block-editor/src/hooks/style.js
+++ b/packages/block-editor/src/hooks/style.js
@@ -4,14 +4,19 @@
import {
capitalize,
find,
+ first,
forEach,
get,
has,
+ isEmpty,
isEqual,
+ kebabCase,
+ map,
omit,
startsWith,
without,
} from 'lodash';
+import classnames from 'classnames';
/**
* WordPress dependencies
@@ -21,8 +26,9 @@ import {
getBlockSupport,
hasBlockSupport,
__EXPERIMENTAL_STYLE_PROPERTY as STYLE_PROPERTY,
+ __EXPERIMENTAL_ELEMENTS as ELEMENTS,
} from '@wordpress/blocks';
-import { createHigherOrderComponent } from '@wordpress/compose';
+import { createHigherOrderComponent, useInstanceId } from '@wordpress/compose';
/**
* Internal dependencies
@@ -69,7 +75,8 @@ export function getInlineStyles( styles = {} ) {
Object.keys( STYLE_PROPERTY ).forEach( ( propKey ) => {
const path = STYLE_PROPERTY[ propKey ].value;
const subPaths = STYLE_PROPERTY[ propKey ].properties;
- if ( has( styles, path ) ) {
+ // Ignore styles on elements because they are handled on the server.
+ if ( has( styles, path ) && 'elements' !== first( path ) ) {
if ( !! subPaths ) {
subPaths.forEach( ( suffix ) => {
output[
@@ -85,6 +92,26 @@ export function getInlineStyles( styles = {} ) {
return output;
}
+function compileElementsStyles( selector, elements = {} ) {
+ return map( elements, ( styles, element ) => {
+ const elementStyles = getInlineStyles( styles );
+ if ( ! isEmpty( elementStyles ) ) {
+ return [
+ `.${ selector } ${ ELEMENTS[ element ] }{`,
+ ...map(
+ elementStyles,
+ ( value, property ) =>
+ `\t${ kebabCase( property ) }: ${ value }${
+ element === 'link' ? '!important' : ''
+ };`
+ ),
+ '}',
+ ].join( '\n' );
+ }
+ return '';
+ } ).join( '\n' );
+}
+
/**
* Filters registered block settings, extending attributes to include `style` attribute.
*
@@ -207,6 +234,45 @@ export const withBlockControls = createHigherOrderComponent(
'withToolbarControls'
);
+/**
+ * Override the default block element to include duotone styles.
+ *
+ * @param {Function} BlockListBlock Original component
+ * @return {Function} Wrapped component
+ */
+const withElementsStyles = createHigherOrderComponent(
+ ( BlockListBlock ) => ( props ) => {
+ const elements = props.attributes.style?.elements;
+ if ( ! elements ) {
+ return ;
+ }
+ const blockElementsContainerIdentifier = `wp-elements-${ useInstanceId(
+ BlockListBlock
+ ) }`;
+ const styles = compileElementsStyles(
+ blockElementsContainerIdentifier,
+ props.attributes.style?.elements
+ );
+
+ return (
+ <>
+
+
+ >
+ );
+ }
+);
+
addFilter(
'blocks.registerBlockType',
'core/style/addAttribute',
@@ -230,3 +296,9 @@ addFilter(
'core/style/with-block-controls',
withBlockControls
);
+
+addFilter(
+ 'editor.BlockListBlock',
+ 'core/editor/with-elements-styles',
+ withElementsStyles
+);
diff --git a/packages/block-editor/src/hooks/use-color-props.js b/packages/block-editor/src/hooks/use-color-props.js
index e10f8a171ee500..768a166c376518 100644
--- a/packages/block-editor/src/hooks/use-color-props.js
+++ b/packages/block-editor/src/hooks/use-color-props.js
@@ -56,7 +56,7 @@ export function getColorClassesAndStyles( attributes ) {
style?.color?.background ||
gradient ||
style?.color?.gradient,
- 'has-link-color': style?.color?.link,
+ 'has-link-color': style?.elements?.link?.color,
} );
// Collect inline styles for colors.
diff --git a/packages/blocks/src/api/constants.js b/packages/blocks/src/api/constants.js
index badbc94320f001..33c017933ad76c 100644
--- a/packages/blocks/src/api/constants.js
+++ b/packages/blocks/src/api/constants.js
@@ -13,8 +13,8 @@ export const DEPRECATED_ENTRY_KEYS = [
];
export const __EXPERIMENTAL_STYLE_PROPERTY = {
+ //kept for back-compatibility purposes.
'--wp--style--color--link': {
- valueGlobal: [ 'elements', 'link', 'color', 'text' ],
value: [ 'color', 'link' ],
support: [ 'color', 'link' ],
},
@@ -46,6 +46,10 @@ export const __EXPERIMENTAL_STYLE_PROPERTY = {
value: [ 'color', 'text' ],
support: [ 'color' ],
},
+ linkColor: {
+ value: [ 'elements', 'link', 'color', 'text' ],
+ support: [ 'color', 'link' ],
+ },
fontFamily: {
value: [ 'typography', 'fontFamily' ],
support: [ '__experimentalFontFamily' ],
@@ -85,3 +89,13 @@ export const __EXPERIMENTAL_STYLE_PROPERTY = {
support: [ '__experimentalTextTransform' ],
},
};
+
+export const __EXPERIMENTAL_ELEMENTS = {
+ link: 'a',
+ h1: 'h1',
+ h2: 'h2',
+ h3: 'h3',
+ h4: 'h4',
+ h5: 'h5',
+ h6: 'h6',
+};
diff --git a/packages/blocks/src/api/index.js b/packages/blocks/src/api/index.js
index 4d535589b09ab0..073fee3396cc98 100644
--- a/packages/blocks/src/api/index.js
+++ b/packages/blocks/src/api/index.js
@@ -157,4 +157,7 @@ export {
} from './templates';
export { default as children } from './children';
export { default as node } from './node';
-export { __EXPERIMENTAL_STYLE_PROPERTY } from './constants';
+export {
+ __EXPERIMENTAL_STYLE_PROPERTY,
+ __EXPERIMENTAL_ELEMENTS,
+} from './constants';
diff --git a/packages/edit-site/src/components/editor/global-styles-provider.js b/packages/edit-site/src/components/editor/global-styles-provider.js
index 38e39cfea986f7..7fdf78b060a829 100644
--- a/packages/edit-site/src/components/editor/global-styles-provider.js
+++ b/packages/edit-site/src/components/editor/global-styles-provider.js
@@ -15,6 +15,7 @@ import {
} from '@wordpress/element';
import {
__EXPERIMENTAL_STYLE_PROPERTY as STYLE_PROPERTY,
+ __EXPERIMENTAL_ELEMENTS as ELEMENTS,
store as blocksStore,
} from '@wordpress/blocks';
import { useEntityProp } from '@wordpress/core-data';
@@ -24,7 +25,6 @@ import { useSelect, useDispatch } from '@wordpress/data';
* Internal dependencies
*/
import {
- ELEMENTS,
ROOT_BLOCK_NAME,
ROOT_BLOCK_SELECTOR,
ROOT_BLOCK_SUPPORTS,
@@ -198,9 +198,7 @@ export default function GlobalStylesProvider( { children, baseStyles } ) {
setContent( JSON.stringify( newContent ) );
},
getStyle: ( context, propertyName, origin = 'merged' ) => {
- const propertyPath =
- STYLE_PROPERTY[ propertyName ].valueGlobal ??
- STYLE_PROPERTY[ propertyName ].value;
+ const propertyPath = STYLE_PROPERTY[ propertyName ].value;
const path =
context === ROOT_BLOCK_NAME
? propertyPath
@@ -230,9 +228,7 @@ export default function GlobalStylesProvider( { children, baseStyles } ) {
ROOT_BLOCK_NAME === context
? [ 'styles' ]
: [ 'styles', 'blocks', context ];
- const propertyPath =
- STYLE_PROPERTY[ propertyName ].valueGlobal ??
- STYLE_PROPERTY[ propertyName ].value;
+ const propertyPath = STYLE_PROPERTY[ propertyName ].value;
let newStyles = get( newContent, path );
if ( ! newStyles ) {
diff --git a/packages/edit-site/src/components/editor/global-styles-renderer.js b/packages/edit-site/src/components/editor/global-styles-renderer.js
index 1a137986155ed3..70c284535cf9ae 100644
--- a/packages/edit-site/src/components/editor/global-styles-renderer.js
+++ b/packages/edit-site/src/components/editor/global-styles-renderer.js
@@ -3,6 +3,7 @@
*/
import {
capitalize,
+ first,
forEach,
get,
isEmpty,
@@ -16,12 +17,15 @@ import {
/**
* WordPress dependencies
*/
-import { __EXPERIMENTAL_STYLE_PROPERTY as STYLE_PROPERTY } from '@wordpress/blocks';
+import {
+ __EXPERIMENTAL_STYLE_PROPERTY as STYLE_PROPERTY,
+ __EXPERIMENTAL_ELEMENTS as ELEMENTS,
+} from '@wordpress/blocks';
/**
* Internal dependencies
*/
-import { PRESET_METADATA, ROOT_BLOCK_SELECTOR, ELEMENTS } from './utils';
+import { PRESET_METADATA, ROOT_BLOCK_SELECTOR } from './utils';
function compileStyleValue( uncompiledValue ) {
const VARIABLE_REFERENCE_PREFIX = 'var:';
@@ -116,8 +120,11 @@ function flattenTree( input = {}, prefix, token ) {
function getStylesDeclarations( blockStyles = {} ) {
return reduce(
STYLE_PROPERTY,
- ( declarations, { value, valueGlobal, properties }, key ) => {
- const pathToValue = valueGlobal ?? value;
+ ( declarations, { value, properties }, key ) => {
+ const pathToValue = value;
+ if ( first( pathToValue ) === 'elements' ) {
+ return declarations;
+ }
if ( !! properties ) {
properties.forEach( ( prop ) => {
if (
@@ -127,9 +134,9 @@ function getStylesDeclarations( blockStyles = {} ) {
// for sub-properties that don't have any value.
return;
}
- const cssProperty = key.startsWith( '--' )
- ? key
- : kebabCase( `${ key }${ capitalize( prop ) }` );
+ const cssProperty = kebabCase(
+ `${ key }${ capitalize( prop ) }`
+ );
declarations.push(
`${ cssProperty }: ${ compileStyleValue(
get( blockStyles, [ ...pathToValue, prop ] )
@@ -271,85 +278,18 @@ export const toCustomProperties = ( tree, blockSelectors ) => {
return ruleset;
};
-const containsLinkElement = ( selector ) =>
- selector.toLowerCase().includes( ELEMENTS.link );
-const withoutLinkSelector = ( selector ) => {
- const newSelector = selector
- .split( ',' )
- .map( ( individualSelector ) =>
- individualSelector.replace( ELEMENTS.link, '' ).trim()
- )
- .join( ',' );
-
- if ( '' === newSelector ) {
- return ROOT_BLOCK_SELECTOR;
- }
-
- return newSelector;
-};
-
export const toStyles = ( tree, blockSelectors ) => {
const nodesWithStyles = getNodesWithStyles( tree, blockSelectors );
const nodesWithSettings = getNodesWithSettings( tree, blockSelectors );
- let ruleset = `${ ELEMENTS.link }{color: var(--wp--style--color--link, #00e);}`;
+ let ruleset = '';
nodesWithStyles.forEach( ( { selector, styles } ) => {
const declarations = getStylesDeclarations( styles );
if ( declarations.length === 0 ) {
return;
}
-
- if ( ! containsLinkElement( selector ) ) {
- ruleset = ruleset + `${ selector }{${ declarations.join( ';' ) };}`;
- } else {
- // To be removed when the user provided styles for link color
- // no longer use the --wp--style--link-color variable.
- //
- // We need to:
- //
- // 1. For the color property, output:
- //
- // $selector_without_the_link_element_selector {
- // --wp--style--color--link: value
- // }
- //
- // 2. For the rest of the properties:
- //
- // $selector {
- // other-prop: value;
- // other-prop: value;
- // }
- //
- // The reason for 1 is that user styles are attached to the block wrapper.
- // If 1 targets the a element is going to have higher specificity
- // and will overwrite the user preferences.
- //
- // Once the user styles are updated to output an `a` element instead
- // this can be removed.
-
- const declarationsColor = declarations.filter(
- ( declaration ) => declaration.split( ':' )[ 0 ] === 'color'
- );
- const declarationsOther = declarations.filter(
- ( declaration ) => declaration.split( ':' )[ 0 ] !== 'color'
- );
-
- if ( declarationsOther.length > 0 ) {
- ruleset =
- ruleset +
- `${ selector }{${ declarationsOther.join( ';' ) };}`;
- }
-
- if ( declarationsColor.length === 1 ) {
- const value = declarationsColor[ 0 ].split( ':' )[ 1 ];
- ruleset =
- ruleset +
- `${ withoutLinkSelector(
- selector
- ) }{--wp--style--color--link:${ value };}`;
- }
- }
+ ruleset = ruleset + `${ selector }{${ declarations.join( ';' ) };}`;
} );
nodesWithSettings.forEach( ( { selector, presets } ) => {
diff --git a/packages/edit-site/src/components/editor/test/global-styles-renderer.js b/packages/edit-site/src/components/editor/test/global-styles-renderer.js
index b92aa7792c17b4..9d0f510c4be592 100644
--- a/packages/edit-site/src/components/editor/test/global-styles-renderer.js
+++ b/packages/edit-site/src/components/editor/test/global-styles-renderer.js
@@ -1,3 +1,8 @@
+/**
+ * WordPress dependencies
+ */
+import { __EXPERIMENTAL_ELEMENTS as ELEMENTS } from '@wordpress/blocks';
+
/**
* Internal dependencies
*/
@@ -7,7 +12,7 @@ import {
toCustomProperties,
toStyles,
} from '../global-styles-renderer';
-import { ELEMENTS, ROOT_BLOCK_SELECTOR } from '../utils';
+import { ROOT_BLOCK_SELECTOR } from '../utils';
describe( 'global styles renderer', () => {
describe( 'getNodesWithStyles', () => {
@@ -315,7 +320,7 @@ describe( 'global styles renderer', () => {
};
expect( toStyles( tree, blockSelectors ) ).toEqual(
- 'a{color: var(--wp--style--color--link, #00e);}body{background-color: red;}h1{font-size: 42px;}h1,h2,h3,h4,h5,h6{color: orange;}h1,h2,h3,h4,h5,h6{--wp--style--color--link: hotpink;}.has-white-color{color: white !important;}.has-white-background-color{background-color: white !important;}.has-white-border-color{border-color: white !important;}.has-black-color{color: black !important;}.has-black-background-color{background-color: black !important;}.has-black-border-color{border-color: black !important;}'
+ 'body{background-color: red;}h1{font-size: 42px;}h1,h2,h3,h4,h5,h6{color: orange;}h1 a,h2 a,h3 a,h4 a,h5 a,h6 a{color: hotpink;}.has-white-color{color: white !important;}.has-white-background-color{background-color: white !important;}.has-white-border-color{border-color: white !important;}.has-black-color{color: black !important;}.has-black-background-color{background-color: black !important;}.has-black-border-color{border-color: black !important;}'
);
} );
} );
diff --git a/packages/edit-site/src/components/editor/utils.js b/packages/edit-site/src/components/editor/utils.js
index 6973aefe1ec36b..865279b4625ba4 100644
--- a/packages/edit-site/src/components/editor/utils.js
+++ b/packages/edit-site/src/components/editor/utils.js
@@ -15,10 +15,10 @@ import { store as editSiteStore } from '../../store';
export const ROOT_BLOCK_NAME = 'root';
export const ROOT_BLOCK_SELECTOR = 'body';
export const ROOT_BLOCK_SUPPORTS = [
- '--wp--style--color--link',
'background',
'backgroundColor',
'color',
+ 'linkColor',
'fontFamily',
'fontSize',
'fontStyle',
@@ -27,15 +27,6 @@ export const ROOT_BLOCK_SUPPORTS = [
'textDecoration',
'textTransform',
];
-export const ELEMENTS = {
- link: 'a',
- h1: 'h1',
- h2: 'h2',
- h3: 'h3',
- h4: 'h4',
- h5: 'h5',
- h6: 'h6',
-};
export const PRESET_METADATA = [
{
@@ -81,7 +72,6 @@ export const PRESET_METADATA = [
const STYLE_PROPERTIES_TO_CSS_VAR_INFIX = {
backgroundColor: 'color',
- LINK_COLOR: 'color',
background: 'gradient',
};
@@ -100,9 +90,6 @@ function getPresetMetadataFromStyleProperty( styleProperty ) {
return getPresetMetadataFromStyleProperty.MAP[ styleProperty ];
}
-export const LINK_COLOR = '--wp--style--color--link';
-export const LINK_COLOR_DECLARATION = `a { color: var(${ LINK_COLOR }, #00e); }`;
-
export function useSetting( path, blockName = '' ) {
const settings = useSelect( ( select ) => {
return select( editSiteStore ).getSettings();
diff --git a/packages/edit-site/src/components/sidebar/color-panel.js b/packages/edit-site/src/components/sidebar/color-panel.js
index 07c24f5f964da2..5dfdc2268f570d 100644
--- a/packages/edit-site/src/components/sidebar/color-panel.js
+++ b/packages/edit-site/src/components/sidebar/color-panel.js
@@ -7,7 +7,8 @@ import { __ } from '@wordpress/i18n';
/**
* Internal dependencies
*/
-import { LINK_COLOR, useSetting } from '../editor/utils';
+
+import { useSetting } from '../editor/utils';
import ColorPalettePanel from './color-palette-panel';
export function useHasColorPanel( { supports } ) {
@@ -15,7 +16,7 @@ export function useHasColorPanel( { supports } ) {
supports.includes( 'color' ) ||
supports.includes( 'backgroundColor' ) ||
supports.includes( 'background' ) ||
- supports.includes( LINK_COLOR )
+ supports.includes( 'linkColor' )
);
}
@@ -84,12 +85,12 @@ export default function ColorPanel( {
} );
}
- if ( supports.includes( LINK_COLOR ) ) {
- const color = getStyle( name, LINK_COLOR );
- const userColor = getStyle( name, LINK_COLOR, 'user' );
+ if ( supports.includes( 'linkColor' ) ) {
+ const color = getStyle( name, 'linkColor' );
+ const userColor = getStyle( name, 'linkColor', 'user' );
settings.push( {
colorValue: color,
- onColorChange: ( value ) => setStyle( name, LINK_COLOR, value ),
+ onColorChange: ( value ) => setStyle( name, 'linkColor', value ),
label: __( 'Link color' ),
clearable: color === userColor,
} );
diff --git a/phpunit/class-elements-test.php b/phpunit/class-elements-test.php
new file mode 100644
index 00000000000000..e9df79ff08a9d6
--- /dev/null
+++ b/phpunit/class-elements-test.php
@@ -0,0 +1,112 @@
+Hello WordPress!
',
+ array(
+ 'blockName' => 'core/paragraph',
+ 'attrs' => array(
+ 'style' => array(
+ 'elements' => array(
+ 'link' => array(
+ 'color' => array(
+ 'text' => 'var:preset|color|subtle-background',
+ ),
+ ),
+ ),
+ ),
+ ),
+ )
+ )
+ );
+ $this->assertSame(
+ $result,
+ 'Hello WordPress!
' . "\n"
+ );
+ }
+
+ /**
+ * Test gutenberg_render_elements_support() with a paragraph containing a class.
+ */
+ public function test_class_paragraph_link_color() {
+ $result = self::make_unique_id_one(
+ gutenberg_render_elements_support(
+ 'Hello WordPress!
',
+ array(
+ 'blockName' => 'core/paragraph',
+ 'attrs' => array(
+ 'style' => array(
+ 'elements' => array(
+ 'link' => array(
+ 'color' => array(
+ 'text' => 'red',
+ ),
+ ),
+ ),
+ ),
+ 'backgroundColor' => 'dark-gray',
+ ),
+ )
+ )
+ );
+ $this->assertSame(
+ $result,
+ 'Hello WordPress!
' . "\n"
+ );
+ }
+
+ /**
+ * Test gutenberg_render_elements_support() with a paragraph containing a anchor.
+ */
+ public function test_anchor_paragraph_link_color() {
+ $result = self::make_unique_id_one(
+ gutenberg_render_elements_support(
+ 'Hello WordPress!
',
+ array(
+ 'blockName' => 'core/paragraph',
+ 'attrs' => array(
+ 'style' => array(
+ 'elements' => array(
+ 'link' => array(
+ 'color' => array(
+ 'text' => '#fff000',
+ ),
+ ),
+ ),
+ ),
+ ),
+ )
+ )
+ );
+ $this->assertSame(
+ $result,
+ 'Hello WordPress!
' . "\n"
+ );
+ }
+}
diff --git a/phpunit/class-wp-theme-json-schema-v0-test.php b/phpunit/class-wp-theme-json-schema-v0-test.php
index 624d5856b74fc7..7e79e598d60e72 100644
--- a/phpunit/class-wp-theme-json-schema-v0-test.php
+++ b/phpunit/class-wp-theme-json-schema-v0-test.php
@@ -461,11 +461,11 @@ function test_get_stylesheet() {
);
$this->assertEquals(
- 'body{--wp--preset--color--grey: grey;--wp--preset--font-family--small: 14px;--wp--preset--font-family--big: 41px;}.wp-block-group{--wp--custom--base-font: 16;--wp--custom--line-height--small: 1.2;--wp--custom--line-height--medium: 1.4;--wp--custom--line-height--large: 1.8;}a{color: var(--wp--style--color--link, #00e);}body{color: var(--wp--preset--color--grey);}a{--wp--style--color--link: #111;}h1{font-size: 1em;}h2{font-size: 2em;}.wp-block-group{padding-top: 12px;padding-bottom: 24px;}.wp-block-group{--wp--style--color--link: #333;}h1,h2,h3,h4,h5,h6{--wp--style--color--link: #222;}.wp-block-post-title{font-size: 5em;}.wp-block-post-title{--wp--style--color--link: #555;}.wp-block-query-title{font-size: 5em;}.wp-block-query-title{--wp--style--color--link: #555;}.has-grey-color{color: grey !important;}.has-grey-background-color{background-color: grey !important;}.has-grey-border-color{border-color: grey !important;}',
+ 'body{--wp--preset--color--grey: grey;--wp--preset--font-family--small: 14px;--wp--preset--font-family--big: 41px;}.wp-block-group{--wp--custom--base-font: 16;--wp--custom--line-height--small: 1.2;--wp--custom--line-height--medium: 1.4;--wp--custom--line-height--large: 1.8;}body{color: var(--wp--preset--color--grey);}a{color: #111;}h1{font-size: 1em;}h2{font-size: 2em;}.wp-block-group{padding-top: 12px;padding-bottom: 24px;}.wp-block-group a{color: #333;}h1 a,h2 a,h3 a,h4 a,h5 a,h6 a{color: #222;}.wp-block-post-title{font-size: 5em;}.wp-block-post-title a{color: #555;}.wp-block-query-title{font-size: 5em;}.wp-block-query-title a{color: #555;}.has-grey-color{color: grey !important;}.has-grey-background-color{background-color: grey !important;}.has-grey-border-color{border-color: grey !important;}',
$theme_json->get_stylesheet()
);
$this->assertEquals(
- 'a{color: var(--wp--style--color--link, #00e);}body{color: var(--wp--preset--color--grey);}a{--wp--style--color--link: #111;}h1{font-size: 1em;}h2{font-size: 2em;}.wp-block-group{padding-top: 12px;padding-bottom: 24px;}.wp-block-group{--wp--style--color--link: #333;}h1,h2,h3,h4,h5,h6{--wp--style--color--link: #222;}.wp-block-post-title{font-size: 5em;}.wp-block-post-title{--wp--style--color--link: #555;}.wp-block-query-title{font-size: 5em;}.wp-block-query-title{--wp--style--color--link: #555;}.has-grey-color{color: grey !important;}.has-grey-background-color{background-color: grey !important;}.has-grey-border-color{border-color: grey !important;}',
+ 'body{color: var(--wp--preset--color--grey);}a{color: #111;}h1{font-size: 1em;}h2{font-size: 2em;}.wp-block-group{padding-top: 12px;padding-bottom: 24px;}.wp-block-group a{color: #333;}h1 a,h2 a,h3 a,h4 a,h5 a,h6 a{color: #222;}.wp-block-post-title{font-size: 5em;}.wp-block-post-title a{color: #555;}.wp-block-query-title{font-size: 5em;}.wp-block-query-title a{color: #555;}.has-grey-color{color: grey !important;}.has-grey-background-color{background-color: grey !important;}.has-grey-border-color{border-color: grey !important;}',
$theme_json->get_stylesheet( 'block_styles' )
);
$this->assertEquals(
diff --git a/phpunit/class-wp-theme-json-test.php b/phpunit/class-wp-theme-json-test.php
index 81ab493724332d..5b6d02059829f2 100644
--- a/phpunit/class-wp-theme-json-test.php
+++ b/phpunit/class-wp-theme-json-test.php
@@ -27,8 +27,12 @@ function test_get_settings() {
),
),
'styles' => array(
- 'color' => array(
- 'link' => 'blue',
+ 'elements' => array(
+ 'link' => array(
+ 'color' => array(
+ 'text' => '#111',
+ ),
+ ),
),
),
)
@@ -157,11 +161,11 @@ function test_get_stylesheet() {
);
$this->assertEquals(
- 'body{--wp--preset--color--grey: grey;--wp--preset--font-family--small: 14px;--wp--preset--font-family--big: 41px;}.wp-block-group{--wp--custom--base-font: 16;--wp--custom--line-height--small: 1.2;--wp--custom--line-height--medium: 1.4;--wp--custom--line-height--large: 1.8;}a{color: var(--wp--style--color--link, #00e);}body{color: var(--wp--preset--color--grey);}a{background-color: #333;}a{--wp--style--color--link: #111;}.wp-block-group{padding-top: 12px;padding-bottom: 24px;}.wp-block-group{--wp--style--color--link: #111;}h1,h2,h3,h4,h5,h6{color: #123456;}h1 a,h2 a,h3 a,h4 a,h5 a,h6 a{background-color: #333;font-size: 60px;}h1,h2,h3,h4,h5,h6{--wp--style--color--link: #111;}.wp-block-post-date{color: #123456;}.wp-block-post-date a{background-color: #777;}.wp-block-post-date{--wp--style--color--link: #555;}.has-grey-color{color: grey !important;}.has-grey-background-color{background-color: grey !important;}.has-grey-border-color{border-color: grey !important;}',
+ 'body{--wp--preset--color--grey: grey;--wp--preset--font-family--small: 14px;--wp--preset--font-family--big: 41px;}.wp-block-group{--wp--custom--base-font: 16;--wp--custom--line-height--small: 1.2;--wp--custom--line-height--medium: 1.4;--wp--custom--line-height--large: 1.8;}body{color: var(--wp--preset--color--grey);}a{background-color: #333;color: #111;}.wp-block-group{padding-top: 12px;padding-bottom: 24px;}.wp-block-group a{color: #111;}h1,h2,h3,h4,h5,h6{color: #123456;}h1 a,h2 a,h3 a,h4 a,h5 a,h6 a{background-color: #333;color: #111;font-size: 60px;}.wp-block-post-date{color: #123456;}.wp-block-post-date a{background-color: #777;color: #555;}.has-grey-color{color: grey !important;}.has-grey-background-color{background-color: grey !important;}.has-grey-border-color{border-color: grey !important;}',
$theme_json->get_stylesheet()
);
$this->assertEquals(
- 'a{color: var(--wp--style--color--link, #00e);}body{color: var(--wp--preset--color--grey);}a{background-color: #333;}a{--wp--style--color--link: #111;}.wp-block-group{padding-top: 12px;padding-bottom: 24px;}.wp-block-group{--wp--style--color--link: #111;}h1,h2,h3,h4,h5,h6{color: #123456;}h1 a,h2 a,h3 a,h4 a,h5 a,h6 a{background-color: #333;font-size: 60px;}h1,h2,h3,h4,h5,h6{--wp--style--color--link: #111;}.wp-block-post-date{color: #123456;}.wp-block-post-date a{background-color: #777;}.wp-block-post-date{--wp--style--color--link: #555;}.has-grey-color{color: grey !important;}.has-grey-background-color{background-color: grey !important;}.has-grey-border-color{border-color: grey !important;}',
+ 'body{color: var(--wp--preset--color--grey);}a{background-color: #333;color: #111;}.wp-block-group{padding-top: 12px;padding-bottom: 24px;}.wp-block-group a{color: #111;}h1,h2,h3,h4,h5,h6{color: #123456;}h1 a,h2 a,h3 a,h4 a,h5 a,h6 a{background-color: #333;color: #111;font-size: 60px;}.wp-block-post-date{color: #123456;}.wp-block-post-date a{background-color: #777;color: #555;}.has-grey-color{color: grey !important;}.has-grey-background-color{background-color: grey !important;}.has-grey-border-color{border-color: grey !important;}',
$theme_json->get_stylesheet( 'block_styles' )
);
$this->assertEquals(
@@ -201,11 +205,11 @@ function test_get_stylesheet_preset_rules_come_after_block_rules() {
);
$this->assertEquals(
- '.wp-block-group{--wp--preset--color--grey: grey;}a{color: var(--wp--style--color--link, #00e);}.wp-block-group{color: red;}.wp-block-group.has-grey-color{color: grey !important;}.wp-block-group.has-grey-background-color{background-color: grey !important;}.wp-block-group.has-grey-border-color{border-color: grey !important;}',
+ '.wp-block-group{--wp--preset--color--grey: grey;}.wp-block-group{color: red;}.wp-block-group.has-grey-color{color: grey !important;}.wp-block-group.has-grey-background-color{background-color: grey !important;}.wp-block-group.has-grey-border-color{border-color: grey !important;}',
$theme_json->get_stylesheet()
);
$this->assertEquals(
- 'a{color: var(--wp--style--color--link, #00e);}.wp-block-group{color: red;}.wp-block-group.has-grey-color{color: grey !important;}.wp-block-group.has-grey-background-color{background-color: grey !important;}.wp-block-group.has-grey-border-color{border-color: grey !important;}',
+ '.wp-block-group{color: red;}.wp-block-group.has-grey-color{color: grey !important;}.wp-block-group.has-grey-background-color{background-color: grey !important;}.wp-block-group.has-grey-border-color{border-color: grey !important;}',
$theme_json->get_stylesheet( 'block_styles' )
);
}
@@ -242,7 +246,7 @@ public function test_get_stylesheet_preset_values_are_marked_as_important() {
);
$this->assertEquals(
- 'body{--wp--preset--color--grey: grey;}a{color: var(--wp--style--color--link, #00e);}p{background-color: blue;color: red;font-size: 12px;line-height: 1.3;}.has-grey-color{color: grey !important;}.has-grey-background-color{background-color: grey !important;}.has-grey-border-color{border-color: grey !important;}',
+ 'body{--wp--preset--color--grey: grey;}p{background-color: blue;color: red;font-size: 12px;line-height: 1.3;}.has-grey-color{color: grey !important;}.has-grey-background-color{background-color: grey !important;}.has-grey-border-color{border-color: grey !important;}',
$theme_json->get_stylesheet()
);
}
@@ -535,11 +539,7 @@ function test_remove_insecure_properties_removes_unsafe_styles() {
'elements' => array(
'link' => array(
'color' => array(
- // We should also allow links but at the moment we don't
- // because they're transformed to --wp--style--color-link
- // due to how user styles work.
- // 'text' => 'var:preset|color|dark-pink',
- // .
+ 'text' => 'var:preset|color|dark-pink',
'background' => 'var:preset|color|dark-red',
),
),
@@ -549,17 +549,13 @@ function test_remove_insecure_properties_removes_unsafe_styles() {
'color' => array(
'text' => 'var:preset|color|dark-gray',
),
- // We should also allow links but at the moment we don't
- // because they're transformed to --wp--style--color-link
- // due to how user styles work.
- // 'elements' => array(
- // 'link' => array(
- // 'color' => array(
- // 'text' => 'var:preset|color|dark-pink',
- // ),
- // ),
- // ),
- // .
+ 'elements' => array(
+ 'link' => array(
+ 'color' => array(
+ 'text' => 'var:preset|color|dark-pink',
+ ),
+ ),
+ ),
),
),
),