Skip to content

GRID: grid layout on web, iOS and Android (engine, fixtures, place-* shorthands) - #253

Merged
thejackshelton merged 56 commits into
masterfrom
claude/native-grid-h62uxx
Oct 10, 2026
Merged

thejackshelton merged 56 commits into
masterfrom
claude/native-grid-h62uxx

Conversation

@thejackshelton

@thejackshelton thejackshelton commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

Requested by Jack · project thread

Before: display: grid and every grid-* property were refused on web, iOS and Android (the engine had no grid layout; the compiler's grid input and lowering were already on master from #197).

After: grid containers and grid items lay out on all three targets. The layout engine gains grid.ts, a port of Blink 145 grid layout, and seven Chrome-compared grid fixtures (ltr and rtl) back the grid support rows, so grid-template-*, grid-area/grid-row/grid-column, grid-auto-*, the gaps and grid alignment become supported on iOS and Android through the translated engine (lane linux-dragon-layout).

This PR lands #212 (engine), #246 (fixtures, stacked on #212) and the G1c branch (place-content, place-items and place-self; every self-alignment value; the template shorthands) together, caught up with today's master. Both had passed their own CI and test-file runs; see their bodies for the full details, which this PR carries unchanged.

How: branch from #246's head 0a786aed15 (which already contains #212's land-ready head), merge origin/master 03ba583 (no textual conflicts), and one fix for master's new scrollRangesWithFaults (overflow.ts builds a Ctx, which now needs gridFaults: NO_GRID_FAULTS). Then a CI regen.

Supersedes

Review fixes (Claude correctness review; Macroscope skipped)

  • block.ts: a grid container with aspect-ratio now keeps its content-based minimum height (ratioFinalBlockSize, as block and flex do). Proven by the new grid-aspect-ratio fixture (ltr and rtl), which matches Chrome.
  • scroll-decidable.ts: the compiler now refuses a native scroll container whose engine range would be refused for a grid container, as overflow.ts does. Without it a grid with overflow: auto reached the native fatalError. The test gains a grid child shape and fails without the rule.
  • grid-corpus.ts: that refusal is owned by G-OVF. The hand-corpus pins move overflow-auto 24 to grid-scroll-overflow 16 plus safe-alignment +8 (OVFL-B landed on master); matched stays 1272.
  • hit-refusals-grid.ts: GRID_OUT names the G1c fixtures and grid-aspect-ratio.

What passed (head 57be38a, master 7882845 merged in, CI regen run 38068030567)

  • pnpm typecheck: pass.
  • Platform-free, local: vitest run packages/dragon packages/layout, 128 files and 2980 tests pass. The only failing file is native-backends.test (no swiftc or kotlinc locally), which passes on CI below.
  • pnpm ci:test-files run 38074580587: 20 Chrome files pass, among them parity (573), native-host (363), grid-computed, all grid-corpus files, grid-fuzz, place-computed, hit-report, hit-lanes, chrome-ports, registry-claims, android-profile, fixture-reader and dist. native-backends passes (13).
  • lanes.test: 380 pass, 2 fail. P5 is the device step pending (ios layout-vectors-device: not run, as on master). The device-hit case fails on alias-paint, which ALIAS-2: -webkit-transform, -webkit-transform-origin and the -webkit- corner radii #249 added on master and which this PR does not touch.
  • PR checks: pass.

🤖 Generated with Claude Code

https://claude.ai/code/session_01DhER59LA5E9S5yeL8xkRHf


Generated by Claude Code

thejackshelton and others added 30 commits October 5, 2026 18:40
…s, the units.ts grid helpers and the G-P differential test

grid.ts from grid-g1a-engine (df45f80, ae4abde, cdcc301): Blink 145's grid placement, ranges and sets, track sizing,
item placement, content and self alignment, container intrinsic sizes and the first baseline, with 21 grid plants on
Ctx.gridFaults (layoutWithGridFaults). The G-P differential test runs every horizontal-tb environment of the frozen Chrome 145
grid corpus (docs/research/grid-spike/probe).
Changes for today's master:
- units.ts: the grid's saturating float helpers are clampedFloatMul and clampedFloatDiv (master's floatMul and floatDiv round
  without saturating); the duplicate FLOAT_MAX and FLOAT_EPSILON are dropped.
- gutters resolve calc() gaps with resolveLength (a calc gap reaches the engine now), an indefinite basis counting as 0.
- layoutContents takes bfcLineOffset 0 for a grid item (a formatting context root).
- a replaced grid item is refused (grid-replaced) until replaced grid items are proven.
- normal block-axis self-alignment of an item with an aspect ratio is start (aspect-ratio reaches the engine now; the corpus
  case a-stretch-aspect proves it in all 8 environments).
- rt-hit.ts and the inline tests build Ctx with NO_GRID_FAULTS.
- environment.ts: grid px track sizes and fit-content() limits clamp to the CSS length range like every other px length,
  also at DPR 1 (review of #197), with a saturation test.
- the G-P pins: the three DPR 2.625 known mismatches now match (KNOWN_MISMATCHES is empty), aspect-ratio cases match
  (hand 1256 -> 1272 matched), and a calc gap with a percentage is owned by CALC-p (the compiler refuses it everywhere).
…egen: fixed point after 2 passes): the translated engine with grid.ts
…-engine-v2): seven grid fixtures in ltr and rtl (placement, named areas, fr, intrinsic, alignment, sizing, nested) in the per-feature grid group; reject-display-grid moves to a grid in a column flex container and reject-grid-justify-items to an unproven block context; the tests that pinned display: grid as refused now pin display: inline-grid
… regen: fixed point after 3 passes): the grid fixtures' captures, emitted CSS and vectors, the profiles (+256 rows per target, none lost or changed), the north-star messages, the Tailwind sweep (+1 supported utility per target) and the WPT expectations
…opped or rescued rules (DRAGON_PIN_WRITE=1; only new lines, every existing pin unchanged)
…play: inline-grid (display: grid is proven now); same intent
…track_collection.cc, grid_track_sizing_algorithm.cc, grid_layout_algorithm.cc and layout_utils.cc (grid.ts and units.ts ports, the G-P deviation reference), and grid.ts on grid_area.h, fetched at 145.0.7632.6 with their sha256 and located ranges
…heavy-lease.sh pnpm regen: fixed point after 3 passes)
# Conflicts:
#	packages/dragon/test/media-wiring.test.ts
#	packages/dragon/test/s5.test.ts
#	packages/parity/test/fixture-reader.test.ts
… grid-g1a-fixtures-v2 (/tmp/heavy-lease.sh pnpm regen: fixed point after 3 passes)
…d on master) is display: inline-grid; same intent, an unsupported value
…-v2 (/tmp/heavy-lease.sh pnpm regen: fixed point after 3 passes)
…ter 2c48409) into grid-g1a-fixtures-v2 (/tmp/heavy-lease.sh pnpm regen: fixed point after 3 passes)
…k 145 (shorthands_custom.cc PlaceContent/PlaceItems/PlaceSelf::ParseShorthand): each part parses as its own longhand, so the longhand rules and refusals apply; place-content's lone baseline gives justify-content start. align-content: last baseline is invalid, as in Chrome (ConsumeFirstBaseline). place-computed.test is the dual computed check against Chrome over every one- and two-value combination of 26 alignment words for each shorthand, with three planted faults each caught on every item. Three fixtures in ltr and rtl: grid-self-values (every justify-items and align-items value, justify-self and align-self auto and center/end, legacy inheritance), grid-place, and grid-template-shorthands (grid-template, grid, the none values, spans on end lines)
…after 3 passes): the three fixtures' captures, emitted CSS and vectors, the profiles (+72 rows per target, none lost or changed) and the derived outputs
…flicts: unsupported.ts keeps INL1a's five inline codes and GRID's three, and drops master's grid-layout placeholder (the engine replaces it); intrinsic.ts sizes a grid container first, then INL1a's strut path; native-support.ts and line-breaks.ts take INL1a's placeIfcLines over its hoisted Ctx, which gains grid_NO_GRID_FAULTS, and the Kotlin imports are the union; grid.test.ts takes both imports (its local column span helper is renamed colSpan beside INL1a's span); inline.test.ts takes INL1a's call with gridFaults. Composition: buildGrid throws on any inline-level child (inline box or br), as INL1a's flex.ts does, instead of skipping it; INL1a's inline-box, inline-box-placelines and parity inline tests give their Ctx gridFaults
…ngine-v2 (/tmp/heavy-lease.sh pnpm regen: fixed point after 3 passes)
…ures-v2. parity.test.ts's row-key test takes the union of the text contexts (GRID's grid item contexts and INL1a's text-in-inline and text-beside-inline) and keeps GRID's grid box contexts
… into grid-g1a-fixtures-v2 (/tmp/heavy-lease.sh pnpm regen: fixed point after 3 passes)
…a) into grid-g1c-align (/tmp/heavy-lease.sh pnpm regen: fixed point after 3 passes)
#203) into grid-g1a-engine-v2. Conflict: rt-hit.ts imports keep GRID's NO_GRID_FAULTS and OVFL's isScrollContainer beside resolveBorder. Master's grid input (input.ts, validate.ts) is unchanged here; the engine reads it as is. Generated files keep the merge result until the CI regen

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WSkSiXMPXTAsNpS9BD9LDR
claude and others added 8 commits October 9, 2026 13:59
…iner by name (hitRefusal and hitTableOf's HitError): Blink paints grid items atomically in order-modified document order (css-grid-2 §9), which the hit table does not model, and on master layout refused every grid so no grid ever reached it. The compiler's R13 hit-model check refuses interaction rules on native in a case with a grid container whose display compiles (hitUnmodelledGrid; package GRID hit model). Tests: rt-hit.test (a grid program throws the named HitError and hitRefusal names it; the same tree as block flow is unaffected) and interaction.test (native refusal, web and non-grid unaffected, the hitUnmodelledNotRefused plant)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WSkSiXMPXTAsNpS9BD9LDR
Commands: pnpm regen --skip lanes-host --skip tw-sweep (macos-26) and pnpm regen --only lanes-host (xcode-27), alternated
to a fixed point, then pnpm regen --only tw-sweep (macos-26).
Base: 26d73e7
Run: https://github.com/compiled-run/dragoncss/actions/runs/37940805954
…/master b97cf50) into grid-g1a-fixtures-v2

Conflicts (both sides' intent kept):
- media-wiring.test: master's @unknown-thing enclosing at-rule with G1a's display: inline-grid probe.
- s5.test: G1a's inline-grid probe with master's CSS-wide keyword list (list-item, revert, revert-layer) plus grid.
- seams.test: master's OVFL reject retargets and G1a's reject-display-grid retarget; the milestone-1 specs but
  reject-display-grid hash to 7725fc41 (master's specs with reject-display-grid left out).
- fixture-reader.test: RETARGETED_AFTER_BASE holds both reject-display-grid (G1a) and reject-overflow-single-axis (OVFL).

Composition with #212's grid hit refusal (rt-hit.ts hitRefusal): the hit lane now leaves out the 14 G1a fixture cases
by name (hit-refusals-grid.ts GRID_OUT and GRID_REASON, judged by hit-report.test and lanes.test in the exact union).
Generated files keep the merge result until the CI regen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WSkSiXMPXTAsNpS9BD9LDR
Commands: pnpm regen --skip lanes-host --skip tw-sweep (macos-26) and pnpm regen --only lanes-host (xcode-27), alternated
to a fixed point, then pnpm regen --only tw-sweep (macos-26).
Base: 73dc191
Run: https://github.com/compiled-run/dragoncss/actions/runs/37950823661
…sedCases) and after HitError's "hit test: " prefix (caseHitTable throws it), as INLINE_REASON does; still anchored at the end

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WSkSiXMPXTAsNpS9BD9LDR
…inline-grid (display: grid is proven on web now, and the test pins that it decides true); same intent

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WSkSiXMPXTAsNpS9BD9LDR
…'s new Ctx call after merging origin/master)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DhER59LA5E9S5yeL8xkRHf
@thejackshelton thejackshelton self-assigned this Oct 10, 2026
claude and others added 13 commits October 10, 2026 10:17
…mum height (ratioFinalBlockSize, as block and flex do), proven by the new grid-aspect-ratio fixture; scroll-decidable refuses a grid container as overflow.ts does

Review findings on #253: the grid branch of block.ts used the ratio height as final, so content taller than the ratio overflowed where
Chrome grows the grid (css-sizing-4 automatic minimum). undecidedScrollContainers now mirrors overflowOf's grid refusal; the
scroll-decidable test gains a grid child shape and fails without the rule.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DhER59LA5E9S5yeL8xkRHf
Commands: pnpm regen --skip lanes-host --skip tw-sweep (macos-26) and pnpm regen --only lanes-host (xcode-27), alternated
to a fixed point, then pnpm regen --only tw-sweep (macos-26).
Base: f5b5622
Run: https://github.com/compiled-run/dragoncss/actions/runs/38043547657
Commands: pnpm regen --skip lanes-host --skip tw-sweep (macos-26) and pnpm regen --only lanes-host (xcode-27), alternated
to a fixed point, then pnpm regen --only tw-sweep (macos-26).
Base: 6d95f7b
Run: https://github.com/compiled-run/dragoncss/actions/runs/38049194088
- grid-corpus.ts: the compiler's refusal of a grid scroll container on native (scroll-decidable mirrors overflow.ts) is owned by
  G-OVF; the corpus pins move overflow-auto 24 to grid-scroll-overflow 16 and safe-alignment +8 on today's master (OVFL-B landed),
  matched unchanged at 1272.
- hit-refusals-grid.ts: GRID_OUT names the G1c fixtures and grid-aspect-ratio, which the hit lane refuses as grid containers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DhER59LA5E9S5yeL8xkRHf
… hit-report and lanes tests take the union of the stacking and grid hit refusals
Commands: pnpm regen --skip lanes-host --skip tw-sweep (macos-26) and pnpm regen --only lanes-host (xcode-27), alternated
to a fixed point, then pnpm regen --only tw-sweep (macos-26).
Base: dd32b45
Run: https://github.com/compiled-run/dragoncss/actions/runs/38054908669
Commands: pnpm regen --skip lanes-host --skip tw-sweep (macos-26) and pnpm regen --only lanes-host (xcode-27), alternated
to a fixed point, then pnpm regen --only tw-sweep (macos-26).
Base: 285a0ef
Run: https://github.com/compiled-run/dragoncss/actions/runs/38061188038
Commands: pnpm regen --skip lanes-host --skip tw-sweep (macos-26) and pnpm regen --only lanes-host (xcode-27), alternated
to a fixed point, then pnpm regen --only tw-sweep (macos-26).
Base: e0e98ce
Run: https://github.com/compiled-run/dragoncss/actions/runs/38068030567
@thejackshelton thejackshelton changed the title GRID G1a: grid layout on web, iOS and Android (engine #212 + fixtures #246, on master) GRID: grid layout on web, iOS and Android (engine, fixtures, place-* shorthands) Oct 10, 2026
@thejackshelton
thejackshelton merged commit 1137788 into master Oct 10, 2026
4 checks passed
thejackshelton pushed a commit that referenced this pull request Oct 10, 2026
…XTURES pin to the batch

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JzUCRZ7PRctKowaJ7qRxYa
thejackshelton pushed a commit that referenced this pull request Oct 10, 2026
Catch up with #253 (grid): the hit-report and lanes unions hold both
GRID_OUT and VISIBILITY_OUT, each with its reason.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012dJVP4456dCkJKMJUUf3xG
thejackshelton pushed a commit that referenced this pull request Oct 10, 2026
grid-aspect-ratio, grid-place, grid-self-values and grid-template-shorthands
(and their -rtl variants) came in with #253 without pins, so pixel-reference
failed for ios and android once the regen produced their PNGs. Written with
DRAGON_PIN_WRITE=1; additions only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AnXuaNziRHaTgQDqijdmBC
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.

2 participants