Skip to content

Add Selectors 4 state pseudo-classes, upstream WPT runner, modern toolchain - #167

Open
jdalton wants to merge 1 commit into
dperini:masterfrom
jdalton:selectors4-modernization
Open

Add Selectors 4 state pseudo-classes, upstream WPT runner, modern toolchain#167
jdalton wants to merge 1 commit into
dperini:masterfrom
jdalton:selectors4-modernization

Conversation

@jdalton

@jdalton jdalton commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator

This adds the Selectors 4 state pseudo-classes, a runner that checks nwsapi against the real web-platform-tests suite, and a toolchain refresh.

Four parts, in the order they matter:

Part What it does
src/nwsapi.js Wires :open, :modal, :fullscreen, :picture-in-picture, and the time-dimensional :current / :past / :future
test/upstream A Playwright runner over 41 WPT selector files, with a 375 entry expectations baseline
bench The old test/speed Benchmark.js presets ported to mitata, 9 groups and 201 selectors
toolchain pnpm 11.18.0, node >= 24, eslint 10 flat config, node build scripts, a rewritten Actions workflow

This is a large diff because the test runner and the toolchain came along with the selector work. If you would rather review the selector changes on their own, say so and I will split the runner and the toolchain into follow-ups.

The selector changes - which pseudo-classes landed, and why :closed is absent on purpose

The state pseudo-classes are wired against the elements that can actually carry the state, so :modal and :fullscreen do not match arbitrary nodes. The media-state pseudo-classes are scoped to media elements for the same reason, and isPlaying is exposed on the Snapshot so the check has something to read.

:current, :past, and :future parse as valid and never match. Selectors Level 5 defines them for time-dimensional documents, and a library with no timing model has nothing to match them against, so accepting them without matching is the behavior the spec asks for.

:closed is deliberately not implemented, which makes it a parse error. The CSSWG removed it and Chrome dropped it in 122, so treating it as valid would mean matching something no browser matches.

Two smaller fixes ride along: an isInstanceOf typo, and the README's sponsorship section, which had corrupted commit fragments in it.

How the WPT runner works - a pinned sparse checkout, and no gitlink

The suite comes from a sparse, shallow, blob:none web-platform-tests checkout pinned in .gitmodules. There is no gitlink committed. The ref is the pin of record, alongside an ls-tree manifest sha256, so the pin is reviewable as text rather than as an opaque commit id.

scripts/git-partial-submodule.mjs drives it with no dependencies and supports clone, verify, verify --deep, and restore-sparse. Its argv handling is hardened, since it shells out to git.

The runner is filterable while you work on one thing: WPT_FILTER narrows to an individual selector and WPT_SECTION narrows to a spec section. The expectations file records the current state so a change in results shows up as a diff rather than as a wall of failures.

Toolchain details - what to run, and what changed underneath

pnpm is pinned at 11.18.0 through packageManager, with engineStrict and saveExact set in pnpm-workspace.yaml so dependency versions stay exact. engines.node is >= 24.

The build scripts are plain node: terser for the minified build, and a clean script. eslint moves to 10 with a flat config. The Actions workflow is rewritten against the above.

For local serving over https, portless from vercel-labs serves the pages at https://nwsapi.localhost, which is what the browser tests point at.

…lchain

- src/nwsapi.js: wire :open, :modal, :fullscreen, :picture-in-picture and
  the time-dimensional :current/:past/:future (valid, never matching per
  Selectors Level 5); treat :closed as a parse error (removed by CSSWG,
  dropped in Chrome 122); fix isInstanceOf typo, expose isPlaying on the
  Snapshot, scope media-state pseudo-classes to media elements
- upstream/wpt: sparse+shallow+blob:none web-platform-tests checkout pinned
  in .gitmodules (gitlink-less; ref is the pin of record; ls-tree manifest
  sha256) with dependency-free scripts/git-partial-submodule.mjs
  (clone / verify / verify --deep / restore-sparse, argv-hardened)
- test/upstream: Playwright runner over 41 WPT files plus state-pseudo
  browser tests (47 tests, 375-entry expectations baseline), filterable by
  individual selector (WPT_FILTER) or spec section (WPT_SECTION)
- bench: legacy test/speed Benchmark.js presets (9 groups, 201 selectors)
  ported verbatim to mitata under node + jsdom with --preset/--selector
- toolchain: pnpm 11.18.0 pinned with exact deps and node >=24
  (engineStrict/saveExact in pnpm-workspace.yaml), eslint 10 flat config,
  node build scripts (terser min, clean), rewritten GitHub Actions
  workflow, portless (vercel-labs) for https://nwsapi.localhost serving
- README: repair corrupted sponsorship-commit fragments; document the new
  pseudo-classes, the :closed decision, and the development workflow
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