From d0e1de96b88fbdaa1dc12f0fe5316719e723adf7 Mon Sep 17 00:00:00 2001 From: Lilleman auf Larv Date: Sat, 31 Oct 2026 12:19:21 +0100 Subject: [PATCH 1/5] Show which filter segment or chip is selected and focused without :has() --- CHANGELOG.md | 6 ++++++ docs/reading-cost.md | 17 +++++++++++++++++ public/css/styles.css | 16 +++++++++------- src/ui/stylesheet-floor.test.ts | 12 ++++++++++++ todo.md | 9 --------- 5 files changed, 44 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c005e02..f8ba92b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,12 @@ at boot. Entries start at 0.3.0. ## Unreleased +### A filter shows its selected and focused choice in browsers without `:has()` + +On Firefox 115, Chromium below 105 and Safari below 15.4, a filter's segments and chips now show +their native radio or checkbox, which marks the selected choice and takes the keyboard focus ring. +Before, they showed neither. Newer browsers look as before. + ### The tests fail on a stylesheet feature the browser floor lacks `npm test` checks every stylesheet in `public/`, `examples/` and `plugins/` against Chromium 87, diff --git a/docs/reading-cost.md b/docs/reading-cost.md index 853a87c..15a4500 100644 --- a/docs/reading-cost.md +++ b/docs/reading-cost.md @@ -226,3 +226,20 @@ Read most (opens and searches): - `release-tooling/dockerhub-overview.test.ts` 1 - `release-tooling/dockerhub-overview.ts` 1 - `src/ci-gate.test.ts` 1 + +## Reading cost, chunk 984f4d4c8a76, finished 2026-10-31T11:18:20Z + +209334 new input tokens before the first commit. + +Read most (opens and searches): + +- `README.md` 6 +- `todo.md` 6 +- `src/ui/stylesheet-floor.test.ts` 4 +- `src/ui/stylesheet-floor.ts` 4 +- `CHANGELOG.md` 3 +- `AGENTS.md` 2 +- `examples/plugins/scheduling/public/scheduling.css` 2 +- `package.json` 2 +- `public/css/styles.css` 2 +- `ci.sh` 1 diff --git a/public/css/styles.css b/public/css/styles.css index 32cdef3..3ee1871 100644 --- a/public/css/styles.css +++ b/public/css/styles.css @@ -294,13 +294,6 @@ span.nav-self { cursor: default; } /* static / non-clickable */ } } -/* Without :has() every branch is open and every theme rule is inert: neither control does anything. - After the base rules, since @supports adds no specificity. */ -@supports not selector(:has(a)) { - .nav-disc { visibility: hidden; } /* keeps its width, so the labels stay aligned */ - .theme-switch { display: none; } -} - .footer-actions { display: flex; align-items: center; gap: 4px; } .profile { display: flex; align-items: center; gap: 9px; flex: 1 1 auto; @@ -485,6 +478,15 @@ span.nav-self { cursor: default; } /* static / non-clickable */ .chip:has(input:focus-visible) { outline: 2px solid var(--focus); outline-offset: 1px; } } +/* Without :has() every branch is open and every theme rule is inert: neither control does anything. + The filter inputs stay visible, since the native control is then the only sign of a choice or of + focus. After the base rules, since @supports adds no specificity. */ +@supports not selector(:has(a)) { + .nav-disc { visibility: hidden; } /* keeps its width, so the labels stay aligned */ + .theme-switch { display: none; } + .segmented input, .chip input { position: static; opacity: 1; margin: 0; } +} + /* date range */ .daterange { display: inline-flex; align-items: center; gap: 6px; diff --git a/src/ui/stylesheet-floor.test.ts b/src/ui/stylesheet-floor.test.ts index df44fc4..8358043 100644 --- a/src/ui/stylesheet-floor.test.ts +++ b/src/ui/stylesheet-floor.test.ts @@ -153,6 +153,18 @@ test("styles.css hides the theme switch and the nav toggles where :has() is miss assert.match(block, /\.nav-disc\s*\{\s*visibility:\s*hidden/); }); +// Both inputs are invisible in the base rules and only a `:has()` rule marks the choice, so without +// `:has()` the native control is the only sign of what is selected or focused. +test("styles.css shows the segmented and chip inputs where :has() is missing, after their base rules", () => { + const guard = stylesheet.indexOf("@supports not selector(:has(a)) {"); + assert.ok(guard >= 0, "no @supports not selector(:has(a)) block"); + const block = stylesheet.slice(guard, blockEnd(guard)); + for (const input of [".segmented input", ".chip input"]) { + assert.ok(stylesheet.indexOf(`${input} {`) < guard, `the base ${input} rule comes after the guard`); + } + assert.match(block, /\.segmented input,\s*\.chip input\s*\{[^}]*position:\s*static[^}]*opacity:\s*1/); +}); + // Without `:has()` nothing reads #nav-toggle, so an off-canvas sidebar there is unreachable and the // hamburger does nothing: the sidebar has to stay in the flow, with the hamburger hidden. test("styles.css moves the sidebar off-canvas at 860px only inside @supports selector(:has(a))", () => { diff --git a/todo.md b/todo.md index fec2e7c..8022d7f 100644 --- a/todo.md +++ b/todo.md @@ -12,7 +12,6 @@ | ID | Release | Blocked by | Exempt | Item | L | E | I | R | S | Moves | Score | |---|---|---|---|---|---|---|---|---|---|---|---| -| 44 | 0.4.21 | | defect | **Show which filter segment or chip is selected and focused on a browser without `:has()`.** | 1 | 2 | 2 | 3 | 2 | 4 by 4 | 23.9 | | 13 | 0.4.21 | | | **Accept a verification code pasted with surrounding whitespace.** | 4 | 2 | 3 | 7 | 7 | 2 by 8 | 1.0 | | 19 | 0.5.0 | | question | **Run the documented Docker commands on macOS and fix whatever misbehaves.** | 2 | 2 | 1 | 3 | 3 | 1 by 3 | 3.3 | | 16 | 0.5.0 | | question | **Decide whether the single Keto `Resource` namespace splits into per-domain namespaces.** | 3 | 2 | 3 | 6 | 6 | 7 by 2 | 0.5 | @@ -27,14 +26,6 @@ ## Details -### 44. Show which filter segment or chip is selected and focused on a browser without `:has()`. - -`.segmented input` and `.chip input` are `opacity: 0`, and only `:has(input:checked)` and -`:has(input:focus-visible)` rules mark the choice. On Firefox 115, Chromium 87–104 and Safari -15.0–15.3 a filter shows no selected state and no keyboard focus, which README → "Two browser -floors" forbids. Show the native inputs in `public/css/styles.css`'s `@supports not selector(:has(a))` -block. - ### 13. Accept a verification code pasted with surrounding whitespace. A pasted code fails the browser's `pattern` check. No zero-JS fix exists: `pattern` -- 2.52.0 From 2170ca728b495b1ae4d7bd992386699143985d01 Mon Sep 17 00:00:00 2001 From: Lilleman auf Larv Date: Sat, 31 Oct 2026 12:25:23 +0100 Subject: [PATCH 2/5] =?UTF-8?q?Answer=20stability=20round=201:=200=20?= =?UTF-8?q?=F0=9F=94=B4,=201=20=F0=9F=9F=A1=20(0=20misread);=20the=20order?= =?UTF-8?q?=20test=20requires=20both=20base=20rules,=20a=20chip's=20dot=20?= =?UTF-8?q?goes=20where=20the=20native=20box=20shows?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/css/styles.css | 8 +++++--- src/ui/stylesheet-floor.test.ts | 6 +++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/public/css/styles.css b/public/css/styles.css index 3ee1871..bcf2a2a 100644 --- a/public/css/styles.css +++ b/public/css/styles.css @@ -478,13 +478,15 @@ span.nav-self { cursor: default; } /* static / non-clickable */ .chip:has(input:focus-visible) { outline: 2px solid var(--focus); outline-offset: 1px; } } -/* Without :has() every branch is open and every theme rule is inert: neither control does anything. - The filter inputs stay visible, since the native control is then the only sign of a choice or of - focus. After the base rules, since @supports adds no specificity. */ +/* Without :has() every nav branch stays open and no theme rule applies, so the disclosure toggles and + the theme switch are hidden. The filter inputs are shown instead: the native control is then the + only sign of a choice or of focus, and the dot goes, since it would stay grey beside a ticked box. This block must come + after the base rules, since @supports adds no specificity. */ @supports not selector(:has(a)) { .nav-disc { visibility: hidden; } /* keeps its width, so the labels stay aligned */ .theme-switch { display: none; } .segmented input, .chip input { position: static; opacity: 1; margin: 0; } + .chip .chip-dot { display: none; } } /* date range */ diff --git a/src/ui/stylesheet-floor.test.ts b/src/ui/stylesheet-floor.test.ts index 8358043..8f6d08c 100644 --- a/src/ui/stylesheet-floor.test.ts +++ b/src/ui/stylesheet-floor.test.ts @@ -153,16 +153,16 @@ test("styles.css hides the theme switch and the nav toggles where :has() is miss assert.match(block, /\.nav-disc\s*\{\s*visibility:\s*hidden/); }); -// Both inputs are invisible in the base rules and only a `:has()` rule marks the choice, so without -// `:has()` the native control is the only sign of what is selected or focused. test("styles.css shows the segmented and chip inputs where :has() is missing, after their base rules", () => { const guard = stylesheet.indexOf("@supports not selector(:has(a)) {"); assert.ok(guard >= 0, "no @supports not selector(:has(a)) block"); const block = stylesheet.slice(guard, blockEnd(guard)); for (const input of [".segmented input", ".chip input"]) { - assert.ok(stylesheet.indexOf(`${input} {`) < guard, `the base ${input} rule comes after the guard`); + const base = stylesheet.indexOf(`${input} {`); + assert.ok(base >= 0 && base < guard, `the base ${input} rule is missing or comes after the guard`); } assert.match(block, /\.segmented input,\s*\.chip input\s*\{[^}]*position:\s*static[^}]*opacity:\s*1/); + assert.match(block, /\.chip \.chip-dot\s*\{\s*display:\s*none/); }); // Without `:has()` nothing reads #nav-toggle, so an off-canvas sidebar there is unreachable and the -- 2.52.0 From eda07269d1079bac7dc89d6a873cd6833cb5afde Mon Sep 17 00:00:00 2001 From: Lilleman auf Larv Date: Sat, 31 Oct 2026 12:25:23 +0100 Subject: [PATCH 3/5] =?UTF-8?q?Answer=20product-owner=20round=201:=200=20?= =?UTF-8?q?=F0=9F=94=B4,=200=20=F0=9F=9F=A1=20(0=20misread);=20both=20?= =?UTF-8?q?=F0=9F=9F=A2s=20fixed=20in=20the=20stability=20and=20prose=20co?= =?UTF-8?q?mmits?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit -- 2.52.0 From 03544e8d8ddd31d2817f225465eda5f277830c3a Mon Sep 17 00:00:00 2001 From: Lilleman auf Larv Date: Sat, 31 Oct 2026 12:25:23 +0100 Subject: [PATCH 4/5] =?UTF-8?q?Answer=20prose=20round=201:=200=20?= =?UTF-8?q?=F0=9F=94=B4,=203=20=F0=9F=9F=A1=20(0=20misread);=20the=20chang?= =?UTF-8?q?elog=20names=20the=20engines=20below=20:has(),=20the=20block=20?= =?UTF-8?q?comment=20names=20what=20it=20hides,=20the=20test=20comment=20g?= =?UTF-8?q?oes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f8ba92b..6034fee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ at boot. Entries start at 0.3.0. ### A filter shows its selected and focused choice in browsers without `:has()` -On Firefox 115, Chromium below 105 and Safari below 15.4, a filter's segments and chips now show +On Chromium below 105, Firefox below 121 and Safari below 15.4, a filter's segments and chips now show their native radio or checkbox, which marks the selected choice and takes the keyboard focus ring. Before, they showed neither. Newer browsers look as before. -- 2.52.0 From c78e5509b2e312e58c3aa60c7bc8d0b296f5fcf2 Mon Sep 17 00:00:00 2001 From: Lilleman auf Larv Date: Sat, 31 Oct 2026 12:25:28 +0100 Subject: [PATCH 5/5] Rewrap the fallback block comment --- public/css/styles.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/css/styles.css b/public/css/styles.css index bcf2a2a..eddc972 100644 --- a/public/css/styles.css +++ b/public/css/styles.css @@ -480,8 +480,8 @@ span.nav-self { cursor: default; } /* static / non-clickable */ /* Without :has() every nav branch stays open and no theme rule applies, so the disclosure toggles and the theme switch are hidden. The filter inputs are shown instead: the native control is then the - only sign of a choice or of focus, and the dot goes, since it would stay grey beside a ticked box. This block must come - after the base rules, since @supports adds no specificity. */ + only sign of a choice or of focus, and the dot goes, since it would stay grey beside a ticked box. + This block must come after the base rules, since @supports adds no specificity. */ @supports not selector(:has(a)) { .nav-disc { visibility: hidden; } /* keeps its width, so the labels stay aligned */ .theme-switch { display: none; } -- 2.52.0