Settings General: #522 feature toggles render scattered — proper flex rows + toggle-switch controls #533

Closed
opened 2026-09-14 19:53:45 +00:00 by crueber · 3 comments
Owner

Settings General: the six #522 feature toggles render scattered — replace stretched checkboxes with proper flex rows + toggle switches

What's requested

The six feature toggles that landed with #522 (Settings → General → "Features") render as a broken, scattered column instead of six clean rows. Re-lay them out as proper flex rows with toggle-switch controls ("radio sliders"), and verify the rendered result before closing.

Root cause (static read of current tree)

web/src/pages/Settings.jsx (Features section, ~line 320-342):

  • Each toggle renders <input class="input" type="checkbox" …>. The input utility (web/src/ui.css ~line 130) is the text-input style: w-full rounded-md border … px-2.5 py-1.5. Applied to a checkbox it becomes a full-width padded bordered box — the checkbox stretches across the row and shoves the label text, which is what reads as "scattered".
  • The row label uses flex items-start gap-2 with a block hint span under the title, so the stretched control and the two-line text stack fight each other instead of aligning.
  • There is no switch/toggle component anywhere in web/src/ (no role="switch", no slider pattern) — the controls need to be introduced once and reused by these six rows.

Fix prescription

  1. Add a shared toggle-switch component (e.g. web/src/components/ToggleSwitch.jsx or alongside the existing shared UI pieces): a styled checkbox with the Tailwind peer pattern (peer + peer-checked: slide knob, track via w-9 h-5-scale track, rounded-full, emerald when on, zinc when off, visible focus-visible ring), rendered as role="switch" with aria-checked, keyboard-operable (a real <input type="checkbox"> under the styling is fine and keeps it accessible). Theme-derived colors in both light and dark — no hardcoded palette.
  2. Re-lay each row as a proper flex row: flex items-center justify-between gap-4 — label (title + muted text-xs hint beneath, not beside) on the left, switch anchored right. The group stays a vertical list (grid gap-2 is fine); rows must align control-to-control so the six switches form a clean right column. Keep the existing flagsDirty() unsaved-changes note and the "Save features" button path untouched.
  3. Drop the input class from these checkboxes entirely — it is a text-field utility and is the direct cause of the stretching.

Acceptance criteria

  • Six rows, each a single aligned flex row: label left, switch right, no stretched or padded checkbox boxes.
  • Switch control is a toggle-slider (track + knob that slides on check), emerald when enabled, zinc when disabled, in both themes.
  • Keyboard and screen-reader operable (role="switch", aria-checked, focus ring); clicking the row label toggles.
  • The input text-field class no longer appears on any checkbox.
  • Save/unsaved-changes behavior of #522 is unchanged (CAS revision, 403 surfacing, settings invalidations).
  • Verification mandate: the implementer must render the page (built SPA, real or headless browser) and confirm the six rows render aligned — screenshot or DOM measurement (row rect alignment / switch positions) — before closing. Do not close on code reading alone.

Static diagnosis — no local repro performed, per standing rule.

# Settings General: the six #522 feature toggles render scattered — replace stretched checkboxes with proper flex rows + toggle switches ## What's requested The six feature toggles that landed with #522 (Settings → General → "Features") render as a broken, scattered column instead of six clean rows. Re-lay them out as proper flex rows with toggle-switch controls ("radio sliders"), and verify the rendered result before closing. ## Root cause (static read of current tree) `web/src/pages/Settings.jsx` (Features section, ~line 320-342): - Each toggle renders `<input class="input" type="checkbox" …>`. The `input` utility (`web/src/ui.css` ~line 130) is the **text-input** style: `w-full rounded-md border … px-2.5 py-1.5`. Applied to a checkbox it becomes a full-width padded bordered box — the checkbox stretches across the row and shoves the label text, which is what reads as "scattered". - The row label uses `flex items-start gap-2` with a `block` hint span under the title, so the stretched control and the two-line text stack fight each other instead of aligning. - There is no switch/toggle component anywhere in `web/src/` (no `role="switch"`, no slider pattern) — the controls need to be introduced once and reused by these six rows. ## Fix prescription 1. **Add a shared toggle-switch component** (e.g. `web/src/components/ToggleSwitch.jsx` or alongside the existing shared UI pieces): a styled checkbox with the Tailwind peer pattern (`peer` + `peer-checked:` slide knob, track via `w-9 h-5`-scale track, `rounded-full`, emerald when on, zinc when off, visible `focus-visible` ring), rendered as `role="switch"` with `aria-checked`, keyboard-operable (a real `<input type="checkbox">` under the styling is fine and keeps it accessible). Theme-derived colors in both light and dark — no hardcoded palette. 2. **Re-lay each row as a proper flex row**: `flex items-center justify-between gap-4` — label (title + muted `text-xs` hint beneath, not beside) on the left, switch anchored right. The group stays a vertical list (`grid gap-2` is fine); rows must align control-to-control so the six switches form a clean right column. Keep the existing `flagsDirty()` unsaved-changes note and the "Save features" button path untouched. 3. **Drop the `input` class from these checkboxes entirely** — it is a text-field utility and is the direct cause of the stretching. ## Acceptance criteria - [ ] Six rows, each a single aligned flex row: label left, switch right, no stretched or padded checkbox boxes. - [ ] Switch control is a toggle-slider (track + knob that slides on check), emerald when enabled, zinc when disabled, in both themes. - [ ] Keyboard and screen-reader operable (`role="switch"`, `aria-checked`, focus ring); clicking the row label toggles. - [ ] The `input` text-field class no longer appears on any checkbox. - [ ] Save/unsaved-changes behavior of #522 is unchanged (CAS revision, 403 surfacing, settings invalidations). - [ ] **Verification mandate: the implementer must render the page (built SPA, real or headless browser) and confirm the six rows render aligned — screenshot or DOM measurement (row rect alignment / switch positions) — before closing. Do not close on code reading alone.** *Static diagnosis — no local repro performed, per standing rule.*
crueber added this to the v1 milestone 2026-09-14 19:53:51 +00:00
Author
Owner

Fix is up in #535 (branch fix/issue-533) — six flex rows + shared ToggleSwitch, AGENTS.md Tailwind-compose rule, 12_web_ui decision. Tests 1218/1217 (1 pre-existing live-server smoke fail), vite+esbuild green. Browser proof still open: needs the independent-rig screenshot verify pre-merge.

Fix is up in #535 (branch fix/issue-533) — six flex rows + shared ToggleSwitch, AGENTS.md Tailwind-compose rule, 12_web_ui decision. Tests 1218/1217 (1 pre-existing live-server smoke fail), vite+esbuild green. Browser proof still open: needs the independent-rig screenshot verify pre-merge.
Author
Owner

REVIEW PR #535 (fix/issue-533, commit 413bdf7) — verified in scratch worktree /tmp/pr535.

NOTE per brief: the orchestrator already rendered-verified this branch (headless-Chromium screenshots 1280px + 390px: six aligned rows, switches in clean right column, no overflow). I did NOT re-drive a browser; criterion 6 is covered by that proof, and I fixed the one place that still claimed otherwise (below).

Acceptance criteria vs code+tests:
[1] Aligned flex rows — PASS. Settings.jsx:338 rows are each one (label span min-w-0 left, ToggleSwitch shrink-0 right) inside the untouched grid gap-2 list; switches form a right column.
[2] Toggle-slider control — PASS. ToggleSwitch.jsx:17-38: real checkbox (peer sr-only) + w-9 h-5 rounded-full track (bg-zinc-300/dark:bg-zinc-700 off, peer-checked:bg-emerald-500/dark:peer-checked:bg-emerald-600 on) + h-4 w-4 knob with peer-checked:translate-x-4; peer order (input first, spans after) is correct so variants fire; both themes covered.
[3] A11y/keyboard — PASS. role="switch" + aria-checked (ToggleSwitch.jsx:21-22), native checkbox stays keyboard-operable, peer-focus-visible ring on track, row IS the label so label-click toggles natively, root is a span so no nested-label markup.
[4] input class off checkboxes — PASS, repo-wide grep: zero type="checkbox" elements carry class "input" (other checkboxes — Wal.jsx:235, Settings.jsx:647, ReleaseNew.jsx:232/246, Import.jsx:334-375 — use bare/mt-0.5 only).
[5] Save path unchanged — PASS. Settings.jsx diff is import + row block only (14+/8-); flagsDirty()/withFeatures/PUT/403-in-note/reseed untouched, pinned by settings-features-533.test.js.
[6] Rendered verification — COVERED by orchestrator screenshots (see note above), not by code reading.

Laws/docs: AGENTS.md Tailwind-compose rule — normative wording, correct placement (Working rules list), cites #533 + #405 precedent. Law 12 satisfied: 12_web_ui.md decision appended in same change. No backend/SDK/API change, no new deps (6-file diff: AGENTS.md, 12_web_ui.md, ToggleSwitch.jsx, Settings.jsx, 2 test files), no ui.css change.

FIX APPLIED (pushed b348515 to origin/fix/issue-533): 12_web_ui.md #533 entry still ended 'Browser proof open (shared-daemon loopback guard…)' — stale given the orchestrator's rendered verification; replaced with the rendered-proof record. Re-tested after fix: 533+522 pin files 9/9 pass.

TESTS: full node suite minus smoke: 1215/1215 pass. smoke.test.js: same pre-existing live-server failure as #520/#521/#522 entries (403 vs 200 on /setup against the standing instance — needs a live Go server, zero PR-caused). vite build green (2.44s).

MERGE RECOMMENDATION: ready to merge.

REVIEW PR #535 (fix/issue-533, commit 413bdf7) — verified in scratch worktree /tmp/pr535. NOTE per brief: the orchestrator already rendered-verified this branch (headless-Chromium screenshots 1280px + 390px: six aligned rows, switches in clean right column, no overflow). I did NOT re-drive a browser; criterion 6 is covered by that proof, and I fixed the one place that still claimed otherwise (below). Acceptance criteria vs code+tests: [1] Aligned flex rows — PASS. Settings.jsx:338 rows are each one <label class="flex cursor-pointer items-center justify-between gap-4"> (label span min-w-0 left, ToggleSwitch shrink-0 right) inside the untouched grid gap-2 list; switches form a right column. [2] Toggle-slider control — PASS. ToggleSwitch.jsx:17-38: real checkbox (peer sr-only) + w-9 h-5 rounded-full track (bg-zinc-300/dark:bg-zinc-700 off, peer-checked:bg-emerald-500/dark:peer-checked:bg-emerald-600 on) + h-4 w-4 knob with peer-checked:translate-x-4; peer order (input first, spans after) is correct so variants fire; both themes covered. [3] A11y/keyboard — PASS. role="switch" + aria-checked (ToggleSwitch.jsx:21-22), native checkbox stays keyboard-operable, peer-focus-visible ring on track, row IS the label so label-click toggles natively, root is a span so no nested-label markup. [4] input class off checkboxes — PASS, repo-wide grep: zero type="checkbox" elements carry class "input" (other checkboxes — Wal.jsx:235, Settings.jsx:647, ReleaseNew.jsx:232/246, Import.jsx:334-375 — use bare/mt-0.5 only). [5] Save path unchanged — PASS. Settings.jsx diff is import + row block only (14+/8-); flagsDirty()/withFeatures/PUT/403-in-note/reseed untouched, pinned by settings-features-533.test.js. [6] Rendered verification — COVERED by orchestrator screenshots (see note above), not by code reading. Laws/docs: AGENTS.md Tailwind-compose rule — normative wording, correct placement (Working rules list), cites #533 + #405 precedent. Law 12 satisfied: 12_web_ui.md decision appended in same change. No backend/SDK/API change, no new deps (6-file diff: AGENTS.md, 12_web_ui.md, ToggleSwitch.jsx, Settings.jsx, 2 test files), no ui.css change. FIX APPLIED (pushed b348515 to origin/fix/issue-533): 12_web_ui.md #533 entry still ended 'Browser proof open (shared-daemon loopback guard…)' — stale given the orchestrator's rendered verification; replaced with the rendered-proof record. Re-tested after fix: 533+522 pin files 9/9 pass. TESTS: full node suite minus smoke: 1215/1215 pass. smoke.test.js: same pre-existing live-server failure as #520/#521/#522 entries (403 vs 200 on /setup against the standing instance — needs a live Go server, zero PR-caused). vite build green (2.44s). MERGE RECOMMENDATION: ready to merge.
Author
Owner

Fixed by PR #535 (review clean; rendered-verified with screenshots at desktop + 390px; AGENTS.md Tailwind rule landed), merged. Closing.

Fixed by PR #535 (review clean; rendered-verified with screenshots at desktop + 390px; AGENTS.md Tailwind rule landed), merged. Closing.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
crueber/walhub#533
No description provided.