Skip to content
42 changes: 36 additions & 6 deletions lib/block-supports/elements.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ function gutenberg_get_elements_class_name( $block ) {
return 'wp-elements-' . md5( serialize( $block ) );
}



/**
* Update the block content with elements class names.
*
Expand All @@ -29,23 +31,29 @@ function gutenberg_render_elements_support( $block_content, $block ) {

$block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] );
$skip_link_color_serialization = gutenberg_should_skip_block_supports_serialization( $block_type, 'color', 'link' );
$skip_link_hover_color_serialization = gutenberg_should_skip_block_supports_serialization( $block_type, 'color', 'link:hover' );

if ( $skip_link_color_serialization ) {
if ( $skip_link_color_serialization && $skip_link_hover_color_serialization ) {
return $block_content;
}

$link_color = null;
if ( ! empty( $block['attrs'] ) ) {
$link_color = _wp_array_get( $block['attrs'], array( 'style', 'elements', 'link', 'color', 'text' ), null );
}

$link_hover_color = null;
if ( ! empty( $block['attrs'] ) ) {
$link_hover_color = _wp_array_get( $block['attrs'], array( 'style', 'elements', 'link:hover', 'color', 'text' ), null );
}

/*
* For now we only care about link color.
* For now we only care about link color and link hover color.
* This code in the future when we have a public API
* should take advantage of WP_Theme_JSON_Gutenberg::compute_style_properties
* and work for any element and style.
*/
if ( null === $link_color ) {
if ( null === $link_color && null === $link_hover_color ) {
return $block_content;
}

Expand Down Expand Up @@ -75,7 +83,11 @@ function gutenberg_render_elements_support( $block_content, $block ) {
}

/**
* Render the elements stylesheet.
* Renders the elements stylesheet for a given block type.
*
* Each block may have styles for it's child HTML elements (currently only a limited subset are supported).
* Render an inline <style> block scoped to the block's CSS selector containing all the `element` style
* rules for the block.
*
* In the case of nested blocks we want the parent element styles to be rendered before their descendants.
* This solves the issue of an element (e.g.: link color) being styled in both the parent and a descendant:
Expand All @@ -90,15 +102,17 @@ function gutenberg_render_elements_support_styles( $pre_render, $block ) {
$block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] );
$element_block_styles = isset( $block['attrs']['style']['elements'] ) ? $block['attrs']['style']['elements'] : null;


/*
* For now we only care about link color.
* For now we only care about link color and link hover color.
* This code in the future when we have a public API
* should take advantage of WP_Theme_JSON_Gutenberg::compute_style_properties
* and work for any element and style.
*/
$skip_link_color_serialization = gutenberg_should_skip_block_supports_serialization( $block_type, 'color', 'link' );
$skip_link_hover_color_serialization = gutenberg_should_skip_block_supports_serialization( $block_type, 'color', 'link:hover' );

if ( $skip_link_color_serialization ) {
if ( $skip_link_color_serialization && $skip_link_hover_color_serialization) {
return null;
}
$class_name = gutenberg_get_elements_class_name( $block );
Expand All @@ -117,6 +131,22 @@ function gutenberg_render_elements_support_styles( $pre_render, $block ) {
gutenberg_enqueue_block_support_styles( $styles['css'] );
}
}

$link_hover_block_styles = isset( $element_block_styles['link:hover'] ) ? $element_block_styles['link:hover'] : null;

if ( $link_hover_block_styles ) {
$styles = gutenberg_style_engine_generate(
$link_hover_block_styles,
array(
'selector' => ".$class_name a:hover",
'css_vars' => true,
)
);

if ( ! empty( $styles['css'] ) ) {
gutenberg_enqueue_block_support_styles( $styles['css'] );
}
}

return null;
}
Expand Down
7 changes: 7 additions & 0 deletions lib/compat/wordpress-6.1/class-wp-theme-json-6-1.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,15 @@
class WP_Theme_JSON_6_1 extends WP_Theme_JSON_6_0 {
const __EXPERIMENTAL_ELEMENT_BUTTON_CLASS_NAME = 'wp-element-button';

/**
* The valid elements that can be found under styles.
*
* @since 5.8.0
* @var string[]
*/
const ELEMENTS = array(
'link' => 'a',
'link:hover' => 'a:hover',
'h1' => 'h1',
'h2' => 'h2',
'h3' => 'h3',
Expand Down
46 changes: 45 additions & 1 deletion packages/block-editor/src/hooks/color.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,13 @@ const resetAllLinkFilter = ( attributes ) => ( {
),
} );

const resetAllLinkHoverFilter = ( attributes ) => ( {
style: clearColorFromStyles(
[ 'elements', 'link:hover', 'color', 'text' ],
attributes.style
),
} );

/**
* Clears all background color related properties including gradients from
* supplied block attributes.
Expand Down Expand Up @@ -216,7 +223,6 @@ export function addSaveProps( props, blockType, attributes ) {
backgroundColor ||
style?.color?.background ||
( hasGradient && ( gradient || style?.color?.gradient ) );

const newClassName = classnames(
props.className,
textClass,
Expand All @@ -232,6 +238,9 @@ export function addSaveProps( props, blockType, attributes ) {
'has-background': serializeHasBackground && hasBackground,
'has-link-color':
shouldSerialize( 'link' ) && style?.elements?.link?.color,
'has-link-hover-color':
shouldSerialize( 'link:hover' ) &&
style?.elements?.[ 'link:hover' ]?.color,
}
);
props.className = newClassName ? newClassName : undefined;
Expand Down Expand Up @@ -284,6 +293,7 @@ const getLinkColorFromAttributeValue = ( colors, value ) => {
*/
export function ColorEdit( props ) {
const { name: blockName, attributes } = props;

// Some color settings have a special handling for deprecated flags in `useSetting`,
// so we can't unwrap them by doing const { ... } = useSetting('color')
// until https://github.com/WordPress/gutenberg/issues/37094 is fixed.
Expand Down Expand Up @@ -443,6 +453,26 @@ export function ColorEdit( props ) {
};
};

const onChangeLinkHoverColor = ( value ) => {
const colorObject = getColorObjectByColorValue( allSolids, value );
const newLinkColorValue = colorObject?.slug
? `var:preset|color|${ colorObject.slug }`
: value;

const newStyle = cleanEmptyObject(
immutableSet(
localAttributes.current?.style,
[ 'elements', 'link:hover', 'color', 'text' ],
newLinkColorValue
)
);
props.setAttributes( { style: newStyle } );
localAttributes.current = {
...localAttributes.current,
...{ style: newStyle },
};
};

const enableContrastChecking =
Platform.OS === 'web' && ! gradient && ! style?.color?.gradient;

Expand Down Expand Up @@ -508,6 +538,20 @@ export function ColorEdit( props ) {
isShownByDefault: defaultColorControls?.link,
resetAllFilter: resetAllLinkFilter,
},
{
label: __( 'Link Hover' ),
onColorChange: onChangeLinkHoverColor,
colorValue: getLinkColorFromAttributeValue(
allSolids,
style?.elements?.[ 'link:hover' ]?.color
?.text
),
clearable: !! style?.elements?.[ 'link:hover' ]
?.color?.text,
isShownByDefault:
defaultColorControls?.[ 'link:hover' ],
resetAllFilter: resetAllLinkHoverFilter,
},
]
: [] ),
] }
Expand Down
1 change: 1 addition & 0 deletions packages/blocks/src/api/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ export const __EXPERIMENTAL_STYLE_PROPERTY = {

export const __EXPERIMENTAL_ELEMENTS = {
link: 'a',
'link:hover': 'a:hover',
h1: 'h1',
h2: 'h2',
h3: 'h3',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
useStyle,
useColorsPerOrigin,
} from './hooks';
import Subtitle from './subtitle';

function ScreenLinkColor( { name } ) {
const supports = getSupportedGlobalStylesPanels( name );
Expand All @@ -39,6 +40,16 @@ function ScreenLinkColor( { name } ) {
'user'
);

const [ linkHoverColor, setLinkHoverColor ] = useStyle(
'elements.link:hover.color.text',
name
);
const [ userLinkHoverColor ] = useStyle(
'elements.link:hover.color.text',
name,
'user'
);

if ( ! hasLinkColor ) {
return null;
}
Expand All @@ -51,6 +62,7 @@ function ScreenLinkColor( { name } ) {
'Set the default color used for links across the site.'
) }
/>
<Subtitle>{ __( 'General' ) }</Subtitle>
<ColorGradientControl
className="edit-site-screen-link-color__control"
colors={ colorsPerOrigin }
Expand All @@ -63,6 +75,19 @@ function ScreenLinkColor( { name } ) {
onColorChange={ setLinkColor }
clearable={ linkColor === userLinkColor }
/>
<Subtitle>{ __( 'Hover' ) }</Subtitle>
<ColorGradientControl
className="edit-site-screen-link-color__control"
colors={ colorsPerOrigin }
disableCustomColors={ ! areCustomSolidsEnabled }
__experimentalHasMultipleOrigins
showTitle={ false }
enableAlpha
__experimentalIsRenderedInSidebar
colorValue={ linkHoverColor }
onColorChange={ setLinkHoverColor }
clearable={ linkColor === userLinkHoverColor }
/>
</>
);
}
Expand Down