How I Built the “Enable Hover Color” Plugin for WordPress Block Editor

Qara Yahya Avatar

As a WordPress developer, I often find myself wanting to extend the functionality of the Block Editor (Gutenberg) to better suit my needs. One such need was the ability to add hover effects—specifically hover colors—to blocks. While the Block Editor provides robust support for colors, it doesn’t natively support hover states. To solve this, I built the “Enable Hover Color” plugin. In this blog post, I’ll walk you through how I built it and share the final code.

Enable Hover Color

Learning Resources

Building this plugin was a challenging but rewarding experience, and I couldn’t have done it without some excellent resources. Here are the two that helped me the most:

The Problem

When working on this plugin, I quickly encountered a challenge: you cannot add a :hover effect using only inline CSS. In traditional CSS, hover states are handled using stylesheets, but in Gutenberg blocks, inline styles are often the most flexible way to apply styles dynamically.

The Solution

The solution I came up with uses inline CSS to add color variables and then applies those variables to the :hover effect. This approach allows for dynamic hover colors without requiring custom CSS for each block. Here’s how it works:

  1. Inline CSS Variables: The plugin adds inline styles to the block’s wrapper element, defining CSS variables like “–hover-background-color”. These variables are set based on the user’s selections in the Block Editor.
<div class="has-hover__background" style="--hover-background-color: #f3f4f6;">
    Hello?
</div>
  1. Hover Effects: The plugin uses these CSS variables in the block’s stylesheet to apply hover effects. For example:
.has-hover__background:hover {
    background-color: var(--hover-background-color);
}
  1. Transition Controls: The plugin also allows users to define the duration and timing function for hover transitions, ensuring smooth and customizable hover effects.

Checking for Text Color Support

One of the key parts of the plugin is ensuring that hover color controls are only added to blocks that support text color. This is important because not all blocks in the Block Editor have color support, and adding unnecessary controls could clutter the interface or cause errors. Here’s a breakdown of the code that handles this:

// Check if the block supports text color.
const hasTextColorSupport = () => {
    const colorSupport = getBlockSupport(settings, "color");
    return colorSupport && colorSupport.text !== false;
};

if (!hasTextColorSupport()) {
    return settings;
}

What This Code Does

  1. getBlockSupport(settings, “color”):
    • This function checks if the block supports the color feature. The settings parameter contains the block’s configuration, and "color" specifies the feature we’re checking for.
    • The color feature typically includes support for text color, background color, and sometimes gradient colors.
  1. colorSupport && colorSupport.text !== false:
    • The colorSupport object returned by getBlockSupport contains details about the block’s color support. Specifically, it includes a text property that indicates whether the block supports text color.
    • The condition checks if colorSupport exists and if the text property is not explicitly set to false. If both conditions are true, the block supports text color.
  1. if (!hasTextColorSupport()) { return settings; }:
    • If the block does not support text color, the function returns the original settings object without making any modifications. This ensures that only blocks with text color support receive the hover color attributes and controls.

Why This Matters

  • Selective Enhancement: By checking for text color support, the plugin ensures that hover color controls are only added to blocks where they make sense (e.g., buttons, headings, paragraphs). This keeps the Block Editor clean and user-friendly.
  • Avoiding Errors: Adding hover color attributes to blocks that don’t support text color could lead to unexpected behavior or errors. This check prevents such issues.
  • Future-Proofing: If new blocks are added to the Block Editor, this code will automatically determine whether they should have hover color support based on their configuration.

This approach is a great example of how to extend the Block Editor responsibly, ensuring compatibility and a smooth user experience.

Step 1: Adding Attributes to Blocks

  • hoverTextColor
  • customHoverTextColor
  • hoverBackgroundColor
  • customHoverBackgroundColor
  • hoverBorderColor
  • customHoverBorderColor
  • hoverTransitionDuration
  • hoverTransitionTiming

Here’s the code:

addFilter(
    "blocks.registerBlockType",
    "enable-hover-color/add-attributes",
    function (settings, name) {
        // Check if the block supports text color.
        const hasTextColorSupport = () => {
            const colorSupport = getBlockSupport(settings, "color");
            return colorSupport && colorSupport.text !== false;
        };

        if (!hasTextColorSupport()) {
            return settings;
        }

        return {
            ...settings,
            attributes: {
                ...settings.attributes,
                hoverTextColor: { type: "string" },
                customHoverTextColor: { type: "string" },
                hoverBackgroundColor: { type: "string" },
                customHoverBackgroundColor: { type: "string" },
                hoverBorderColor: { type: "string" },
                customHoverBorderColor: { type: "string" },
                hoverTransitionDuration: { type: "number", default: 200 },
                hoverTransitionTiming: { type: "string", default: "ease" },
            },
        };
    }
);

Step 2: Adding Controls to the Block Editor

The <InspectorControls> component allows you to integrate your custom controls into existing groups like color or typography. This is particularly useful for maintaining a consistent user experience. For instance, in the “Enable Hover Color” plugin, I added hover color and transition controls to the color group:

<InspectorControls group="color">
    <HoverColorsControls {...props} />
    <HoverTransitionControls {...props} />
</InspectorControls>

This ensures that all color-related settings, including hover effects, are located in one place. It’s a great way to enhance functionality without cluttering the sidebar with additional panels.

addFilter(
    "editor.BlockEdit",
    "enable-hover-color/add-inspector-controls",
    createHigherOrderComponent((BlockEdit) => {
        return (props) => {
            const { name, attributes, setAttributes, clientId } = props;

            const hasTextColorSupport = () => {
                const blockType = getBlockType(name);
                const colorSupport = getBlockSupport(blockType, "color");
                return colorSupport && colorSupport.text !== false;
            };

            if (!hasTextColorSupport()) {
                return <BlockEdit {...props} />;
            }

            return (
                <>
                    <BlockEdit {...props} />
                    <InspectorControls group="color">
                        <HoverColorsControls
                            attributes={attributes}
                            setAttributes={setAttributes}
                            clientId={clientId}
                        />
                        <HoverTransitionControls
                            attributes={attributes}
                            setAttributes={setAttributes}
                            clientId={clientId}
                        />
                    </InspectorControls>
                </>
            );
        };
    })
);

Step 3: Applying Hover Styles in the Editor

To apply the hover styles in the editor, I used the editor.BlockListBlock filter. This filter allows me to modify the block’s wrapper props and add inline styles and classes dynamically.

The plugin uses CSS variables to apply the hover colors and transitions. Here’s how it works:

addFilter(
    "editor.BlockListBlock",
    "enable-hover-color/add-styles",
    createHigherOrderComponent((BlockListBlock) => {
        return (props) => {
            const { name, attributes } = props;

            const hasTextColorSupport = () => {
                const blockType = getBlockType(name);
                const colorSupport = getBlockSupport(blockType, "color");
                return colorSupport && colorSupport.text !== false;
            };

            if (!hasTextColorSupport()) {
                return <BlockListBlock {...props} />;
            }

            const {
                hoverTextColor,
                customHoverTextColor,
                hoverBackgroundColor,
                customHoverBackgroundColor,
                hoverBorderColor,
                customHoverBorderColor,
                hoverTransitionDuration,
                hoverTransitionTiming,
            } = attributes;

            const getColorValue = (presetColor, customColor) => {
                return presetColor
                    ? `var(--wp--preset--color--${presetColor})`
                    : customColor || "";
            };

            const inlineStyle = {
                "--hover-color": getColorValue(hoverTextColor, customHoverTextColor),
                "--hover-background-color": getColorValue(
                    hoverBackgroundColor,
                    customHoverBackgroundColor
                ),
                "--hover-border-color": getColorValue(
                    hoverBorderColor,
                    customHoverBorderColor
                ),
                "--hover-transition-duration": attributes.hoverTransitionDuration + "ms",
                "--hover-transition-timing": attributes.hoverTransitionTiming,
            };

            return (
                <BlockListBlock
                    {...props}
                    wrapperProps={{ style: inlineStyle }}
                    className={classnames(props.className, {
                        "has-hover__color": hoverTextColor || customHoverTextColor,
                        "has-hover__background-color": hoverBackgroundColor || customHoverBackgroundColor,
                        "has-hover__border-color": hoverBorderColor || customHoverBorderColor,
                    })}
                />
            );
        };
    })
);

This dynamically updates the block’s appearance inside the editor, giving a real-time preview of hover effects.


The Color Controls Component

The “HoverColorsControls” component is responsible for rendering the color pickers in the sidebar. It uses WordPress’s “ColorGradientSettingsDropdown” component to provide a user-friendly interface for selecting colors.

const HoverColorsControls = ({
	clientId,
	hoverTextColor,
	hoverBackgroundColor,
	hoverBorderColor,
	setHoverTextColor,
	setHoverBackgroundColor,
	setHoverBorderColor,
}) => {
	const colorSettings = [
		{
			value: hoverTextColor?.color,
			onChange: setHoverTextColor,
			label: __("Hover Text"),
			resetAllFilter: () => ({
				hoverTextColor: undefined,
				customHoverTextColor: undefined,
			}),
		},
		// Similar settings for background and border colors
	];

	const colorGradientSettings = useMultipleOriginColorsAndGradients();

	return (
		<>
			{colorSettings.map(({ onChange, label, value, resetAllFilter }) => (
				<ColorGradientSettingsDropdown
					key={`hover-color-${label}`}
					settings={[
						{
							colorValue: value,
							label,
							onColorChange: onChange,
							resetAllFilter,
							enableAlpha: true,
							clearable: true,
						},
					]}
					panelId={clientId}
					{...colorGradientSettings}
				/>
			))}
		</>
	);
};

Final Thoughts

Let me know what you think!

One response to “How I Built the “Enable Hover Color” Plugin for WordPress Block Editor”

  1. Jb Audras Avatar

Leave a Reply

Your email address will not be published. Required fields are marked *