fix(as-S5a): every control named, one h1 and a skip link, rings and hit areas
The markup and CSS half of the anti-slop interaction work. The in-place
client is untouched (that is S5b).
- Glyph-only controls carry a name: the withdraw ×s, downloads, open full
page, the viewers' ✕, the board's pin, copy and remove, the bench's
remove, the 1:1 toggle ("1:1, natural pixels"). Film-strip and tray
frames carry the file's name as sr-only text instead of reading "01".
A Desk row's wipe names its booth.
- Fields are named by aria-label, not by their placeholder.
- The inline ask's options are a radiogroup labelled by the prompt; a
single-question fieldset gets an sr-only legend; a titled ask's title
takes bk-ask-<id>-title (it duplicated the question's id).
- One h1 per page (sr-only on the Desk, review and compare), a skip link
to <main id="main">, theme-color for light and dark.
- The review tape is one picture (role=img); its segments leave the tab
order (the film strip holds the same links, named).
- Wipe now uses the Desk's delegated prompt, moved to base.html: it names
the booth and asks the kept-booth question for a kept booth.
- Embed focus rings of its own; rings drawn inside clipping containers;
the withdraw × at least 24px, 44px under a coarse pointer;
touch-action:manipulation; strips contain their overscroll; a long
slug wraps on a phone.
- A truncated why carries its full text in title; a countdown of 48h or
more reads in days.
Two r2_flow.toml rows for the confirm helper now name base.html, where
the helper moved (anchors unchanged; the gate found them drifted).
Contract: as_antislop S5a. Falsifiers: antislop.toml 86/86 proved (S1-S6, S5a);
all 12 tables 366/366 proved on this tree.
This commit is contained in:
@@ -14,6 +14,8 @@ touches:
|
||||
- "booth/templates/_marks.html, _ask_inline.html, booth.html (S1)"
|
||||
- "booth/templates/base.html, view.html; booth/static/embed.js (S2)"
|
||||
- "booth/templates/base.html, view.html, compare.html; tests/test_antislop_browser.py (S3)"
|
||||
- "booth/templates/base.html (S4, S6)"
|
||||
- "booth/app.py (`human_dur`); every page template; booth/static/embed.js (S5a)"
|
||||
- "tests/test_antislop.py; tests/mutations/antislop.toml"
|
||||
assumptions:
|
||||
- "ONE VIEWER, on this box: local time is the operator's time (US Pacific), as the existing date filters already assume."
|
||||
@@ -129,6 +131,40 @@ The run found that the Booth is sound on desktop and has a set of problems a vie
|
||||
- **The hazard stripe sits on a `::before`,** not on the button's background, for Wipe now and the armed bulk delete. The button's own background is honestly transparent (a detector read the 3px background band as the whole background, 1.0:1), and the stripe renders exactly as before.
|
||||
- *Falsifiable:* `test_the_hazard_stripe_is_a_pseudo_element`, in a real browser: no gradient on the button, a 3px striped `::before`.
|
||||
|
||||
## S5
|
||||
## S5a — names, landmarks, focus rings, hit areas
|
||||
|
||||
Interaction and screen readers: focus after a swap, a status slot the viewer cannot cover, live-region timing, names, keys, focus rings, hit areas, and the unsaved-draft guard. It rewires the in-place client, so it gets its own contract section and review before any code.
|
||||
The markup and CSS half of the interaction work. It changes no script behaviour except where Wipe now's prompt comes from. The in-place client is untouched; that half is S5b.
|
||||
|
||||
- **Every link and button has a word for a name.** A control a screen reader would announce as "×", "⬇", "⤢", "☆", "1:1" or "01" carries an `aria-label`, or the file's name as `.sr-only` text. The visible label stays inside the name (`1:1, natural pixels`).
|
||||
- Covered: the withdraw ×s, downloads, open-full-page, the viewers' close ✕, the board's pin, copy and remove, the bench's remove, the zoom toggle, and the film-strip and flagged-tray frames.
|
||||
- A Desk row's wipe names its booth (`wipe the booth alpha`), so a list of rows is not a list of identical "wipe booth"s.
|
||||
- *Falsifiable:* `test_every_control_has_a_word_for_a_name` (every page, the link board included; the name is computed from `aria-label`, else the text plus each image's `alt`), and `test_desk_row_controls_name_their_booth`.
|
||||
- **Every field has a name that is not its placeholder.** Every note field, the bench's two inputs and the inline ask's notes carry an `aria-label`.
|
||||
- *Falsifiable:* `test_fields_are_named`, on every page and on both embed placements.
|
||||
- **An ask's options are a named group, and its ids are unique.** The inline ask's options are `role="radiogroup"`, labelled by the question's prompt. The marks page's single-question fieldset gets a visually hidden `<legend>`. A titled ask's title takes `bk-ask-<id>-title`: it used to reuse `bk-ask-<id>`, which the question already holds.
|
||||
- *Falsifiable:* `test_radio_groups_are_named_and_ids_are_unique`, checked on each placement the embed makes: either `whole`, or the questions plus `submit`.
|
||||
- **Every page has one h1, a skip link and a named main.** The Desk, the review and compare get a visually hidden h1. A doc's own h1 is content and is not counted.
|
||||
- *Falsifiable:* `test_every_page_has_one_h1_and_a_skip_link`.
|
||||
- **The browser chrome matches the theme:** `theme-color` for light (`#f0f4f5`) and for dark (`#15191d`).
|
||||
- *Falsifiable:* `test_theme_color_for_both_schemes`.
|
||||
- **The review's progress tape is one picture** (`role="img"`, named "N of M seen"). Its segments leave the tab order: the film strip below it holds the same links, named.
|
||||
- *Falsifiable:* `test_the_tape_is_one_picture`.
|
||||
- **Wipe now asks by name.** The Desk's delegated prompt moves to `base.html`, and a booth page's Wipe now uses it. It names the booth, and asks the kept-booth question for a kept booth. This replaces an inline `confirm('Wipe this booth now?')`. With JS off the form still submits, as before.
|
||||
- *Falsifiable:* `test_wipe_now_asks_by_name` (markup), and `test_wipe_now_asks_by_name_in_the_browser`: the dialog's text, and dismissing it wipes nothing.
|
||||
- **Focus rings and hit areas.**
|
||||
- The embed draws its own focus rings, so a host's `outline:none` cannot remove them.
|
||||
- Rings inside `overflow:hidden` containers are drawn inside (`outline-offset:-2px`), where they cannot be clipped.
|
||||
- The withdraw × is at least 24px, and 44px under a coarse pointer.
|
||||
- Controls take `touch-action:manipulation`, and the scrolling strips contain their overscroll.
|
||||
- A long booth slug wraps on a phone.
|
||||
- *Falsifiable:* `test_embed_chrome_draws_its_own_focus_rings`, `test_focus_rings_are_drawn_inside_clipping_containers`, `test_withdraw_buttons_are_big_enough_to_hit` (measured at 1280, and at 390 with touch), and `test_touch_and_scroll_behaviour` (computed style).
|
||||
- **Small truths.**
|
||||
- A why truncated with an ellipsis carries its full text in `title`.
|
||||
- A countdown of 48h or more rolls up to days (`6d 23h`, not `167h 12m`).
|
||||
- *Falsifiable:* `test_a_truncated_why_carries_its_full_text`, `test_human_dur_rolls_up_to_days`.
|
||||
- **Existing rows this slice edits** (booth-dev's): two `r2_flow.toml` rows for the confirm helper now name `base.html`, where the helper moved. Their anchors are unchanged.
|
||||
- **Reported, not changed:** mark ids repeat across a tile and its aside (`mark-note-1`). The CLI prints `#mark-<id>` links to them, so the fix is booth-dev's call.
|
||||
|
||||
## S5b
|
||||
|
||||
The in-place client half: focus restored after a swap, a status slot the viewer cannot cover (and `inert` behind it), live-region timing and "Saving…", key handling, and the unsaved-draft guard. It rewires the in-place client, so it gets its own contract section and review before any code.
|
||||
|
||||
Reference in New Issue
Block a user