Skip to content

FORM-a 2/3: appearance as a longhand, and <button> as a CSS-painted control (block and flex), against Chrome - #78

Open
thejackshelton wants to merge 47 commits into
form-a-v2from
form-a3-v2
Open

thejackshelton wants to merge 47 commits into
form-a-v2from
form-a3-v2

Conversation

@thejackshelton

@thejackshelton thejackshelton commented Oct 3, 2026 •

Copy link
Copy Markdown
Contributor

FORM-a part 2 of 3 (T052, A2 and A3 in notes/T052J-form-a.md): appearance as a real longhand, and <button> as a CSS-painted control. It is stacked on FORM-a part 1 (#77) under the contract's stacked-slice exception. Its base is now form-a-v2 (#77's review-clean head a7a3267), so this diff is only this slice; GitHub moves it to master when #77 lands.

Phase 1 on #77 a7a3267 (2026-10-09)

Retargeted pins (this phase)

  • hit-report.test and lanes.test. The exact refusal union gains the 10 FORM-a controls cases. They are listed in the new hit-refusals-controls.ts with their reason; CONTROLS_REASON is anchored and allows only HitError's hit test: prefix. Intent kept: every left-out case is named with its own reason. hit-report failed on 66fefee (run 37974645063) with 48 cases left out against the 38 the old union named.
  • not-applicable-native.test. -webkit-appearance: none on a div now compiles on every target, through A3's appearance:none rows, so the "off the list" entry moves to its own test. Intent kept: -webkit-appearance never takes the not-applicable path. Unproven keywords stay blocked on every target with DRAGON_UNSUPPORTED_VALUE per target, and none and auto compile only through their rows on all three profiles. CI checks on 6507542 failed here: expected blocked, got checked.

Review fix (90ee2b2, High, ua/datasets.ts:427): inherited longhands a UA rule resets

The problem: the ELB-2 declared capture kept a longhand only when its value differed from unset under a default parent. Chrome's html.css control rules reset some inherited longhands to the value a default parent passes down anyway (line-height: normal, color: ButtonText), so the capture missed them. A <button> under an ancestor with another line-height or colour therefore inherited those values in Dragon, while Chrome resets them.

  • Capture (scripts/capture-ua-defaults.ts, fb3cdd6):
    • every element is also read under a parent with a non-default value for each inherited milestone longhand (INHERITED_SKEW);
    • a guard throws if a new inherited longhand has no skew, and each skew must actually change the parent;
    • a longhand that differs there from inheriting is recorded as a declared UA value, but only when its value is the same under every parent; otherwise the capture throws;
    • the unmodelled capture does the same for the inherited properties the control rules reset: font: -webkit-small-control's font-* longhands, letter-spacing, word-spacing, text-indent, text-transform, text-shadow, text-rendering and cursor;
    • self-consistency now also reproduces every key under the skewed parent, so a missed reset fails the capture. The new plant drop-inherited-reset (button's line-height) must fail it;
    • ButtonText is Chrome's own computed value in each scheme: rgb(0, 0, 0) in light, and the dark dataset's own value in dark.
  • Rows that change (regen 2ef8622, https://github.com/compiled-run/dragoncss/actions/runs/37982961694):
    • the button, input and input[type=range] keys' declared rows gain line-height: normal and color, and input gains text-align: start;
    • their unmodelled rows gain the reset list above.
    • Master-era row: ul gains its declared list-style-type: disc. It equals the initial value, so only the skewed capture finds it; in Chrome a ul under an ancestor with another list-style-type gives its items disc.
    • No earlier fixture's resolved values, layout input or web body change: controls-identity passes, and emitted CSS changes only by its digest header. Profile rows only gain the new cases.
  • Fixture controls-button-inherit (ltr and rtl) is the reviewer's probe, compared against Chrome: body with line-height 30px and rgb(200, 0, 0), and block buttons with font: inherit sizes. It covers a button with a block child that inherits the reset, an author line-height/colour that wins, and an author line-height. The probe needs an author border: without one, the UA outset border is refused (border-style: outset), so the review's probe as written does not compile. line-height: inherit has no profile row, so the author case uses explicit values. The hit lane refuses this fixture by name with the other controls cases.
  • Unit test: packages/dragon/test/controls-inherited.test.ts (platform-free). It fails on the old dataset. It pins:
    • the button key's line-height: normal and colour in both directions, light and dark;
    • the probe resolving to those values, and the button's contents inheriting them;
    • an author value winning;
    • the probe compiling on all three targets.
  • Not changed: userAgentTextFonts (the font-weight and font-style text fonts) is unchanged. A button inside a bold or italic UA ancestor is still refused on native, so that case stays fail-closed. The font-* resets go to userAgentUnmodelled as data. The non-milestone resets cannot reach the output, because Dragon refuses those properties when an author sets them.

Retargeted pins (review fix)

  • ua-elb2.test: the button and input unmodelled rows are now cursor plus the control reset list, pinned exactly in both directions. ul's declared list-style-type: disc is pinned too.
  • controls-identity.test: the controls group has 6 layout fixtures (was 5), now that controls-button-inherit is added.
  • glyph-clearance-pins.json: controls-button-inherit's lines are appended (49ce521, --floor-write run https://github.com/compiled-run/dragoncss/actions/runs/37989870975). These are additions only.

What passed at f19f68d

  • pnpm typecheck (local): pass.
  • Platform-free (local, npx vitest run): attributes, block-elements, media-runtime, paint-seams, dragon registry-claims, s4b, seams, ua-elb2, ua, not-applicable-native, controls-inherited, layout inline, rt-hit, layout rt-vectors. 14 files, 313 tests, all pass.
  • pnpm ci:test-files https://github.com/compiled-run/dragoncss/actions/runs/37990256398: 31 Chrome files pass:
    • android-profile, chrome-ports, controls-identity, device-lanes, dist, env-engine, fixture-reader, fonts-fixtures, glyph-calibration;
    • hit-capture-identity, hit-lanes, hit-report, parity inline, interaction-forced, line-breaks, parity media-runtime, media-sweep;
    • native-host, p6a-dash-oracle, parity, phrasing-blockified, pixel-reference, platform, pnt2-quads, pnt2-samples;
    • parity registry-claims, replaced-identity, replaced-paint, tree-faults, values, translate rt-vectors.
  • lanes.test (native): 367 pass. Its 1 failure is P5 "every device lane ran" (ios layout-vectors-device: not run), which is the device step, still pending.
  • CI checks at this head: success on attempt 3 (https://github.com/compiled-run/dragoncss/actions/runs/37990256257). Attempts 1 and 2 died at "Lint the landing workflows" on the Docker Hub pull rate limit, before any test ran.
  • pnpm pr:review 78 --once: exit 0. It reports UNREVIEWED because Macroscope is at its spending limit, with 0 unanswered findings.
  • Reviewed diff against form-a-v2 (outside .macroscope/ignore.md paths): about 148 KB.

Outside the spec (this phase)

  • scripts/capture-ua-defaults.ts beyond A2's plant retarget (the inherited-reset capture)
  • packages/dragon/test/controls-inherited.test.ts (new), packages/dragon/test/ua-elb2.test.ts
  • packages/parity/src/hit-capture.ts (identity strip of appearance at none)
  • packages/parity/test/hit-capture-identity.test.ts, hit-report.test.ts, lanes.test.ts, new hit-refusals-controls.ts
  • packages/dragon/test/not-applicable-native.test.ts

The sections below describe the slice as first opened. Its "What passed" figures were measured on the host at the earlier head; the section above is the current record.

What changed

  • A2: appearance and -webkit-appearance.
    • appearance is a real longhand (route 2a, as object-fit), with Chrome's legacy -webkit-appearance alias.
    • The grammar is narrowed to what Chrome 145 parses: it drops base, although webref's grammar allows it.
    • The UA capture records it: auto on button, input and input[type=range], none elsewhere. The forced-value reference never takes the declared appearance, so the forced display: inline-block rows stay as they were.
    • scripts/check-appearance-migration.ts proves the output migration only adds appearance (details under "What passed"). It now counts only rules outside @media, because an @media rule writes only the longhands that differ (MQ-a). A new emitted-media plant covers that.
  • A3: <button>.
    • What is supported: a button whose used appearance is CSS painting. That is appearance: none, or auto with an author background or border (R11, the devolve rule, from FORM-0's probe).
    • Layout:
      • A block button lowers to A1's button-block control and centres its contents safely.
      • A flex button is a plain flex container.
      • In block flow, a block button's auto width fits its content. Chrome fixtures confirmed this before the block.ts hunk was added (amendment A3/A4-1).
    • The appearance display adjustment (amendment A3-1) is in computed.ts. Chrome 145 was measured on every display value:
      • inline, math and every table-internal display become inline-block;
      • table and list-item become block.
    • The button's UA display: inline-block is an ordinary UA value that an author display overrides. Any other forced row on the button key throws.
    • type is handled on button: a fixture with every variant matches Chrome.
    • Refused:
      • a theme-painted button (FORM-b);
      • an inline-level button (RF-INL);
      • an absolutely positioned button;
      • an auto-width flex button in block flow (T156);
      • the UA control font (R13).
  • Port registry. Chrome's layout_theme.cc is LGPL (class A). computed.ts states the display table as the Chrome 145 measurement, with LayoutTheme::AdjustStyle cited as a reference only. docs/ports.json lists computed.ts APPEARANCE_DISPLAY and analysis/elements/controls.ts as references, adds controls-identity.test.ts as a proof, and gains the block_layout_algorithm.cc 3341-3352 range that block.ts cites.
  • Fixtures (controls group):
    • layout: controls-button-block, -flex, -demo, -type and controls-appearance-display, each in ltr and rtl;
    • rejects: reject-controls-button-theme, -inline, -abspos, -flex-width and -font, and reject-controls-appearance-inline.

What passed (host, at the head of this PR)

  • pnpm regen is at a fixed point: "fixed point after 1 passes, 19 steps run", no files changed. Generated outputs are in their own commits, each naming its command.

  • pnpm typecheck passes.

  • pnpm test (full suite): 161 of 166 files pass, and 3709 tests pass with 1 skipped.

    • The 12 failing tests and 1 failing suite are the device-record checks only: lanes.test, lanes-records.test, lanes-concurrent.test, device-failures.test and p6a-promotion.test. They fail only because the committed packages/parity/out/lanes.json is host-only; the train's device run for this position commits it.
  • The controls cases pass both host lanes in ltr and rtl, and every reject passes. Turning off the fit-content hunk or the display adjustment makes them fail.

  • controls-identity.test: every pre-existing FIXTURES case's resolved values, native layout input and web body deep-equal part 1's. It also checks the A3 helpers on their edges.

  • node --conditions=dragon-internal scripts/check-appearance-migration.ts 75bf9e5c5: every file that existed at part 1 differs only by appearance:

    • captures: 1966 files, 35070 keys;
    • emitted CSS: 395 files, 8184 declarations outside @media;
    • UA data: 2 files, 114 entries.

    Engine inputs, Chrome line breaks and the pixel manifest's existing entries are unchanged. The check's only other failures are the 180 added controls-* files, which come from A3, not A2. All 13 plants fail as required.

  • Profile rows only gain rows and proof cases.

  • North star (errors 333 → 323):

    • the <button> element errors go 7 → 0, and the type errors on buttons 7 → 0;
    • -webkit-appearance: none is no longer an error on web and ios;
    • the demo buttons gain 8 UA-control-font errors per target. The demo's font: inherit shorthand is unsupported, so they keep Chrome's control font. This is recorded on the board as TXT-W2 (T147 part 2).

Changed tests and checks, with reasons

  • pixel-reference.test: the glyph-clearance pins add the 10 controls cases' own terms, because their button text sits close to button edges:

    • ios and android at DPR 2: edge +68, edge:glyph +22, border +6, rescued edge +67;
    • at DPR 3: edge +66, edge:glyph +22, border +6, rescued edge +64, rescued border +4;
    • android at DPR 2.625: edge +68, edge:glyph +22, border +6, rescued edge +60.

    With the controls cases filtered out, the earlier pins hold exactly.

  • block-elements.test: the element table lets the control tags read the ELB-2 element keys (button reads the button key's tag).

  • seams.test: LONGHANDS is 71 → 72 (appearance). Appearance and -webkit-appearance parsing are pinned to the Chrome 145 probe: 14 keywords kept as written; base, the older -webkit- keywords, two keywords and an unknown word dropped.

  • paint-seams.test and s4b.test: appearance joins the paint longhands, with its two keywords.

  • css-escapes.test: the escaped-twin count goes 13834 → 13904 (+70, the appearance and -webkit-appearance grammar words and names).

  • ua.test and ua-elb2.test: the element-key longhand lists gain appearance on button, input and input[type=range]. Appearance moves from the unmodelled rows to the declared rows as auto; the forced display pins are unchanged.

  • attributes.test: type is handled on button; on other elements it stays refused, naming FORM-a.

  • scripts/capture-ua-defaults.ts: the drop-unmodelled plant is retargeted from button appearance (now modelled) to cursor. A guard makes the plant fail loudly when its target is not an unmodelled row.

  • check-appearance-migration.ts: counts only rules outside @media, plus the emitted-media plant. No check is removed.

Catch-up onto master (2026-10-05)

Merged form-a-v2 after its catch-up (#72's last review head, then origin/master f50b6ad with HOTSPOT-SPLIT #137/#138). Regenerated in 1fa2e6c (pnpm regen). The "What passed" section above was measured at the earlier head.

  • packages/layout/src/block.ts: the block button's fit-content auto width now clamps against master's stretched size (Blink-order margins, layout opportunity). The 3341-3352 range is kept in docs/ports.json beside master's.
  • PIN-DERIVE: master's derived floor and pin checks replace this PR's literal lists in seams, paint-seams, s4b, css-escapes and pixel-reference. New entries were appended with the write flags, and nothing is removed or lowered:
    • appearance and -webkit-appearance in seams-floor.json (longhands, shorthands, paint role);
    • the two escaped twins in css-escapes-floor.json;
    • the p1 corpus counts in p1-floor.json;
    • the controls cases' lines in glyph-clearance-pins.json (37bd1cf, DRAGON_PIN_WRITE=1). Every existing case line is unchanged; the chain checked this before committing.
  • s4b's paint-pair map gains appearance's two keywords (none, auto).
  • HOTSPOT-SPLIT: appearance: DISCRETE is in animation-kinds/box.ts (Chrome 145's interpolable snapshot lists appearance and -webkit-appearance as not interpolable). The controls group is one sorted GROUPS line, so it now runs after the legacy groups, in id order. No diagnostic codes, faults or regen steps are added.

Passed at c201360 (no local full test, per the lane contract): pnpm typecheck; targeted tests (this stack's tests, the layout package, parity, values, replaced-identity, controls-identity, pixel-reference, chrome-ports, css-escapes, seams, s4b, paint-seams, registry-claims, diagnostics, animation-kinds, regen and corpus-dpr): 64 of 64 files, 1916 tests passed, 2 skipped.

Reviewed size: about 104 KB (41 files) against part 1.

🤖 Generated with Claude Code


Generated by Claude Code

… longhand (route 2a, as object-fit)

appearance joins the box family after object-position (paint aspect, layout false; css-ui-4 §7.1) and the grammar subset.
gen-css-grammar.ts narrows webref's appearance grammar to what Chrome 145 parses (CHROME_SYNTAX: base dropped, probed 2026-10-01:
declared empty and computed none in Chrome; base-select and every <compat-auto>/<compat-special> keyword kept and computed as
written), failing if webref's grammar moves. Retargeted pins: seams LONGHANDS and paint role (+appearance), paint-seams longhand
count 71 -> 72, s4b paint role (+appearance, with none/auto as its two values), css-escapes twin count 13788 -> 13823 (+35 from
appearance's grammar words; every twin still reads as Chrome reads it).
…mmar:gen): appearance, <compat-auto> and <compat-special>
… the UA capture plants cannot pass silently

-webkit-appearance is a box-family alias of appearance (shorthands/box.ts, the grid-gap -> gap precedent; PM T052J-A2 amendment),
with the same Chrome-narrowed grammar. capture-ua-defaults.ts: drop-unmodelled now deletes button's cursor, since appearance is a
modelled longhand and leaves the unmodelled rows; every plant throws when its row entry is missing, so a plant that deletes nothing
can no longer pass. Retargeted pins: seams SHORTHANDS order (+-webkit-appearance after padding), css-escapes twin count
13823 -> 13858 (+35 from the alias's grammar words; every twin still reads as Chrome reads it).
…rance

Blink LayoutTheme::AdjustStyle makes any element whose appearance is not none inline-block (inline, inline-table, table-row,
table-cell) or block (table, list-item; probed in Chrome 145 on a span). With appearance now a declared longhand, giving it to the
dragon-unstyled reference made the reference inline-block too, so button, input and input[type=range] lost their forced
display: inline-block, which Chrome keeps under appearance: none as well (probed). The reference takes every declared value but
appearance, and a declared appearance is never reported as forced; the forced rows are byte-identical to before.
… adds only appearance (initial none, or the UA auto moved from the unmodelled to the declared rows on button, input and input[type=range]); engine inputs, line breaks and pixel manifests unchanged; 12 plants; --ua-plant runs ua:capture --check --plant drop-unmodelled and requires it to fail naming button's cursor
…apture, parity:capture, profile:rows, parity:capture, layout:vectors, parity:dpr-capture, layout:dpr-vectors, layout:break-vectors, parity:break-capture, parity:pixel-capture, layout:paint-vectors, native:gen, parity:lanes -- --run-host, node --conditions=dragon-internal examples/music-player/tools/check.ts, parity:glyph-b3 -- --write-bottom-pins, wpt:check -- --target web (every entry matches), tw:sweep (grammar:gen was committed with the source). node scripts/check-appearance-migration.ts e009c15 exits 0: captures 1850 of 1850 files changed, only by 34050 "appearance": "none" keys; emitted 366 of 366, 7929 "appearance: none;" declarations; UA 2 files, 114 entries (computed rows none, or auto on button, input and input[type=range], whose appearance moved from the unmodelled to the declared rows; forced rows unchanged); engine inputs 4623 files, line breaks 1380 and the pixel manifest unchanged; no file added. Profiles, lanes.json, native generated sources (digests 38f0cb30, d1ea5e56) and bottom pins unchanged. North star: -webkit-appearance: none moves from DRAGON_UNSUPPORTED_PROPERTY (target-independent, 1) to DRAGON_UNSUPPORTED_VALUE per target (no appearance profile row yet): errors 853 -> 854, UNSUPPORTED_PROPERTY 60 -> 58, blanked declarations 63 -> 61. Tailwind sweep: only appearance-auto and appearance-none change blocker (UNSUPPORTED_PROPERTY -> UNSUPPORTED_VALUE, profile appearance:*); supported stays web and ios 2230, android 0; the rest is the fix-table reindex.
…and lists gain appearance on button, input and input[type=range]; ua-elb2.test's unmodelled rows lose it and the declared rows pin it as auto in both directions (the forced display: inline-block pins hold unchanged); seams.test pins appearance and -webkit-appearance parsing to the Chrome 145 probe (14 keywords kept as written, base, the older -webkit- keywords, two keywords and an unknown word dropped)
…ted control

- elements/controls.ts: button is a supported tag reading the ELB-2 button key; its used appearance follows Blink
  IsControlStyled (R11): appearance none, or auto with an author background or border, paints as CSS boxes; anything else is the
  platform theme, refused naming FORM-b.
- datasets.ts: UaKey gains the control key button; uaRows reads the element-key tables. Its forced display: inline-block is
  html.css's declared display (an author display wins, probed), so it becomes a declared UA value; any other forced row throws.
- computed.ts and resolve.ts (A3-1): Blink LayoutTheme::AdjustStyle: an appearance other than none makes inline, inline-table,
  math and the table-internal displays inline-block, and table and list-item block, before blockification (measured on every
  display value with appearance auto and none in Chrome 145).
- computed-checks.ts: a button that is themed (FORM-b), inline-level (RF-INL), of any display but block or flex, absolutely
  positioned or holding an absolutely positioned box (control-out-of-flow), an auto-width flex button in block flow, or using
  the UA control font (R13) is refused. color-scheme is not modelled, so no dark system colour can reach a used value.
- ios-layout.ts: a block button lowers to a button-block control box; a flex button is a plain flex box.
- block.ts (A3/A4-1): a block button's auto width fits its content in block flow (block_layout_algorithm.cc:3341-3352).
- attributes.ts: type is handled on button (it changes only activation; controls-button-type proves the boxes).
- Fixture group controls: button block, flex, demo, type and the appearance display adjustment, ltr and rtl, plus 6 rejects;
  controls-identity.test.ts proves every earlier case unchanged.
…ws, parity:capture, profile:rows, layout:vectors, parity:dpr-capture, layout:dpr-vectors, layout:break-vectors, parity:break-capture, parity:pixel-capture, layout:paint-vectors, native:gen (block.ts fit-content hunk; P1 digest 38f0cb30 unchanged, extended digest d1ea5e56 -> 3f8c6127 from the 10 new cases' vectors), parity:lanes -- --run-host, node --conditions=dragon-internal examples/music-player/tools/check.ts, parity:glyph-b3 -- --write-bottom-pins, wpt:check -- --target web, tw:sweep; a second parity:capture and parity:dpr-capture leave every output unchanged. Every earlier capture, vector and break vector is byte-identical (no modified or deleted file under packages/layout/vectors, packages/parity/expected or expected-dpr against form-a2); emitted CSS changes only by its compilation digest header; profile rows only gain rows and proof cases (appearance auto and none in paint/ltr and rtl, font-family and font-size inherit, and the button contexts). packages/wpt/expectations/web.json: only grid-button-001's not-runnable reason, which wpt:check reported changed (its button is now supported, so it is refused at display: grid), rewritten by hand from the Chrome-free wpt:check output with its status unchanged. Tailwind sweep web and ios 2230 -> 2232 supported. North star: <button> elements 0 errors (51 -> 61 supported elements), type attributes on buttons 0.
…nce display adjustment against the Chrome 145 probe (auto and button adjust, none leaves every display), the button key's declared display: inline-block and the throw on any other or missing forced row, and the R11 used appearance (inherited background stays theme)
…d in the receipt): block-elements.test's element table lets the control tags read the ELB-2 element keys (button reads the button key's tag); pixel-reference.test's glyph-clearance pins gain only the 10 controls cases' dropped and rescued rules (button text close to button edges): with controls-* filtered out the old pins hold exactly
…pm run wpt:run -- --target web, then pnpm run wpt:update-expectations -- --target web, from form-a2's web.json): the regenerated file is byte-identical to the one committed in 5e4d1f5 (only grid-button-001's not-runnable reason differs from form-a2: DRAGON_UNSUPPORTED_VALUE:display:grid, status unchanged), and packages/wpt/snapshots is unchanged, so this commit records the regeneration and changes no file
…orm-a3, resolved by intent: ios-layout.ts refuses a control root and lowers through master's rootFontSizeOf; gen-css-grammar.ts keeps A2's CHROME_SYNTAX narrowing beside master's SYNTAX_EXTENSIONS; pins: LONGHANDS 72 (aspect-ratio, object-fit, object-position, appearance), css-escapes twins 13904 (+4, +52, +70), pixel-reference takes the merged side (recomputed after regen); generated outputs take the merged side, rebuilt by pnpm regen
…egistry) into form-a3-v2, resolved by intent: attributes.test keeps A3's button type test beside REPL-a Phase B's iframe src title; resolve.ts composes master's forcedOver with A3's button rows unchanged; generated outputs take the merged side, rebuilt by pnpm regen
… (class A), so computed.ts states the appearance display table as the Chrome 145 measurement it is (controls-button-display fixtures), with AdjustStyle as a reference only; docs/ports.json lists computed.ts APPEARANCE_DISPLAY and elements/controls.ts as references of layout_theme.cc, its AdjustStyle range, and controls-identity.test.ts as a proof
…A3's block.ts fit-content hunk cites (blockLevelInlineSize is already listed)
…nerated outputs take the merged side, rebuilt by pnpm regen
…@media writes only the longhands that differ, so it gains no appearance; count only rules outside @media (as check-object-fit-migration does) and add the emitted-media plant (an appearance declaration planted in an @media rule must fail). Before the fix the 18 media-* emitted files failed against form-a-v2
…regen, fixed point after 3 passes): grammar, captures, DPR and font captures, emitted CSS, profiles, A3's controls vectors on master's V2 input shape, pixel manifest (only the 10 controls cases added), translated engine (A3's block.ts hunk), north-star check, Tailwind sweep, glyph bottom pins, media sweep, host lanes.json. node --conditions=dragon-internal scripts/check-appearance-migration.ts 75bf9e5: every file at the base changes only by appearance (captures 1966 files, 35070 keys; emitted 395 files, 8184 declarations outside @media; UA 2 files, 114 entries); engine inputs, line breaks and pixel manifest entries unchanged; its only other failures are the 180 added controls-* files of A3 (A2 alone adds none)
…st base had): the glyph-clearance pins compose additively, REPL-a's and INL1a's terms plus the 10 controls cases' own, the same terms as on form-a3 022fcc7 (ios and android DPR 2: edge +68, edge:glyph +22, border +6, rescued edge +67; DPR 3: +66, +22, +6, rescued edge +64 and border +4; android 2.625: +68, +22, +6, rescued edge +60); with the controls cases filtered out the REPL-a pins hold exactly
…b1, PNT2 1-2, PIN-DERIVE) into form-a3-v2

Resolutions by intent:
- block.ts: a block button's fit-content auto width now clamps against master's stretched size (Blink-order margins, layout opportunity).
- docs/ports.json: block_layout_algorithm.cc keeps both sides' ranges (3341-3352 beside master's 2799-2804 and 3283-3304).
- seams, paint-seams, s4b, css-escapes, pixel-reference tests: master's derived floor and pin checks replace FORM-a's literal lists; appearance and -webkit-appearance appended to seams-floor.json (DRAGON_FLOOR_WRITE=1); s4b's paint pair map gains appearance's two keywords.
- animation-kinds.ts: appearance is discrete (Chrome 145 snapshot: interpolable false).
- fixture groups: controls stays after replaced, before values and states.
Generated outputs take the merged side; the following regen commit rebuilds them, and the controls cases' glyph-clearance pins are appended with DRAGON_PIN_WRITE=1 after it.
…OTSPOT-SPLIT #137/#138) into form-a3-v2

Per the HOTSPOT-SPLIT migration note: master's animation-kinds.ts and fixtures.ts are taken whole; appearance's kind (discrete, Chrome 145 snapshot) moves to animation-kinds/box.ts, and the controls group is one sorted GROUPS line (it now runs after the legacy groups, in id order). Generated outputs take the merged side, rebuilt by the following regen.
…into form-a3-v2; generated outputs take form-a-v2's side, rebuilt by pnpm regen
…ITE=1 vitest run packages/parity/test/pixel-reference.test.ts); every existing case line unchanged
…ted and floor tests): packages/parity/test/css-escapes-floor.json packages/translate/test/p1-floor.json
…erated) into form-a3-v2. Conflicts: ios-layout.ts lowerBox keeps GRID's gridParent and grid styles and FORM-a3's ControlBox return (a button lowered as a grid item keeps its placement); the root check keeps FORM-a3's control-box refusal; fixtures.ts GROUPS keeps controls and master's ctx-proof, display-legacy, env; p1-floor.json takes the larger counts (form-a-v2's)
…ted and floor tests): packages/translate/test/p1-floor.json
…1a C2) into form-a3-v2. Conflicts: ios-layout.ts lowerBox takes INL1a's Lowerer and inline runs and still returns a block <button> as a ControlBox, with the strut INL1a gives a box (a control child is not inline content); computed-checks.ts runs INL1a's checkInline on buttons too, beside checkButton, and keeps the button walk state
…ted and floor tests): packages/translate/test/p1-floor.json
claude added 2 commits October 9, 2026 17:44
…rm-a3-v2

Conflicts:
- computed-checks.ts: checkComputed's walk keeps #77's scroller (percent-relative inside scroll containers), the PAINT_VALUES
  refusals and GEN-b's faults, and A3's button walk state (the enclosing button and the parent display, which display: contents
  passes through); checkButton and checkInsideButton run as before.
- resolve.ts: the computed.ts import takes both appearanceDisplay (A3) and computeLists (GEN-b).
- attributes.ts: HANDLED_ATTRIBUTES keeps button type (A3) and GEN-c's ol start and reversed and li value.
- ios-layout.ts lowerTree: lowers the root with #77's propagated viewport overflow source and still refuses a root that lowers to
  a control box.

#77 now refuses a form control in rt-hit.ts hitRefusal and hitTableOf by name (controlRefusal), so the hit lane leaves A3's
controls-* cases out with that reason.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WSkSiXMPXTAsNpS9BD9LDR
…e identity base, is removed only at its initial value none (JSON key and emitted declaration); auto and any other value stay in the text, so such a file no longer hashes to the base. hit-capture-identity.test pins it (fails without the fix: every base capture and emitted file differs by appearance: none)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WSkSiXMPXTAsNpS9BD9LDR
@thejackshelton
thejackshelton changed the base branch from review/form-a-v2 to form-a-v2 October 9, 2026 17:44
github-actions Bot and others added 8 commits October 9, 2026 18:36
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: e0ed79c
Run: https://github.com/compiled-run/dragoncss/actions/runs/37968435334
…s rt-hit.ts refuses a form control by name, so the hit lane leaves out A3's 10 controls-* cases. hit-refusals-controls.ts lists them with their reason; hit-report.test and lanes.test judge the exact union of the transform, radius, INL1a inline and controls refusals, each with its own reason (hit-report failed on 66fefee, ci:test-files run 37974645063: 48 left out against the 38 the old union named)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WSkSiXMPXTAsNpS9BD9LDR
…(a pin retarget). Since A2 the alias reaches the appearance longhand, refused per target as a value, and A3's controls fixtures prove none and auto (the appearance:* profile rows), so '-webkit-appearance: none' on a div now compiles on every target (CI checks on 6507542: expected blocked, got checked). The intent is kept: -webkit-appearance never takes the not-applicable path; unproven keywords (button, textfield, menulist-button) stay refused on every target with DRAGON_UNSUPPORTED_VALUE per target, and none and auto compile only through their rows on all three profiles

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WSkSiXMPXTAsNpS9BD9LDR
…inherited longhands a UA rule resets to their default

The ELB-2 declared capture kept a longhand only when it differed from "unset" under a default parent. Chrome's html.css control
rules reset inherited longhands to the value a default parent passes down anyway (line-height: normal, color: ButtonText), so the
button key had neither, and a button under an ancestor with another line-height or colour inherited them in Dragon while Chrome
reset them.

scripts/capture-ua-defaults.ts now reads every element under a parent with a non-default value for each inherited milestone
longhand (INHERITED_SKEW; a guard throws if a new inherited longhand has no skew, and each skew must change the parent). An inherited
longhand whose value there differs from inheriting is a UA declaration, recorded when its value is the same under every parent
(else the capture throws), and the declared-versus-captured check counts these resets. The unmodelled capture does the same for
the inherited properties no longhand models that the control rules reset (font: -webkit-small-control's font-* longhands,
letter-spacing, word-spacing, text-transform, text-indent, text-shadow, text-rendering, cursor). Self-consistency now also
reproduces every key under the skewed parent, so a missed reset fails the capture; plant drop-inherited-reset (button's
line-height) must fail it. ButtonText is Chrome's own computed value in each scheme's dataset.

Tests: dragon controls-inherited.test.ts (platform-free) pins the button key's line-height: normal and color in both directions,
light and dark, and the review's probe resolving to them (and its contents inheriting them) under body line-height 30px and red;
it fails on the committed dataset. Parity fixture controls-button-inherit (ltr and rtl) compares the probe against Chrome; the hit
lane refuses it by name with the other controls cases. The probe needs an author border: the UA outset border is refused.

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: fb3cdd6
Run: https://github.com/compiled-run/dragoncss/actions/runs/37982961694
…: the button and input keys' unmodelled rows gain the inherited properties html.css's control rules reset to their defaults (font: -webkit-small-control's font-* longhands, letter-spacing, word-spacing, text-indent, text-transform, text-shadow, text-rendering) beside cursor, pinned exactly in both directions; ul's declared list-style-type: disc, a master-era row the skewed capture now finds, is pinned

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WSkSiXMPXTAsNpS9BD9LDR
…r and rtl (DRAGON_PIN_WRITE=1 through pnpm ci:test-files 5b585f1 packages/parity/test/pixel-reference.test.ts --floor-write, run 37989870975); additions only

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WSkSiXMPXTAsNpS9BD9LDR
… -> 6 with controls-button-inherit (a pin retarget; the earlier cases' identity checks are unchanged and pass)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WSkSiXMPXTAsNpS9BD9LDR

This branch has not been deployed

No deployments
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