Block styles are one of the most underused tools in the WordPress design system. They let you register named visual variants for any block, core or custom, and make those variants selectable from a single click in the Style panel. A Button block with “Outline” and “Ghost” variants. A Group block with “Card” and “Panel” variants. A Quote block with “Pull Quote” and “Sidebar” variants. Each variant is just a CSS class, but the infrastructure around it, PHP registration, editor preview, theme.json integration, and the removal system, is what turns a scattered collection of utility classes into a first-class design language.
This is the final article in the Block Theme Design Systems series. The earlier articles covered design tokens in theme.json, style variations, template parts, synced patterns, and fluid typography. Block styles are the last major piece of the puzzle, the mechanism that lets you expose design system choices at the individual block level without requiring clients to open a style variation or edit CSS. By the end of this guide you will know how to register block styles in PHP and JavaScript, write precise CSS for each variant, use theme.json to style block style variations globally, remove default WordPress styles that conflict with your design system, and build a repeatable pattern for client-specific block style libraries.
What Block Styles Are and How WordPress Renders Them
A block style in WordPress is a named CSS class modifier that gets added to a block’s wrapper element when a user selects it. The class follows the pattern is-style-{slug}. When a user selects the “Outline” style for a Button block, WordPress adds is-style-outline to the block’s outer element. Your CSS targets that class to apply the visual treatment.
The full rendering pipeline works like this. First, a block style is registered, either in PHP via register_block_style() or in JavaScript via wp.blocks.registerBlockStyle(). Registration tells the editor the style exists, gives it a label for the UI, and optionally provides inline CSS for the editor preview. When a user activates the style, the block’s className attribute is updated in the stored block grammar. On the front end, WordPress outputs the block HTML with the is-style-{slug} class on the wrapper. Your theme’s stylesheet uses that class as a CSS specificity anchor to apply the variant’s visual rules.
This separation of concerns is important. Block styles do not change a block’s HTML structure, they only add a class. The block still renders its standard markup; the style controls what that markup looks like. This makes block styles safe, predictable, and easy to remove without breaking the post content. The block grammar stores the style name, and if the style is later removed from the theme, the block degrades gracefully to its unstyled state rather than breaking.
Block Styles vs. Block Variations vs. Block Patterns
These three concepts are often confused. Block styles change how a single block looks, same markup, different CSS. Block variations are pre-configured instances of a block with different default attributes, for example, a “Social Links” variation of the core Group block that has specific alignment and icon settings baked in. Block patterns are complete multi-block templates that can be inserted as a unit. If you want to give a client multiple visual treatments for a single block, use block styles. If you want to pre-configure a block’s settings, use block variations. If you want reusable layouts, use block patterns.
Registering Block Styles in PHP
PHP registration is the recommended approach for block styles that are part of your theme’s core design system. It happens at the server level, which means the styles are available immediately on page load without any JavaScript dependency. Use PHP registration for styles that will always be present in the theme.
The register_block_style() function accepts two arguments: the block name (in namespace/block-name format) and an array of style properties. The required properties are name (the slug used for the CSS class) and label (the display name in the editor). Optional properties include inline_css for editor-only preview styles and is_default to make this style active by default.
A few things to notice in this registration. The hook is init, not after_setup_theme, block style registration needs to happen after WordPress has loaded its block registry. The inline_css property accepts a string of CSS that is scoped to the editor preview only; it is not output on the front end. This is useful for giving editors an accurate visual preview without doubling your front-end CSS. For front-end styles, always use a proper enqueued stylesheet. The is_default flag on the “Card” style means new Group blocks will start with that style active unless the user explicitly changes it.
Registering Styles for Multiple Blocks at Once
If your design system defines a consistent “Card” or “Highlighted” treatment that should apply across many blocks, registering styles one by one becomes verbose. A better pattern is to define your block style library as a data structure and iterate over it.
This approach also makes it straightforward to conditionally register styles. If a style only applies on sites where a specific plugin is active, you can add a check inside the loop. If a style should only appear on certain post types, you can check get_post_type() and filter the registration accordingly. The data-driven structure keeps registration logic out of your CSS and makes auditing your design system inventory much easier.
Registering Block Styles in JavaScript
JavaScript registration via wp.blocks.registerBlockStyle() is the right approach for block styles that are registered dynamically or depend on JavaScript state. It is also commonly used in block plugins that need to register styles independently of a theme. The API is nearly identical to the PHP version.
The JavaScript registration file must be enqueued in the block editor. Use the enqueue_block_editor_assets hook to load it. This hook fires only inside the editor context, so your JavaScript registration code never runs on the front end, which is correct, because block style registration is purely an editor concept. The front-end rendering is handled entirely by CSS.
One practical reason to prefer PHP registration for theme-level styles: PHP-registered styles survive JavaScript errors. If your editor JavaScript has a runtime error during initialization, JavaScript-registered block styles might not appear in the Style panel. PHP-registered styles are always present because they are registered at the server level and passed to the editor as part of the initial block registry payload.
PHP vs. JavaScript: When to Use Each
| Scenario | Recommended Method |
|---|---|
| Theme design system styles | PHP (register_block_style()) |
| Plugin-provided styles | PHP or JavaScript (either works) |
| Dynamically generated styles | JavaScript |
| Styles that depend on JS state | JavaScript |
| Styles needed for SSR preview accuracy | PHP (more reliable) |
Writing CSS for Block Style Variations
The CSS for a block style is straightforward in principle but requires discipline in specificity management. The selector pattern is .wp-block-{block-name}.is-style-{slug}. For core blocks, the block name maps directly to the block’s namespace. The Button block is wp-block-button, the Quote block is wp-block-quote, the Group block is wp-block-group.
Several principles apply when writing block style CSS. First, always scope your selectors to the specific block class. Writing bare .is-style-ghost without the block class prefix will match any block that happens to have that style slug, which can cause unintended visual collisions if you register the same slug on multiple blocks for different purposes. Second, use CSS custom properties (design tokens from theme.json) wherever possible, this keeps your block styles responsive to theme.json changes and style variation overrides. Third, be explicit about property resets. If the default Button block has a solid background, your “Ghost” variant needs to explicitly set background: transparent, relying on inheritance from the parent is fragile.
Using Theme.json Custom Properties in Block Style CSS
When you define colors, spacing values, and font sizes in theme.json, WordPress generates CSS custom properties on the :root element. These custom properties are available in your block style CSS. Using them instead of hard-coded values means your block styles automatically update when the theme or a style variation changes the underlying design tokens.
The naming pattern for theme.json-generated custom properties is --wp--preset--{category}--{slug}. For example, a color with slug “primary” becomes --wp--preset--color--primary. A spacing preset with slug “lg” becomes --wp--preset--spacing--lg. For custom properties defined in the theme.json custom section, the pattern is --wp--custom--{path} where nested keys in the JSON are joined with double hyphens.
This is the bridge between your design token layer (theme.json) and your block style layer (CSS). When a user activates a style variation that redefines --wp--preset--color--primary to a different color, every block style that references that token updates automatically. You write the block style once, and it adapts to all your style variations without any duplication.
Using theme.json to Style Block Style Variations
As of WordPress 6.1, theme.json supports a dedicated blocks section inside styles that lets you target specific blocks and their style variations directly from the JSON configuration file. This is a significant capability, it means you can define the visual treatment for a block style variation entirely in theme.json, without writing any separate CSS file, and the styles participate in the full theme.json inheritance and override system.
The structure for targeting a block style variation in theme.json uses a nested path: styles.blocks.{block-name}.variations.{style-slug}. Inside that path, you can use any of the standard theme.json style properties: color, typography, spacing, border, shadow, and elements.
The advantage of defining block style variations in theme.json is that they are fully overridable by style variations. If you ship a “Minimal” style variation that redefines the Card group’s background to a lighter tone, you do that in the style variation’s JSON file under the same styles.blocks.core/group.variations.card path. The style variation JSON merges on top of the base theme.json, and the Card variation’s visual treatment updates without touching any CSS file. This is the correct way to build design-system-aware block styles for commercial themes.
There is one important scoping distinction to understand. Styles defined in styles.blocks.{block}.variations.{slug} in theme.json are output as CSS scoped to .wp-block-{block}.is-style-{slug}. Styles defined in the top-level styles.blocks.{block} path apply to all instances of that block regardless of style. Use the variations path for style-specific rules and the top-level block path for rules that should apply to every instance of the block.
Removing Default WordPress Block Styles
WordPress core ships several blocks with default styles pre-registered. The Image block has “Rounded” style. The Quote block has “Plain” style. The Separator block has “Wide Line”, “Dots”, and “Short Line” styles. The Button block has “Outline” style. In many design systems, especially those built for clients with specific brand guidelines, these default styles are either redundant, visually inconsistent with the design system, or outright confusing to non-technical editors.
Removing them requires the unregister_block_style() function in PHP or wp.blocks.unregisterBlockStyle() in JavaScript. PHP removal happens on the server and cleans up the style from the block registry before the editor loads. JavaScript removal happens in the editor after the initial registry is built, which means there is a brief window where the style may appear before it is removed. For cleaner editor experience, prefer PHP unregistration for theme-level cleanup.
When removing block styles, audit what you are removing against your design system’s intended vocabulary. If your design system has its own “Rounded” image treatment registered under a different slug, removing the core “rounded” style prevents vocabulary collision. If a client specifically requests that the outline button option be removed to enforce consistent use of their primary brand button style, removing “outline” enforces that constraint at the editor level rather than relying on editor training. Use removal as a curation tool, not as a default cleanup, only remove styles that actively conflict with your design system goals.
JavaScript Unregistration for Plugin-Added Styles
PHP unregistration only works for styles registered in PHP. For styles registered by JavaScript (many plugin-added styles fall into this category), you need JavaScript unregistration. The challenge is timing: your unregistration script needs to run after the plugin registers the style. Use the wp.domReady() callback to defer unregistration until after all scripts have loaded and the block registry is fully populated.
Building Block Styles for Client-Specific Needs
The most valuable application of custom block styles is client-specific design systems. When you build a block theme for a client, you are not just delivering a visual design, you are delivering an editorial system. The block styles you register become the vocabulary editors use every day. Well-designed block styles make it harder for editors to make off-brand decisions and easier for them to produce content that looks intentionally designed.
There are several patterns that work well for client-specific block style systems. The first is brand-aligned button variants. Instead of the generic “Outline” and “Fill” options WordPress provides, register styles named after their actual purpose in the client’s communication context: “Primary Action”, “Secondary Action”, “Text Link”. This tells editors what each button style is for, not just what it looks like.
The second pattern is content type markers. Register block styles on the Group block that communicate content type: “Callout” for advisory content, “Tip” for helpful hints, “Warning” for critical information, “Quote Feature” for pull quotes that deserve visual prominence. Each style gets a distinct visual treatment, border color, background tint, icon treatment, that signals the content category without requiring editors to manually apply complex styling.
The third pattern is layout modifiers. Register block styles on containers and columns that adjust layout behavior: “Narrow” for a centered, constrained-width group, “Full Bleed” for an edge-to-edge section, “Sticky Sidebar” for a layout where one column stays in view while scrolling. These are editorial decisions that should be made by choosing a named style, not by adjusting manual spacing and width controls.
When delivering client-specific block styles, document each style in a simple style guide that editors can reference. The documentation should show a visual example of each style in use, explain when to use it and when not to, and note any limitations. Block styles are only as useful as the training that accompanies them. A “Callout” style that editors do not know about will never be used; a “Callout” style that is prominently documented in the client’s editorial guide will become part of their standard publishing workflow.
Organizing Block Styles in a Theme Codebase
As your block style library grows, the organization of your code matters as much as the styles themselves. A flat file structure that works fine for five block styles becomes difficult to navigate at twenty. A deliberate directory structure keeps styles maintainable and makes it clear where to look when a specific block’s appearance needs to change.
A recommended structure separates block style registration (PHP) from block style CSS, and organizes both by block name. This mirrors the structure the block editor itself uses internally and makes it natural to find all the code related to a specific block in one place.
The main block-styles.php file in this structure is responsible only for including the per-block registration files and enqueueing the CSS. The per-block files (e.g., inc/block-styles/button.php) contain only the register_block_style() calls for that block. The CSS directory mirrors the same structure, with one file per block containing all style variations for that block. This approach also makes it straightforward to conditionally load block styles, if a particular block style is only needed on a specific page template, you can wrap its inclusion in a conditional check in the main file.
Testing and Debugging Block Styles
Several issues commonly arise when building block styles. The first is the style not appearing in the Style panel. Check that the block name is correct, a common mistake is using the block’s display name instead of its registered name. Use the block editor’s List View or the Block Inspector to see the block’s registered name, or look it up in the WordPress block reference. Also confirm that your registration hook fires at the right time and that there are no PHP errors preventing it from executing.
The second issue is CSS not applying on the front end even though the class is present. Open your browser’s developer tools and inspect the block element. Confirm the is-style-{slug} class is present on the correct element. Then check your CSS selector, it needs to match the block’s actual wrapper element. Some blocks wrap their content in multiple levels of elements, and the is-style- class is always on the outermost block wrapper, not on inner elements. If your CSS targets an inner element (like .wp-block-button__link for button text), write the selector as .wp-block-button.is-style-ghost .wp-block-button__link.
The third issue is styles appearing in the editor but not on the front end, or vice versa. The editor and front end use different stylesheets. The inline_css property in PHP registration only applies in the editor. Your enqueued stylesheet applies on the front end. If you need consistent appearance in both contexts, you must either use the same stylesheet in both contexts (using add_editor_style() to add your front-end stylesheet to the editor) or maintain separate but visually consistent inline CSS and front-end CSS.
Using add_editor_style() for Editor-Frontend Parity
The most reliable way to ensure your block styles look the same in the editor and on the front end is to add your front-end stylesheet to the editor via add_editor_style(). This function, called in after_setup_theme, tells WordPress to load an additional stylesheet in the block editor’s iframed content area. Because the editor’s content area is isolated in an iframe, styles loaded via add_editor_style() apply to the blocks as they would appear on the front end.
With this setup, you maintain one CSS file for block styles that applies consistently in both contexts. The only exception is if your front-end CSS depends on parent element selectors that are not present in the editor iframe, in that case you may need minor editor-specific overrides. But for most block style CSS, a single shared stylesheet produces accurate editor previews with minimal maintenance overhead.
Block Styles as the Final Layer of the Design System
If you have followed this series from the beginning, you now have a complete picture of how the WordPress design system is layered. At the foundation, theme.json defines design tokens, the colors, scales, spacing, and typography that everything else references. Style variations let you offer multiple complete design personalities built from the same token vocabulary. Template parts define the structural skeleton of your site. Synced patterns give editors reusable, consistently updated content components. Fluid typography creates a responsive scale that adapts without breakpoints. And block styles are the final layer, the mechanism that exposes controlled design choices at the individual block level, giving editors named, intentional options rather than a blank canvas of manual controls.
The cumulative effect of building all these layers deliberately is an editorial system that guides good decisions. A content editor working inside a well-designed block theme with a thoughtful block style library cannot easily produce off-brand content, the available choices are all on-brand, and the controls that would let them stray are either removed or intentionally constrained. That is what a design system is supposed to do: make the right thing easy and the wrong thing hard.
For theme developers, this also represents a commercial opportunity. Themes that ship with rich, well-documented block style libraries provide dramatically better out-of-box experiences than themes that require manual CSS to achieve standard design treatments. When a user can activate a “Featured Article” style on a Group block and immediately see a polished, on-brand featured content section, without touching the Customizer, without adding custom CSS, without opening the Site Editor, that is the kind of experience that drives positive reviews and repeat purchases.
Quick Reference: Block Style API
| Task | PHP Function / Hook | JavaScript Method |
|---|---|---|
| Register a style | register_block_style() on init | wp.blocks.registerBlockStyle() |
| Remove a style | unregister_block_style() on init | wp.blocks.unregisterBlockStyle() |
| Remove a plugin style (JS-registered) | Not applicable | wp.domReady() + unregisterBlockStyle() |
| Add front-end CSS to editor | add_editor_style() on after_setup_theme | Not applicable |
| Style via theme.json | styles.blocks.{name}.variations.{slug} | Not applicable |
| Enqueue editor JS | wp_enqueue_script() on enqueue_block_editor_assets | Not applicable |
What to Build Next
With the full Block Theme Design Systems series complete, the logical next step is to audit your existing themes against the design system principles covered across all six articles. Start with the token layer: are all colors, font sizes, and spacing values defined as theme.json presets, or are some still hard-coded in CSS? Then examine your block style inventory: which blocks do editors interact with most frequently, and do those blocks have style options that match the design decisions editors actually need to make?
If you are starting a new theme from scratch, begin with the token vocabulary in theme.json before writing a single line of CSS. Define your color palette, spacing scale, and typography scale as presets. Then write your style variations, referencing only those tokens. Then build your block styles, using CSS custom properties that reference the same tokens. By the time you write CSS, you should be writing almost exclusively selectors and custom property references, not raw values. That is the sign of a mature, design-system-aware block theme.
The articles in this series on style variations and design tokens in theme.json are the natural companion reads to this one. Style variations show you how to offer complete design personalities built from your token vocabulary. The design tokens article shows you how to plan and name the token layer that everything else depends on.
Summary
- Block styles register named CSS class variants for any block using
register_block_style()in PHP orwp.blocks.registerBlockStyle()in JavaScript. - PHP registration is more reliable for theme-level design systems; JavaScript registration is appropriate for dynamic or plugin-provided styles.
- The CSS selector pattern for a block style is
.wp-block-{block-name}.is-style-{slug}, always scope to the specific block class to avoid unintended collisions. - Use theme.json design token custom properties (
--wp--preset--color--{slug}, etc.) in block style CSS so styles automatically adapt to style variations. - theme.json supports direct block style variation targeting via
styles.blocks.{block-name}.variations.{slug}, this is the design-system-aware way to define variation styles for commercial themes. - Remove conflicting default WordPress block styles with
unregister_block_style()(PHP for PHP-registered styles, JavaScript withwp.domReady()for plugin-registered styles). - For client-specific themes, name block styles after their editorial purpose, not their visual appearance, “Primary Action” is more useful than “Blue Button”.
- Use
add_editor_style()to apply your front-end block style CSS inside the editor, ensuring accurate editor previews without maintaining two separate stylesheets.
Ready to Build Your Block Style Library?
This article wraps up the Block Theme Design Systems series. If you found it useful, the earlier articles on design tokens, style variations, and fluid typography build the complete picture of how to structure a production-quality block theme. Bookmark this series as your reference when starting your next block theme project.
