Skip to content

WM-0: writing-mode accepted as horizontal-tb only; vertical values refused - #41

Merged
thejackshelton merged 31 commits into
masterfrom
wm0-horizontal-tb
Sep 30, 2026
Merged

thejackshelton merged 31 commits into
masterfrom
wm0-horizontal-tb

Conversation

@thejackshelton

@thejackshelton thejackshelton commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

What changed

  • writing-mode, text-orientation and text-combine-upright are surrogate shorthands with no longhands.
    • writing-mode accepts horizontal-tb and the SVG 1.1 lr, lr-tb, rl and rl-tb, which Chrome 145 computes to horizontal-tb.
    • Every vertical value (vertical-, sideways-, tb, tb-rl) is refused with DRAGON_UNSUPPORTED_VALUE.
    • text-orientation and text-combine-upright are accepted as inert. text-combine-upright digits is refused, because Chrome 145 does not parse it.
  • A var() value on writing-mode or text-combine-upright is refused. A property with no longhands is never checked after var() substitution, so --wm: vertical-rl; writing-mode: var(--wm) used to compile silently. Four tests cover it and fail without the fix.
  • Keywords are lowercased ASCII-only and escapes are decoded, as elsewhere in Dragon.
  • The grammar gains the SVG 1.1 writing-mode values (css-writing-modes-4 Appendix B).
  • The writing-mode fixture group adds 2 layout fixtures (ltr and rtl) and 5 reject fixtures, and compares writing-mode's computed value in both renderings. The family is registered before grid, the order master's comments already expect.

Test changes, with reasons

  • logical-props reject-writing-mode and logical.test.ts now expect DRAGON_UNSUPPORTED_VALUE, because vertical values are now a value refusal.
  • The seams property-order pin gains the family, and the writing-mode registry test checks that it sits between background and grid.
  • pixel-reference glyph-clearance pins gain WM-0's four cases. With those cases removed, the old pins and bottom-scanlines hold exactly.
  • The css-escapes count goes from 13732 to 13778: +46 declarations from the new properties, all read the same by Chrome and Dragon.

Regenerated outputs are in their own commits, each naming its commands. A second run changes nothing, and existing outputs change only in their emitted-CSS compilation header.

Passed (macOS, after merging origin/master b41500f)

  • pnpm typecheck
  • pnpm test: 124 files, 2541/2541
  • pnpm wpt:check --target web: every entry matches, with no pass/fail status change
  • parity:lanes --run-device on iOS and Android, 392 cases per DPR:
    • layout-vectors-device, frames, applied and lines pass;
    • device-pixels fails with master's exact counts (iOS 135, Android 182), and the device-failures files are unchanged and name no writing-mode case.

This is the "P5 device step for WM-0's 4 cases" that HANDOFF.md listed as pending.

🤖 Generated with Claude Code

Catch-up (c0204fd): merged origin/master 74d72d9 (#43 V1b, #38 MQ-a); only generated files conflicted, taken from master and regenerated to a fixed point (media-sweep --check 18/18). Glyph-clearance pins in pixel-reference.test.ts = merge base + WM-0 increments + V1b increments. Device lanes, 460 cases per DPR: frames/applied/lines/layout-vectors pass on iOS and Android; device-pixels iOS 135 / Android 182, equal to master, same per-case breakdown, no writing-mode case. pnpm typecheck; pnpm test 132 files / 2904 tests; wpt:check --target web pass.

Note

Accept horizontal-tb for writing-mode; refuse vertical values

  • Adds writing-mode, text-orientation, and text-combine-upright as inherited no-longhand properties registered in properties.ts and the generated grammar
  • New shorthand handlers in writing-mode.ts accept only horizontal-tb plus SVG legacy horizontal aliases; vertical values emit a DRAGON_UNSUPPORTED_VALUE diagnostic at the offending token. text-combine-upright values with the digits keyword are refused
  • stylesheet.ts now returns a diagnostic for zero-longhand shorthands with a refusal function instead of parsing unchecked
  • Adds parity fixtures, layout vectors, and expected snapshots for horizontal and inert-text writing-mode cases in LTR and RTL, and rejection fixtures for vertical, sideways, legacy, and digit values
  • Updates WPT expectations in web.json so unsupported vertical writing-mode tests report unsupported-value diagnostics
  • Risk: dual.ts now uses horizontal-tb as the Dragon reset value for writing-mode when resolving reset longhands; check WRITING_MODE_RESET_LONGHANDS consumers if capture data is missing the property

Macroscope summarized c0204fd.

…the SVG 1.1 lr, lr-tb, rl, rl-tb, which Chrome 145 computes to horizontal-tb); vertical values, tb and tb-rl included, refused with DRAGON_UNSUPPORTED_VALUE; text-orientation and text-combine-upright accepted as inert (digits refused: Chrome 145 does not parse it). Surrogate shorthands with no longhands; writing-mode's computed value is compared in both renderings via computedExtra. Grammar gains the SVG 1.1 writing-mode values (css-writing-modes-4 Appendix B). Pinned tests retargeted: logical-props reject-writing-mode and logical.test.ts now expect DRAGON_UNSUPPORTED_VALUE for vertical values; seams SHORTHANDS order appends the family
…t:vectors, parity:dpr-capture, layout:dpr-vectors, profile:rows, native:gen, parity:lanes -- --run-host; web.json not-runnable reasons from wpt:check's run (no status changed). Existing emitted CSS changes only in its compilation header
Hand-written conflicts keep both sides: the writing-mode family is registered before grid, as master's comments already expect
(properties.ts, shorthands/index.ts, gen-css-grammar.ts SUBSET, seams.test.ts SHORTHANDS pin), and the writing-mode fixture group
runs before cascade-var, attributes and grid. Conflicted generated files take master's side here; the regeneration commit that
follows rebuilds them.
…e-upright

Substitution runs per longhand winner, and the writing-mode family sets no longhand, so after master's var() support
'--wm: vertical-rl; writing-mode: var(--wm)' compiled ready with no diagnostic. A shorthand with no longhands and a refuse check
now refuses a var() value at parse time with DRAGON_UNSUPPORTED_VALUE on the value; text-orientation refuses nothing and stays
inert through var(). Tests: four refusal cases (fail with the check removed) and text-orientation var() inertness. The registry
test now pins the family between background and grid instead of last, since grid is appended after it. Comments from the
logical-properties merge that said writing-mode is refused now say it accepts horizontal-tb only.
…r:gen, ua:capture, parity:capture and profile:rows (twice), parity:dpr-capture, layout:vectors, layout:dpr-vectors, layout:break-vectors, parity:break-capture, parity:pixel-capture, native:gen, north-star:check, parity:lanes -- --run-host; a second full run changes nothing

Conflicted generated files were taken from master in the merge and rebuilt here. Existing outputs of other fixtures change only in
the emitted CSS compilation header; the four writing-mode captures and emitted files gain master's grid longhands. web.json:
254 not-runnable reasons refreshed from a Chrome-free wpt run (no status changed); pnpm wpt:check --target web matches every entry.
… counts in pixel-reference.test.ts gain WM-0's four cases (writing-mode-horizontal, -inert-text, each ltr and rtl): edge +38, edge:glyph +28 at 2, +14 at 3, +23 at 2.625, rescued edge +38 (+27 at 2.625). With those four cases filtered out, the previous pins and bottom-scanline pins hold exactly, so no existing case changed
… (P5 device lanes on iOS and Android, 379 cases per DPR) and pnpm run parity:glyph-b3 -- --write-bottom-pins

Every device lane passes except device-pixels, which fails with master's counts exactly (ios 135, android 182, the known P6
failures); out/device-failures-{ios,android}.json are unchanged and name no writing-mode case. bottom-scanlines.json only adds
the four writing-mode cases.
…ld ASCII-only (asciiLower), as master's handlers now do; tests: escaped keywords (\68 orizontal-tb, l\r accepted; \76 ertical-rl, \74 b-rl refused, named by their decoded value)
… -> 13778. The twins cover every keyword of every grammar SUBSET property, and WM-0 adds writing-mode, text-orientation and text-combine-upright: exactly 46 of the 13778 twins are theirs (13732 without them, master's pin), and Chrome 145 and Dragon read every one like its plain twin (judgeTwins is empty)
pixel-reference.test.ts glyph-clearance pins take both sides' additions (master's showcase-player-card: edge +12, rescued edge +12;
WM-0's four cases as before). Conflicted generated files take master's side here; the regeneration commit that follows rebuilds them.
…grammar:gen, ua:capture, parity:capture and profile:rows (twice), parity:dpr-capture, layout:vectors, layout:dpr-vectors, layout:break-vectors, parity:break-capture, parity:pixel-capture, native:gen, north-star:check, tw:sweep, parity:glyph-b3 -- --write-bottom-pins, parity:lanes -- --run-host; a second full run changes nothing

Emitted CSS changes only in its compilation header; profiles gain the four writing-mode case ids; the device lanes read not run
until the device run that follows.
…t/lanes.json from /tmp/device-lease.sh pnpm run parity:lanes -- --run-device (380 cases per DPR)

Every device lane passes except device-pixels, which fails with master's counts exactly (ios 135, android 182, the known P6
failures); out/device-failures-{ios,android}.json are unchanged and name no writing-mode case.
…it, #37) into wm0-horizontal-tb

Only packages/parity/out/lanes.json conflicted; it takes master's side here and is rebuilt by the regeneration and device run that follow.
…run grammar:gen, ua:capture, parity:capture and profile:rows (twice), parity:dpr-capture, layout:vectors, layout:dpr-vectors, layout:break-vectors, parity:break-capture, parity:pixel-capture, native:gen, north-star:check, tw:sweep, parity:glyph-b3 -- --write-bottom-pins, parity:lanes -- --run-host; a second full run changes nothing

Only out/lanes.json changes (host lanes rerun; device lanes read not run until the device run that follows). A Chrome-free wpt
run changes no web.json status or reason.
…y/out/lanes.json from /tmp/device-lease.sh pnpm run parity:lanes -- --run-device (380 cases per DPR, with #36's Android settle wait)

Every device lane passes except device-pixels, which fails with master's counts exactly (ios 135, android 182, the known P6
failures); out/device-failures-{ios,android}.json are unchanged and name no writing-mode case.
@macroscopeapp

macroscopeapp Bot commented Sep 30, 2026 •

Copy link
Copy Markdown

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This change adds production-facing support for a previously refused CSS property family and changes handling of variable-backed declarations, while also updating grammar, support profiles, and extensive parity artifacts. The rendered impact is intentionally limited and well covered, but the broadened acceptance behavior warrants human review.

You can add or adjust custom eligibility rules. Learn more.

Fixture groups keep master's order: writing-mode after contexts, then cascade-var, attributes, grid, showcase and #40's
phrasing-blockified. pixel-reference.test.ts glyph-clearance pins take master's values plus WM-0's four cases (edge +38,
edge:glyph +28 at 2, +14 at 3, +23 at 2.625, rescued edge +38, +27 at 2.625). Conflicted generated files take master's side here;
the regeneration commit that follows rebuilds them.
…run grammar:gen, ua:capture, parity:capture and profile:rows (twice), parity:dpr-capture, layout:vectors, layout:dpr-vectors, layout:break-vectors, parity:break-capture, parity:pixel-capture, native:gen, north-star:check, tw:sweep, parity:glyph-b3 -- --write-bottom-pins, parity:lanes -- --run-host; a second full run changes nothing

Emitted CSS changes only in its compilation header; profiles gain the four writing-mode case ids. web.json: 254 not-runnable
reasons refreshed from a Chrome-free wpt run over master's file (the writing-mode reasons move to their next blocker; no status
changed). The device lanes read not run until the device run that follows.
…y/out/lanes.json from /tmp/device-lease.sh pnpm run parity:lanes -- --run-device (392 cases per DPR)

Every device lane passes except device-pixels, which fails with master's counts exactly (ios 135, android 182, the known P6
failures); out/device-failures-{ios,android}.json equal master's and name no writing-mode case.
…to packages/parity/test/writing-mode-computed.test.ts, unchanged (as a4c904c did for grid); a guard keeps browsers out of packages/dragon tests

CI runs vitest run packages/layout packages/dragon without Chrome, and the check launched it. Every assertion and the
verticalAcceptedAsHorizontal plant move as they were; parse is cast as in parity's grid-fuzz/compare.ts because parity's css-tree
types omit context. The Chrome-free parts stay in writing-mode.test.ts, including the compareDual plant on the committed capture.
platform-free.test.ts fails when a packages/dragon test imports Playwright, launches Chromium or loads parity's chrome.ts; it
flags the previous writing-mode.test.ts, and a planted-pattern test pins what it catches.
macroscopeapp[bot]
macroscopeapp Bot previously approved these changes Sep 30, 2026
…-parity) into wm0-horizontal-tb; conflicted generated outputs (emitted/*.css, out/lanes.json, corpus-dpr.json) taken from master and regenerated after
…run grammar:gen, ua:capture, parity:capture and profile:rows (twice), parity:dpr-capture, layout:vectors, layout:dpr-vectors, layout:break-vectors, parity:break-capture, parity:pixel-capture, native:gen, north-star:check (as node --conditions=dragon-internal examples/music-player/tools/check.ts; the package script lacks the condition on master), tw:sweep, parity:glyph-b3 -- --write-bottom-pins, parity:lanes -- --run-host
…lease.sh pnpm run parity:lanes -- --run-host --run-device (frames, applied, lines, layout-vectors pass on ios and android; device-pixels 135 ios / 182 android, equal to master, device-failures files unchanged)
…ontal-tb; fixtures.ts keeps both imports (writing-mode in place, values stays last per values.test.ts); the glyph-clearance pins in pixel-reference.test.ts are the merge base plus both sides' increments (WM-0: edge +38 at every DPR, edge:glyph +28/+14/+23, rescued +38/+38/+27 at DPR 2/3/2.625; V1b: edge +12/+10/+11, rescued +12/+10/+9). Generated conflicts (profiles ios.ts/web.ts, emitted/*.css, expected-fonts/emitted/*.css, lanes.json, corpus-dpr.json) taken from master and regenerated after
…run grammar:gen, ua:capture, parity:capture and profile:rows (twice), parity:dpr-capture, layout:vectors, layout:dpr-vectors, layout:break-vectors, parity:break-capture, parity:pixel-capture, native:gen, north-star:check (as node --conditions=dragon-internal examples/music-player/tools/check.ts), tw:sweep, parity:glyph-b3 -- --write-bottom-pins, parity:lanes -- --run-host
…lease.sh pnpm run parity:lanes -- --run-host --run-device (442 cases per DPR; frames, applied, lines, layout-vectors pass on ios and android; device-pixels 135 ios / 182 android, equal to master, device-failures files unchanged, no writing-mode case)
…0-horizontal-tb; only generated files conflicted (profiles ios.ts/web.ts, emitted/*.css, out/lanes.json, translate/corpus-dpr.json): taken from master and regenerated after
…run grammar:gen, ua:capture, parity:capture and profile:rows (twice), parity:dpr-capture, layout:vectors, layout:dpr-vectors, layout:break-vectors, parity:break-capture, parity:pixel-capture, native:gen, node --conditions=dragon-internal examples/music-player/tools/check.ts, tw:sweep, parity:glyph-b3 -- --write-bottom-pins, node --conditions=dragon-internal packages/parity/src/cli/media-sweep.ts (then --check: 18/18, 286 samples), parity:lanes -- --run-host
…lease.sh pnpm run parity:lanes -- --run-host --run-device (460 cases per DPR; frames, applied, lines, layout-vectors pass on ios and android; device-pixels 135 ios (66, 69) / 182 android (69, 49, 64), equal to master, device-failures files unchanged, no writing-mode case)
@macroscopeapp
macroscopeapp Bot dismissed their stale review September 30, 2026 22:48

Dismissing prior approval to re-evaluate c0204fd

@thejackshelton
thejackshelton merged commit 4a0c961 into master Sep 30, 2026
4 checks passed
thejackshelton added a commit that referenced this pull request Sep 30, 2026
thejackshelton added a commit that referenced this pull request Sep 30, 2026
… WM-0): /tmp/device-lease.sh pnpm run parity:devices (460 cases, both targets at once, 515 s; only laneCode moves, out/device-failures-{ios,android}.json byte-identical to origin/master's)
thejackshelton added a commit that referenced this pull request Oct 1, 2026
…son and corpus-dpr.json taken from master and regenerated after
thejackshelton added a commit that referenced this pull request Oct 1, 2026
…en sequence of 4e3957a: pnpm run grammar:gen, ua:capture, parity:capture and profile:rows twice, layout:vectors, parity:dpr-capture, layout:dpr-vectors, layout:break-vectors, parity:break-capture, parity:pixel-capture, native:gen, node --conditions=dragon-internal examples/music-player/tools/check.ts, wpt:run and wpt:update-expectations -- --target web, tw:sweep, parity:glyph-b3 -- --write-bottom-pins, node --conditions=dragon-internal packages/parity/src/cli/media-sweep.ts (then --check: 18/18, 286 samples), parity:lanes -- --run-host): the 4 writing-mode cases' vectors at DPR 1, 2, 3 and 2.625 gain V2a's neutral engine fields (check-additive-migration origin/master: 3237 files, 1857 changed, 7899 neutral additions removed, every other byte identical), corpus-dpr.json digests, lanes.json host run; every other output equals master's
thejackshelton added a commit that referenced this pull request Oct 1, 2026
… parity:lanes -- --run-host --run-device) after merging origin/master (#41 WM-0) on the round-1 engine: 460 cases; layout-vectors-host, layout-vectors-device, device-frames, device-applied and device-lines pass on ios and android with the V1 suites on V2a's engine inputs; device-pixels fails with ios 135 and android 182 failures, as on origin/master; out/device-failures-{ios,android}.json unchanged and byte-identical to master's
thejackshelton added a commit that referenced this pull request Oct 1, 2026
… errors, #49 north star and the Android profile rule, board) into anim-a2-translate: lanes.ts takes master's SUITE_LINE and SUITE_CAUSE; native-swift and native-kotlin pins take master's cause check with rt 54588/54588 appended; generated files (engines, harnesses, corpus.json, lanes.json) taken from master and regenerated in the next commit
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant