This repository has no description
bez specs custom-properties.md
5.2 kB


enabled: true touches: [crates/layout/src/bin/custom_properties_check.rs] depends-on: [] #

custom-properties: measure --* and var() against the three browsers #

The Web Features map (docs/web-features-map.json, feature custom-properties) lists it as none with 249 WPT test files under css/css-variables. Substitution of var(), fallbacks, inheritance and cycle detection all happen in Stylo's cascade (crates/dom/src/style.rs hands it the author stylesheets and reads back computed values), so the engine's layout should already see the substituted value in width, margin-*, padding-* and the rest. Nothing in the repository measures that, so the map cannot claim it and a regression would go unseen.

This spec adds the measurement and nothing else. The smallest thing that stands on its own is a corpus and a check binary: if the engine already agrees with the browsers, no engine code changes; if it does not, the disagreements are the finding, and fixing them is a separate spec, because a fix would edit crates/dom/src/computed.rs or crates/layout/src/fragment.rs, which the queued generated-function specs edit and which this spec's touches therefore leaves alone.

Scope. In: block-level, non-replaced boxes without text, in normal flow or absolutely positioned, where every declaration reaching layout is a length, a percentage or auto after substitution. Custom properties declared on the box itself, on an ancestor and on :root; inheritance and a child overriding an inherited value; var(--x) as the whole value and inside a shorthand (margin, padding); fallbacks (var(--missing, 10px), a fallback that is itself a var()); a custom property that references another; a cycle (--a: var(--b); --b: var(--a)), whose users compute as unset; an invalid substitution (width: var(--x) with --x: red), which is invalid at computed-value time and leaves width at auto; !important on a custom property; var() inside a length-only calc() (calc(var(--a) + 5px), no percentage, so Stylo folds it to a plain length). Out: registered custom properties (@property, the feature's css.at-rules.property keys), var() in properties the engine does not lay out, percentage-mixed calc() (its own spec, calc-lengths), env(), scripted setProperty and every test that reads getComputedStyle. Say so in the binary's module doc.

Build. One file, crates/layout/src/bin/custom_properties_check.rs, modelled on crates/layout/src/bin/fragment_check.rs. crates/layout/Cargo.toml needs no edit because src/bin/*.rs is discovered.

  • A const CASES: &[(&str, &str, &str)] of (id, category, html) with literal documents, since crates/harness/src/cases.rs Elem is pub(crate). Every document opens with the same zeroing rule the recipe corpus's RESET uses, so body margin does not enter the comparison. At least three cases per category above, and at least 40 in all.
  • The engine side is fragment_check.rs engine_dump copied, not shared: layout binaries cannot import each other.
  • The browser side is harness::oracle::Oracle::start and Oracle::dump. A case counts only when every browser dump that came back agrees with the others under harness::compare::compare (outcome.passed()); a case whose browsers disagree, or that a browser failed to dump, is dropped and listed with the reason. It passes when the engine agrees with every dump that counted.
  • A #[cfg(test)] module checks that ids are unique, categories are non-empty and every document parses with dom::Document::parse and yields a box tree. This is the part cargo test --workspace runs; the browsers are not needed.
  • The binary exits non-zero when fewer than 20 cases were compared, so an oracle that silently produced nothing cannot pass. It exits zero when some cases disagree with the engine; those cases are the result.

Where the result goes. In the binary's module doc: the counts (cases written, dropped with reasons, compared, passed, per category) and, for each failing case, its id and the one-line cause found in Stylo's output or in computed.rs. No other file records it, so the spec touches nothing shared. No docs/feature-map.json rule is added: an exact-key rule for a check-only result could only be hand-written, which the map treats as partial, and that file is edited by every queued generated-function spec.

Heavy steps go to a build lane and never run on the development box:

  1. bench/on-lane.sh --lane-only "$PWD" cargo test -p layout --bin custom_properties_check
  2. bench/on-lane.sh --lane-only "$PWD" cargo run --release -p layout --bin custom_properties_check

If no lane answers, report that instead of running either step locally.

The model-first steps the generated-function specs carry (ground-truth cache, generation loop, model batches through bench/batch-lane.sh) do not apply: this spec adds no generated function, since Stylo, not engine code, does the substitution, and the corpus is the entire deliverable.

Gates. cargo test --workspace passes. fragment_check --random stays 227/227: no engine source changes.