Skip to content

ENGINEERING LEADER

WordPress Developer

Speaker

Unapologetic Punk

Mitch Canter

  • Threads
  • Instagram
  • Bluesky
  • LinkedIn
  • GitHub
  • YouTube
WordPress

A Field Guide to Responsive Block Styles in WordPress 7.1

Mitch Canter

Reading time: 5 minutes
wordpress, lanyards, blog, blogging, blue, logo, code, open source, blue logo, blue blog, blue code, blue coding, wordpress, wordpress, wordpress, wordpress, wordpress

WordPress 7.1 added responsive styling to the block editor. Basically, you can now define how a block looks at different screen sizes without writing a line of CSS. It’s one of the longest-standing requests in block-theme land, and it landed alongside a few related pieces: configurable breakpoints, pseudo-state styling, and – importantly – some new controls for managing it’s use and access.

This post is more technical than “hey, it shipped” – we hit the theme.json syntax, the defaults, the constraints, and how it fits with the fluid typography and spacing tokens you may already have in your theme.

Responsive Styling

Responsive styling is available in two places: Global Styles (these are scoped more globally) and individual block instances (more locally scoped – on a block-by-block basis).

There are two default “viewports” set:

@mobile	@media (width <= 480px)
@tablet	@media (480px < width <= 782px)

Notice there’s no @desktop style – that’s because that’s the default base style – any @ reference will override that default.

Theme.JSON Syntax

Block-level (Global) responsive styles nest a viewport key inside the block’s style object:

"styles": {
  "blocks": {
    "core/group": {
      "spacing": {
        "padding": { "top": "3rem", "right": "3rem", "bottom": "3rem", "left": "3rem" }
      },
      "@mobile": {
        "spacing": {
          "padding": { "top": "1rem", "right": "1rem", "bottom": "1rem", "left": "1rem" }
        }
      }
    }
  }
}

That’s 3rem of padding everywhere, dropping to 1rem at the 480px breapoint and below. Notice that the structure in theme.json is the same, just scoped more specifically to the @mobile object. Every instance of that block is affected at all levels in a site.

Instance-level styling is found in the block’s style attribute as you look at the post content:

<!-- wp:paragraph {"style":{"@mobile":{"typography":{"fontSize":"1rem"}}}} -->
<p>Text with a responsive font size.</p>
<!-- /wp:paragraph -->

Two notes, because i know someone’s going to ask about it:

  • Non-layout per-instance declarations are output with !important so they can override inline styles. Yes, it drives me nuts, too.
  • Layout values and blockGap go through the existing layout support rather than that path. Which, is better anyway since we’re separating content with layout anyway – or we should be, at least.

Setting Your Own Breakpoints

A settings.viewport object in the theme.json can let you override the defaults with your own:

"settings": {
  "viewport": { "mobile": "30rem", "tablet": "45rem" }
}

This will generate, in CSS:

@media (width <= 30rem) { /* Mobile */ }
@media (30rem < width <= 45rem) { /* Tablet */ }

One thing to note: you can only use px, em, or rem values here – no %s. The default (782px) matches the breakpoint WordPress already uses natively in its admin screens. If you already have breakpoints set in CSS (using @media queries, most likely) you should match those up to your theme.json objects using the above code: otherwise, you’ll end up with conflicting breakpoints.

pseudo- and custom style states

This is a long-standing feature that I’ve been asking for a long time. While it’s not exactly the same as the responsive styling, it uses the same underlying style-states, so I want to mention it here.

Currently, the supported pseudo states are limited to the usual suspects: :hover, :focus, :focus-visible, and :active. Which, if you’re familiar with these uses, means the next limitation is no surprise – these are only really supported officially on Button and Navigation Link blocks. If you use these states anywhere else, you’ll want to keep using your own CSS for now.

That said, the same syntax to responsive breakpoints applies here as well:

"styles": {
  "blocks": {
    "core/button": {
      "color": { "background": "black", "text": "white" },
      ":hover": {
        "color": { "background": "blue" }
      }
    }
  }
}

…and can live inside of viewports, which brings us full circle:

"@mobile": {
  ":hover": {
    "color": { "background": "var:preset|color|contrast", "text": "var:preset|color|base" }
  }
}

It can also live in the post content, but I’d not recommend that – doing it this way renders it invisible to your design system. Best to keep it tokenized in theme.json. BUT, if you need the override, you can use it:

<!-- wp:button {"backgroundColor":"accent-3","style":{":hover":{"color":{"background":"var:preset|color|accent-2"}}}} -->

One note: Navigation links also support the :current state – which showcases which link you are actively navigated to. This state, plus other custom states, generate a CSS class, and require registration through the block.json‘s selectors property:

":current": {
  "color": { "text": "var:preset|color|contrast" }
}

How can I disable the UI?

Two filters/settings exist, and they behave the same way:

function example_disable_responsive_editing( $settings ) {
  $settings['responsiveEditingEnabled'] = false;
  return $settings;
}
add_filter( 'block_editor_settings_all', 'example_disable_responsive_editing' );
  • responsiveEditingEnabled controls the responsive editing interface.
  • blockStatesEditingEnabled does the same for pseudo-state editing. Defaults to true.

That said, both govern the editing interface only. Any styles saved into theme.json, in Global Styles, or in the style attribute on a block are left alone with this filter. Media query CSS is still also generated. This only removes the UI, not the functionality.

I’m already using token and primitives… how does this fit in?

The main thought here is that the viewport styles change at a threshold, while the fluid tokens can change continuously. Both are responsive, but solve different problems. Something like clamp()responds at 900px – which no default viewport covers. Viewport styles can also control layout changes, like changing a row to a column for organizational purposes.

Presets Become CSS Custom Properties

Everything defined in theme.json presets is emitted as a custom property:

  • --wp--preset--color--primary
  • --wp--preset--font-size--large
  • --wp--custom--*

And so on. This works for anything under the settings.custom object. Those tokens are available to you in your stylesheet, as well – without needing to refine them.

Fluid Typography

Setting "fluid": true in the typography settings, with per-size min and max values, produces font sizes that work between viewport widths. This is the mechanism that handles continuous type scaling; no media query is involved.

Clamp() in the spacing scale

Spacing presets accept clamp(), which does for rhythm what fluid typography does for type – a section gap that compresses smoothly as the viewport narrows.

Available Constraints

These may seem familiar to anyone who’s in theme.json regularly, but these options are also availble to you:

  • settings.color.custom — the custom color picker
  • settings.typography.customFontSize — arbitrary font-size entry
  • settings.spacing.customSpacingSize — arbitrary spacing entry

Set to false, these allow choice from presets rather than arbitrary values. This works well with the “Decisions, Not Options” model that has governed WordPress in the past – and makes for much easier client editing, in my opinion.

Naming Conventions

Preset slugs become the names editors see in the interface and the custom-property names in your CSS. Slugs that describe a role (primary, base, border-light) stay accurate through a palette change; slugs that describe the value (blue-500) don’t.

So, what’s missing?

This is pretty all-encompassing, and we’re recapping a few, but there’s some notable things either missing or coming soon:

  • Pseudo states are limited to Button and Navigation Link.
  • This is limited to two viewports, not an arbitrary number.
  • Breakpoint values are limited to plain lengths. That means no CSS functions or percentages.
  • Container queries are not part of this; these are viewport-width media queries.

Quick Reference and Sources

ThingSyntaxNotes
Mobile styles"@mobile": { ... }≤480px by default
Tablet styles"@tablet": { ... }480–782px by default
Desktop styles(the base style object)No @desktop key exists
Custom breakpointssettings.viewport.mobile / .tabletpx, em, rem only
Pseudo states":hover", ":focus", ":focus-visible", ":active"Button + Nav Link only
Nav Link current item":current"Custom state, class-based
Nesting"@mobile": { ":hover": { ... } }Viewport wraps state
Disable responsive UIresponsiveEditingEnabledUI only; saved styles still render
Disable states UIblockStatesEditingEnabledDefaults to true
WordPress 7.1 responsive styling and style states — quick reference.
  • Responsive block styles and configurable viewports in WordPress 7.1 — Make WordPress Core
  • Pseudo and custom style states in WordPress 7.1 — Make WordPress Core
  • WordPress 7.1 Field Guide — Make WordPress Core

Block Editor, Block Themes, css, Design Tokens, responsive design, Theme Development, Theme.json, WordPress 7.1
  • Advanced Custom Fields: Building a Client Friendly “Page Builder”, Part 1

    Advanced Custom Fields: Building a Client Friendly “Page Builder”, Part 1

    Reading time: 4 minutes

    There are very few subjects debated so hotly in the WordPress world as the ones regarding “Page Builders”. For the unfamiliar, a page builder allows the end user to set up content without needing knowledge of code.  While – to the end user – the allure of being able to have full control over design and…

    Tutorial, WordPress
  • Freshly Pressed: A Wild New Plugin Appears! Custom Classes

    Freshly Pressed: A Wild New Plugin Appears! Custom Classes

    Reading time: 1 minute

    From time to time I like to surf through the repository and try out new plugins.  It keeps me in the know and I’m able to share those plugins with you.  So, here’s what I found as I was surfing this fine Wednesday: Custom Classes Justin Tadlock is a man synonymous with great plugins, and his latest…

    WordPress
  • Home
  • About
  • Speaking
  • Articles
  • Contact