Carry an authored auto menu margin onto the navigation host - #896
Merged
Conversation
…ion host
## Summary
Restate an authored inline-axis `auto` margin on the promoted navigation block after author CSS, so a menu the author pushed to the far end of its landmark still renders there.
## Why
A header authored as `nav{display:flex}` with `.navlinks{margin:0 0 0 auto}` puts its menu at the opposite end from the brand. The transformed page put it back at the start.
The authored rule was not lost. The class rides onto the navigation block and the stylesheet reaches the page — the rendered menu still takes `gap` and `padding` from the very same rule set, so only `margin` failed. Core's own navigation stylesheet owns the inner list: `.wp-block-navigation ul{margin-left:0}` is specificity 0,1,1 and outranks the authored `.navlinks` at 0,1,0. An authored margin can never win on that element.
The block host is the flex item that actually moves, so that is where the authored margin has to be restated. Emitting it as a block attribute is not available: `core/navigation` declares `supports.spacing` as `blockGap` only, in both vendored copies of its `block.json`, so a margin attribute would never serialize.
## How
Collect the classes carried by promoted list-navigation hosts from the serialized output, then for each authored bare-class rule whose class sits on one of those hosts and whose inline-axis margin is `auto`, emit `.wp-block-navigation.blocks-engine-list-navigation.<class>{margin-…:…!important}` after author CSS. This is the mechanism the file already uses to reassert `display:flex` on the same host.
Two deliberate limits:
- **Only `auto` is carried.** That is the declaration which positions a flex item and the one core's list reset destroys. An authored length is left to the author rule, which core does not contest on the host.
- **The class must sit on a navigation host.** Matching any bare-class rule with an auto margin made real projects emit `.wrap{margin:0 auto}` (a page wrapper) and `.brand{margin-right:auto}` (a brand) as navigation rules — dead CSS on 54 corpus documents. Reading the classes off the `wp:navigation` attributes took that to 10.
The emitted selector is self-limiting: it matches only an element that is both a promoted list navigation and carries the authored class.
## Verified in a render
Re-rendered through the same `run-fixture-matrix` pipeline the promotion gate uses, WordPress 7.0.2. In the captured DOM the source menu computes `margin-left: 665.547px` at `x=965.55`; the transformed menu now computes `margin-left: 665.547px` at `x=965.55`. Individual items match too: 965.55 / 1037.08 / 1102.47 on both sides.
## Blast radius
10 of 269 corpus design documents gain CSS. **No document changes block markup** — this is a stylesheet-only change. Unlike the neighbouring navigation fixes, "nothing changed" is not available as a safety argument here, so the render is the evidence.
## Known gap
What zeroes `margin-left` on the navigation *root* specifically is still unidentified — the inner-list override is proven, the root's is not. `!important` sidesteps that question rather than answering it.
## Testing
- [ ] `cd php-transformer && php tests/unit/navigation-inline-margin-carry.php` — 5 assertions; 2 fail against trunk
- [ ] `cd php-transformer && php tests/parity/run.php` — 278 fixtures
- [ ] `cd php-transformer && php tests/contract/run.php`
- [ ] `cd php-transformer && composer test`
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.
What
A header authored as
nav{display:flex}with.navlinks{margin:0 0 0 auto}puts its menu at the opposite end from the brand. The transformed page put it back at the start.The authored rule was not lost. The class rides onto the navigation block and the stylesheet reaches the page — the rendered menu still takes
gapandpaddingfrom the very same rule set, so onlymarginfailed. Core's own navigation stylesheet owns the inner list:An authored margin can never win on that element. The block host is the flex item that actually moves, so that is where it has to be restated.
Emitting it as a block attribute is not available:
core/navigationdeclaressupports.spacingasblockGaponly — checked in both vendored copies of itsblock.json— so a margin attribute would never serialize.How
Collect the classes carried by promoted list-navigation hosts from the serialized output, then for each authored bare-class rule whose class sits on one of those hosts and whose inline-axis margin is
auto, emit after author CSS:This is the mechanism the file already uses to reassert
display:flexon the same host. The selector is self-limiting — it matches only an element that is both a promoted list navigation and carries the authored class.Two deliberate limits:
autois carried. That is the declaration which positions a flex item and the one core's list reset destroys. An authored length is left to the author rule, which core does not contest on the host..wrap{margin:0 auto}(a page wrapper) and.brand{margin-right:auto}(a brand) as navigation rules — dead CSS on 54 corpus documents. Reading the classes off thewp:navigationattributes took that to 10.Verified in a render, not by reasoning
Re-rendered through the same
run-fixture-matrixpipeline the promotion gate uses, WordPress 7.0.2, with only the transformer swapped. From the captured DOM:margin-leftxxBlast radius
10 of 269 corpus design documents gain CSS. No document changes block markup — this is a stylesheet-only change. Unlike the neighbouring navigation fixes this one does not get to claim "nothing changed" as a safety argument, so the render is the evidence.
Known gaps
margin-lefton the navigation root specifically is still unidentified. The inner-list override is proven; the root's is not.!importantsidesteps that question rather than answering it.y30 vs 26.2). Unrelated to inline-axis alignment and not addressed here, but the promotion gate runs at--pixel-threshold 0, so that fixture can still register a difference.Testing
php tests/unit/navigation-inline-margin-carry.php— 5 assertions, 2 fail against trunk. Covers the carry, the opposite side riding along, a page wrapper producing no rule, an authored length producing no rule, and a plain menu producing no rule.php tests/parity/run.php— 278 fixtures passphp tests/contract/run.php— passcomposer test— exit 0No fixture was modified and no threshold was relaxed.