Repository navigation
fix: emit per-block layout compound classes in the React and Vue renderers - #703
Merged
ViewFromTheBox merged 1 commit intoAug 6, 2026
Conversation
…erers #700 fixed the missing `wp-block-{slug}-is-layout-{type}` compound in the Blade renderer only, leaving React and Vue emitting the pre-fix markup and rendering differently from Blade for the same block tree. Ports the Blade approach to both JS renderers via a shared helper alongside `classList()` — `layoutClass()` resolves `layout.type` against an allowlist so an unknown stored value can't mint an arbitrary class token, `layoutPair()` appends the per-block compound, and `layoutWrapperForBlock()` combines them. The slug is passed in explicitly because `row` and `stack` both render `wp-block-group` markup and need the `group` compound, not their own. Closes the four gaps the issue enumerates: - group / row / stack / buttons / post-template now pair each shared `is-layout-*` modifier with its compound - columns emits `is-layout-flex` + compound, where it previously emitted no layout class at all - post-content reads its `layout` attribute, defaulting to `flow` - group gained the `grid` branch its ternary chain silently rendered as flow `is-layout-masonry` stays unpaired — it is an ArtisanPack extension with no upstream compound. `LAYOUT_BASELINE_CSS` gains the constrained-group containment rules mirroring rule set C in `ThemeJsonTokensCompiler::compileLayoutRules()`. Blade gates those on theme.json declaring `contentSize` / `wideSize`; this string is static, so the gating falls out of `var()` semantics instead — an undefined custom property makes the declaration invalid at computed-value time and `max-width` resolves to its initial `none`. Both packages change together because `parity.test.ts` asserts React and Vue produce byte-identical HTML. Adds a parity fixture covering the constrained / grid group branches and a layout-carrying post-content, plus a `layoutCompound` suite per package covering every wrapper and the helper's allowlist fallback. Closes #702 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
📝 WalkthroughWalkthroughReact and Vue now share layout class serialization helpers. Layout-supporting blocks emit generic and block-specific classes. Post content and post templates use the helpers. Baseline CSS constrains group children. Tests cover fallback behavior, rendering, CSS, and parity. ChangesLayout class parity
Estimated code review effort: 4 (Complex) | ~45 minutes Possibly related issues
Possibly related PRs
Sequence Diagram(s)sequenceDiagram
participant BlockTree
participant LayoutBlock
participant Attributes
participant Markup
BlockTree->>LayoutBlock: Render layout-supporting block
LayoutBlock->>Attributes: Resolve layoutClass and layoutPair
Attributes-->>LayoutBlock: Return shared and compound classes
LayoutBlock->>Markup: Apply classes to wrapper
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
ViewFromTheBox
marked this pull request as ready for review
August 6, 2026 14:42
ViewFromTheBox
deleted the
bugfix/702-react-vue-renderers-per-block-layout-compound
branch
August 6, 2026 14:42
This was referenced Aug 6, 2026
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
#700 fixed the missing
wp-block-{slug}-is-layout-{type}compound class in the Blade renderer only, so the React and Vue renderers kept emitting the pre-fix markup and began rendering differently from Blade for the same block tree. This ports #700's approach to both JS renderers.Closes: #702
Type of Change
Related Issue
Issue: #702 (follow-up to #700 / PR #701)
Motivation and Context
The three renderers are meant to be interchangeable —
parity.test.tsdescribes them as sharing "the Blade partial contract" — but that test only compares React against Vue, never against Blade. That is why #700 was safe to land Blade-only, and also why the resulting divergence went unnoticed: a page rendered through React or Vue was no longer visually identical to the same tree rendered through Blade.The block-library stylesheet targets the per-block compound exclusively for several rules (constrained containment on
.wp-block-group, alignment handling on.wp-block-post-template), so emitting only the sharedis-layout-*modifier left those rules unmatched.Changes Made
layoutClass()/layoutPair()/layoutWrapperForBlock()tosrc/support/attributes.tsin both packages — the JS twin ofLayoutSupport.php.layoutClass()resolveslayout.typeagainst aconstrained|flex|flow|gridallowlist so an unknown stored value cannot mint an arbitrary class token. The slug is passed in explicitly rather than derived from the block name, becauserowandstackboth renderwp-block-groupmarkup and need thegroupcompound.group,row,stack,buttonsandpost-templatenow pair each shared modifier with its per-block compound.columnsemitsis-layout-flex+ compound, where it previously emitted no layout class at all.post-contentnow reads itslayoutattribute, defaulting toflow(matchinggroupand upstream).groupgained thegridbranch its ternary chain silently rendered asis-layout-flow— a pre-existing divergence, not fallout from Renderer emits shared layout class but not per-block layout class the block-library CSS targets #700.is-layout-masonrydeliberately stays unpaired: it is an ArtisanPack extension with no upstream compound.LAYOUT_BASELINE_CSSgained the constrained-group containment rules mirroring rule set C inThemeJsonTokensCompiler::compileLayoutRules().Both packages change together because
parity.test.tsasserts React and Vue produce byte-identical HTML — patching one alone fails the suite.Note on the baseline CSS gating
The Blade side gates those containment rules on
theme.jsondeclaringcontentSize/wideSize, butLAYOUT_BASELINE_CSSis a static string with no theme-token compiler behind it. The gating instead falls out ofvar()semantics: an undefined custom property makes the declaration invalid at computed-value time, somax-widthresolves to its initialnone.The auto margins are not gated the same way — with no
contentSizeconfigured they still apply, which centers any child that sets its own width. That matches what WordPress core emits for a constrained layout, and it is documented inline as the one place this baseline goes further than the Blade renderer's gated output.How Has This Been Tested?
Testing Environment:
release/1.6Tests Performed:
npm test— 331 files / 3077 tests passing (was 3042; +35 new)../vendor/bin/pest— 1700 passing, 1 skipped, 4493 assertions. The Blade renderer is untouched by this change and stays green.npx tsc --noEmit— 0 errors in either renderer package. (Pre-existing errors elsewhere underresources/js/are unrelated and untouched.)npm run verify:parity— all 152 blocks registered across react / vue / blade.Accessibility Tests Run
Details:
Not applicable — this change adds CSS class tokens to existing wrapper elements and adds layout rules to a stylesheet. No interactive elements, focus order, semantics, ARIA, or color values are introduced or altered. The one perceivable effect is that constrained groups now cap their unaligned children at the theme's content size, which is the intended (and upstream-standard) reading-width behavior.
Tests Added
Test details:
tests/layoutCompound.test.tsx(React) andtests/layoutCompound.test.ts(Vue) — 13 tests each. Cover every layout-supporting wrapper's exact class string, thegridbranch,post-contentwith and without a stored layout,post-templategrid/flow/masonry, theLAYOUT_BASELINE_CSScontainment rules, and the helper's allowlist fallback (including that a hostilelayout.typecannot mint a class token).parity.test.tscovering the constrained and grid group branches plus a layout-carryingpost-content, so React/Vue drift on the new paths is caught.BlockTree.test.*andPostAndSiteContextRendering.test.*to the new expected markup.Documentation
Documentation details:
Docblocks on the three new helpers explaining the allowlist and why the slug is passed in explicitly rather than derived from the block name. Inline notes at each call site. The
layoutBaselineCss.tsheader previously said to keep the string byte-identical with the Blade partial; it now records the deliberate exception and why it exists.Screenshots
n/a — the change is at the class-attribute level; the expected markup is asserted exactly in the new test suites.
Pre-Submission Checklist
Follow-up worth considering
The issue notes that nothing currently guards against the three renderers drifting on markup:
scripts/verify-renderer-parity.mjsonly checks that each renderer registers the same block names, andparity.test.tsonly compares React against Vue. A Blade-vs-JS markup fixture check would have caught this class of bug at the point #700 landed. Not done here to keep the fix scoped — worth its own issue.🤖 Generated with Claude Code
Summary by CodeRabbit
Enhancements
Tests