Custom CSS for individual block instances in WordPress 7.0

WordPress 7.0 adds Custom CSS for individual block instances, and it is late. Since 6.2, styling one block meant a two-step dance: add a CSS class in the Advanced panel, then go and find the Global Styles panel to write the actual rule. The field now sits on the block you are editing.

I have spent 14 years around WordPress and I have lost count of the sites carrying a pile of one-off CSS classes that nobody dares delete. That is the debt this feature pays down. You write the rule in the block inspector, in the same scoped syntax the Site Editor already uses.

The problem with the old way

Say you wanted one specific Heading block to have its own hover effect. You added an Additional CSS Class in the Advanced panel, then opened the Site Editor’s global Custom CSS field and wrote the rules over there. It was fragile, and it was out of reach for any editor without full admin permissions.

Most users never found that path. Plenty of them installed a heavy styling plugin instead, to do something core should have handled. I wrote about where the styling system has been heading in an earlier post on native block CSS and layout fixes.

How Custom CSS for individual block instances works

Under the hood, WordPress registers a new customCSS support. Whatever you type into the field is saved as an attribute, inside the block’s style attribute under the css key.

<!-- wp:heading {"style":{"css":"&:hover { color: orange; }\n"}} -->
<h2 class="wp-block-heading has-custom-css">Dynamic Heading</h2>
<!-- /wp:heading -->

At render time WordPress generates a hash-based class for that one instance, so the rules cannot leak into another block. The frontend output ends up looking like this:

<style id="wp-block-custom-css">
    :root :where(.wp-custom-css-8841bf3c3cc9) {
        color: blue;
    }
</style>
<h2 class="wp-block-heading has-custom-css wp-custom-css-8841bf3c3cc9">Heading</h2>

The generated stylesheet is registered with a dependency on global-styles, so instance-level CSS wins over the defaults. Same idea as WordPress 7.0 dimensions support, which handles layout controls without an external stylesheet.

Opting out, and custom blocks

If you write blocks, some of them will break the moment a root-level class gets added, with raw HTML and shortcode wrappers the usual suspects. You can opt those out in block.json.

{
    "supports": {
        "customCSS": false
    }
}

For server-rendered blocks, WordPress injects the classes with WP_HTML_Tag_Processor rather than a regex pass over the markup. The details are in PR #73959 and the official Block Supports API reference.

If this kind of block styling work is eating your dev hours, I can take it on. I have been building on WordPress since the 4.x days.

The takeaway

WordPress 7.0 finally gives us the granularity, and Custom CSS for individual block instances means the Additional CSS workaround can go. One caution: a field that is easy to reach is also an easy way to scatter twenty near-identical rules across one page, so site-wide decisions still belong in Global Styles. The official dev note has the rest.

author avatar
Ahmad Wael
I'm a WordPress and WooCommerce developer with 15+ years of experience building custom e-commerce solutions and plugins. I specialize in PHP development, following WordPress coding standards to deliver clean, maintainable code. Currently, I'm exploring AI and e-commerce by building multi-agent systems and SaaS products that integrate technologies like Google Gemini API with WordPress platforms, approaching every project with a commitment to performance, security, and exceptional user experience.