Skip to content

Responsive @tablet/@mobile styles are ignored in standalone block style variation JSON partials #82392

Closed

Description

@draganescu

Description

Gutenberg port of Trac ticket #65992 (milestoned for WordPress 7.1.1), which is being fixed in core via wordpress-develop#13325. The plugin has the identical bug, and since WP_Theme_JSON_Gutenberg overrides core's class while the plugin is active, the core fix alone does not resolve it for Gutenberg users — the plugin needs the equivalent change.

WordPress 7.1 introduced responsive style states using the @tablet and @mobile keys. They work when a block style variation is declared inside theme.json under styles.blocks.<block>.variations.<slug>, but are silently dropped when the same variation is declared in a standalone JSON partial in the theme's styles/ directory. The base styles of the partial are generated correctly; no media-query CSS is generated for the @tablet/@mobile blocks, and nothing surfaces an error in the editor or on the front end.

Root cause (plugin side)

Standalone partials declare their styles at the root of the styles object, and WP_Theme_JSON_Resolver_Gutenberg::get_style_variations_from_directory() sanitizes each partial by running it through WP_Theme_JSON_Gutenberg (lib/class-wp-theme-json-resolver-gutenberg.php:816), so partials are validated against the top-level styles schema in WP_Theme_JSON_Gutenberg::sanitize().

The responsive breakpoint states were only added to the element, block, and block-style-variation branches of the schema (lib/class-wp-theme-json-gutenberg.php:1293, :1320, :1376). The top-level styles schema ($schema['styles'], lib/class-wp-theme-json-gutenberg.php:1402) never gets the breakpoint-state keys, so remove_keys_not_in_schema() strips @tablet and @mobile from partial roots as unknown keys.

The variation branch of the schema cannot cover partials: it validates a different location in the tree, and its guard requires the variation to already be present in $valid_variations, which is derived from the block styles registry — partials are only registered by gutenberg_register_block_style_variations_from_theme_json_partials() after get_style_variations() returns.

The proposed core fix adds the responsive breakpoint states to the top-level styles schema, mirroring what the block style variation branch already does; the plugin fix should do the same in WP_Theme_JSON_Gutenberg::sanitize().

Step-by-step reproduction instructions

  1. On a block theme with the Gutenberg plugin active, create styles/article-heading-xl.json:
    {
        "$schema": "https://schemas.wp.org/wp/7.1/theme.json",
        "version": 3,
        "title": "Heading XL",
        "slug": "article-heading-xl",
        "blockTypes": [ "core/heading", "core/post-title" ],
        "styles": {
            "typography": { "fontSize": "40px", "fontWeight": "700", "lineHeight": "1.3" },
            "@tablet": { "typography": { "fontSize": "28px" } },
            "@mobile": { "typography": { "fontSize": "16px", "lineHeight": "1.35" } }
        }
    }
  2. Add a Heading block to a page and apply the "Heading XL" block style.
  3. View the page on the front end and inspect the heading.
  4. Resize the viewport below the tablet breakpoint, then below the mobile breakpoint.

Expected: the base rule plus rules wrapped in the tablet and mobile media queries are generated for the variation selector, and the heading font size steps down at each breakpoint.

Actual: only the base rule is generated; the heading keeps the desktop font size at every viewport width. Moving the identical @tablet/@mobile blocks into theme.json under styles.blocks.core/heading.variations.article-heading-xl produces the expected media queries — only the standalone partial path is affected.

Additional context

  • Reported on Trac by @imflexwala; root-cause analysis and core patch (with unit tests) by @mitgiselle in wordpress-develop#13325, who offered a companion Gutenberg PR in ticket comment 4 so the two can be reviewed together.
  • Screenshots demonstrating the bug and a Playground test report validating the core patch are attached to the Trac ticket.

Activity

  1. added
    [Type] BugAn existing feature does not function as intended
    Global StylesAnything related to the broader Global Styles efforts, including Styles Engine and theme.json
    on Sep 3, 2026
  2. moved this from 🏗️ In Progress to 🔎 Needs Review in WordPress 7.1.x Editor Taskson Sep 5, 2026
  3. added
    [Feature] Block Style VariationsIssues or PRs that are related to the style variations for blocks
    [Feature] Style StatesRelated to block style states (currently viewport and pseudo-states)
    on Sep 5, 2026
  4. talldan commented on Sep 7, 2026

    @talldan
    Contributor

    It is already known that the responsive states (@tablet, @mobile) aren't supported at the root styles level. The docs mention it (kind of):

    Any style property that is valid at the block or element level can be nested under one of these keys.

    I think the unexpected consequence is the way it affects the standalone variations, because they get processed as though they're a root theme.json file.

    Shame this wasn't caught earlier.

    I see the PR #82403. My concern with this is that it'll allow values at the root of a style object in a regular theme.json file too. Although I don't think they are processed, but I think it means they are saved in the database, so could result in those stray values becoming invalid at some point in the future, possibly throwing errors.

    Or we decide to support it, but that might be too much for a 7.1.x fix.

  5. talldan commented on Sep 7, 2026

    @talldan
    Contributor

    Ok, after some more investigation, I think I have an idea on how we can fix this, and it's not too difficult. We could add support for the responsive states at the top level like #82403 does, but only when the theme.json data contains a blockTypes property - blockTypes being the thing that scopes the styles to a block, essentially making them a style variation.

  6. firdaus666 commented on Sep 9, 2026

    @firdaus666

    I can reproduce a similar issue with pseudo-selector styles in a standalone block style variation JSON file.

    Environment:

    • WordPress 7.1
    • Block theme
    • Gutenberg plugin inactive
    • PHP 8.3

    Example standalone variation:

    {
      "version": 3,
      "title": "Theme: Light",
      "slug": "fir-theme-light",
      "blockTypes": ["core/button"],
      "styles": {
        "color": {
          "background": "var:preset|color|primary-background",
          "text": "var:preset|color|tertiary-text"
        },
        ":hover": {
          "color": {
            "background": "var:preset|color|secondary-background",
            "text": "var:preset|color|primary-text"
          }
        }
      }
    }

    The base color styles work, but the root :hover styles are removed while the standalone variation is sanitized.

    I confirmed this by inspecting:

    WP_Theme_JSON_Resolver::get_style_variations( 'block' );

    The resolved variation still contains color, but no longer contains :hover.

    The same structured :hover declaration works when the variation is defined inside the main theme.json under:

    styles.blocks.core/button.variations.fir-theme-light
    

    Using the standalone variation’s styles.css field also works as a temporary workaround:

    "css": "& .wp-block-button__link:hover { background-color: var(--wp--preset--color--secondary-background); color: var(--wp--preset--color--primary-text); }"

    This appears related to the standalone-partial sanitization described here: root state keys are not present in the top-level styles schema and are consequently removed by remove_keys_not_in_schema().

    Would this pseudo-selector case belong in this issue, or would you prefer a separate issue linked to it?

  7. talldan commented on Sep 10, 2026

    @talldan
    Contributor

    @firdaus666 The current proposed fix (#82403) should solve the problem with pseudo selectors too.

  8. pbiron commented on Sep 10, 2026

    @pbiron

    I can reproduce a similar issue with pseudo-selector styles in a standalone block style variation JSON file.

    I just came to open an issue about pseudo-selector styles because I just discovered the problem today.

    Environment:

    • WordPress 7.1 (and 7.1.1-RC1)
    • Block theme
    • Gutenberg plugin inactive (and 23.9.1 active)
    • PHP 8.4
  9. added a commit that references this issue on Sep 10, 2026
    09ea4d0
  10. moved this from 🔎 Needs Review to ✅ Done in WordPress 7.1.x Editor Taskson Sep 11, 2026
  11. pbiron commented on Sep 11, 2026

    @pbiron

    Can confirm that PR 82403 allows :hover:, etc to work properly in style variation partials. Thanx everyone!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Global StylesAnything related to the broader Global Styles efforts, including Styles Engine and theme.json[Feature] Block Style VariationsIssues or PRs that are related to the style variations for blocks[Feature] Style StatesRelated to block style states (currently viewport and pseudo-states)[Status] In ProgressTracking issues with work in progress[Type] BugAn existing feature does not function as intended

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions