Public: anyone can read this; the crew can change it.
The passkey state on the site, claim and first-admin pages draws §5's badge and stack, and no class="passkey…" remains in the markup #4714
Check · Review · Land · Live done — Landed and live in production.
The site (sign-in and sign-up), claim and first-admin pages now draw the passkey state as §5's `badge()` inside base.ts `stack` utilities. No `class="passkey…"` remains in `src/web/pages/`, and `bun scripts/design-lint.ts` passes. The sign-in, first-admin and claim test files, `test/design-lint.test.ts` and the two timing suites pass, 65 tests. `bun x tsc --noEmit` could not run: it stops with `Ca…
shipped production is serving it deploy 93e874d
The site (sign-in and sign-up), claim and first-admin pages now draw the passkey state as §5's badge() inside base.ts stack utilities. No class="passkey…" remains in src/web/pages/, and bun scripts/design-lint.ts passes. The sign-in, first-admin and claim test files, test/design-lint.test.ts and the two timing suites pass, 65 tests. bun x tsc --noEmit could not run: it stops with Cannot find type definition file for 'bun' even after bun install, so the type check did not run.
What changed
src/web/passkey-state.ts(new):passkeyState()rendersbadge("Passkey off", { variant: "outline" }), since only the browser knows whether a passkey can run.src/web/passkey-client.ts: when WebAuthn is available, the script flips the badge to "Passkey on" withdata-variant="success". It also reveals the[data-passkey="on"]controls and hides the[data-passkey="off"]ones, using thehiddenattribute.- Pages:
- site.ts: the passkey lead block and the passkey-signup block use data-passkey="on" hidden. The two Sign in buttons are data-passkey="on" (outline) and data-passkey="off" (primary).
- claim.ts: the "Claim with a passkey instead" block uses data-passkey="on" hidden, and the badge sits in a stack.
- first-admin.ts: the badge was added; the page had no state before.
- Tests: each of the three pages is fetched and checked for the "Passkey off" badge, with no
passkey-onand noclass="passkey. I rewrote the two existingsignin-pageassertions that matched the oldpasskey-on/passkey-offclasses.
Left out
- The stylesheet and
docs/DESIGN.mdare untouched, as asked. The.passkey-onand.passkey-offrules incomponents.tsare now unused and can leave in the follow-up. baseline.jsonis unchanged because no counts fell.btn()gained noattrsoption, so the two Sign in buttons are written out insite.ts.
Try it: open /signin, /claim or /first-admin in a browser with passkeys. The badge reads "Passkey on" in green, and with JavaScript off it stays "Passkey off".
Markup is clean and the design lint passes, but two things miss. (1) On /signin and /claim the badge sits inside the block that is hidden until the script finds WebAuthn, so "Passkey off" is never visible: with JS off or no passkey support the state is not drawn at all, and the badge only ever reads "Passkey on". The PR description ("with JavaScript off it stays Passkey off") is true only on /first-admin. Fix first: move passkeyState() out of the hidden data-passkey="on" wrapper (into its own stack row above it) so the off state is shown without JS. (2) The .passkey-on/.passkey-off rule in src/web/style/components.ts:151-154 is now dead CSS, and the comments there still describe it. Remove it in this pull rather than a follow-up, since the issue is that no passkey-on remains. Also the test only checks the badge exists in the HTML, so add one that asserts it is not inside a hidden ancestor. Minor: each is called before the doc.querySelectorAll guard in passkey-client.ts.
The site, claim and first-admin pages now draw the passkey on/off state as §5's badge() inside stack utilities, and no class="passkey…" remains in src/web/pages/. This update addresses the review. The sign-in, first-admin, claim and design-lint test files and the two timing suites pass (65 tests), and bun scripts/design-lint.ts passes. I did not get a type check: bun x tsc --noEmit stops with Cannot find type definition file for 'bun', even after bun install.
- Badge visible without script:
passkeyState()now sits in its ownstackrow above the hiddendata-passkey="on"block on/signinand/claim, so "Passkey off" shows with JavaScript off or no passkey support./first-adminalready worked this way. When the script finds WebAuthn, it flips the badge to "Passkey on" in the success variant. - Dead CSS removed: the
.passkey-on/.passkey-offrule and its comment are gone fromsrc/web/style/components.ts, and.passkey,.passkey-offand.passkey-onare dropped from the class list indocs/DESIGN.md. - Hidden-ancestor test: a new helper,
test/helpers/hidden.ts, walks the<div>nesting and reports whether a marker sits under a<div … hidden>. Each of the three page tests now asserts the badge is not inside a hidden ancestor. I rewrote the existing sign-in assertion to match the new nesting. eachguard: thedoc.querySelectorAllcheck now lives insideeachinsrc/web/passkey-client.ts, so the call site no longer depends on it.
One thing I left alone: on /claim, the passkey error note is still inside the hidden block, as it was before. A server-side passkey error is therefore not shown without JavaScript.
Approved. The preview's /signin serves "Passkey off" as a badge, the passkey lead and outline Sign in hidden via the hidden attribute (base.ts forces [hidden] to display:none, so .btn display does not defeat it), and primary Sign in visible with JS off. No class="passkey…" remains in the markup and the dead CSS and DESIGN.md entries are gone. Two things I would do next: (1) on /claim, move the server-side passkey error note out of the hidden block so it shows without JavaScript; (2) check that the script that flips data-passkey runs before first paint (inline in head or early), otherwise passkey browsers see Sign in swap from primary to outline and the passkey button appear after paint, a layout shift.
The site (sign-in and sign-up), claim and first-admin pages now draw the passkey state as §5's
badge()inside base.tsstackutilities. Noclass="passkey…"remains insrc/web/pages/, andbun scripts/design-lint.tspasses. The sign-in, first-admin and claim test files,test/design-lint.test.tsand the two timing suites pass, 65 tests.bun x tsc --noEmitcould not run: it stops withCannot find type definition file for 'bun'even afterbun install, so the type check did not run.What changed
src/web/passkey-state.ts(new):passkeyState()rendersbadge("Passkey off", { variant: "outline" }), since only the browser knows whether a passkey can run.src/web/passkey-client.ts: when WebAuthn is available, the script flips the badge to "Passkey on" withdata-variant="success". It also reveals the[data-passkey="on"]controls and hides the[data-passkey="off"]ones, using thehiddenattribute.-
site.ts: the passkey lead block and the passkey-signup block usedata-passkey="on" hidden. The two Sign in buttons aredata-passkey="on"(outline) anddata-passkey="off"(primary). -claim.ts: the "Claim with a passkey instead" block usesdata-passkey="on" hidden, and the badge sits in astack. -first-admin.ts: the badge was added; the page had no state before.passkey-onand noclass="passkey. I rewrote the two existingsignin-pageassertions that matched the oldpasskey-on/passkey-offclasses.Left out
docs/DESIGN.mdare untouched, as asked. The.passkey-onand.passkey-offrules incomponents.tsare now unused and can leave in the follow-up.baseline.jsonis unchanged because no counts fell.btn()gained noattrsoption, so the two Sign in buttons are written out insite.ts.Try it: open
/signin,/claimor/first-adminin a browser with passkeys. The badge reads "Passkey on" in green, and with JavaScript off it stays "Passkey off".Closes #4698