Make the overlays usable from a keyboard
Measured every overlay in the tool before touching anything. For a dealer running this on a laptop, all six were broken in the same three ways. - focus never moved into the dialog: 0 of 5 measured - Tab walked straight out of the dialog into the page behind the scrim: 5 of 5 - Escape did nothing: only the rulings sheet handled it, 1 of 5 - the pot wizard — the dialog that pays people — had no role and no accessible name at all So opening the pot wizard left focus on the button behind it, tabbing wandered through a page nobody can see or click, and there was no way back out except the mouse. One implementation, not six The overlays are opened and closed by five different modules, which is why they each missed the same things. Rather than patch this into all of them, modal.js watches the `hidden` attribute they already toggle: focus moves to the first real control on open, Tab and Shift-Tab wrap inside the dialog, Escape closes it, and focus returns to whatever opened it. `aria-modal` is set while open and removed on close. An overlay added later gets all of it without anyone remembering to wire it up, which is the failure being fixed. Escape presses the dialog's own cancel button rather than just hiding the overlay, so the confirm dialog still resolves its promise — verified: Escape resolves it to false rather than leaving an await hanging forever. Shortcuts.overlayOpen() had the same shape of bug, listing the six overlay ids by hand. It asks the document now, so a sheet added later can't quietly start letting "f" fold the table behind it. Focus you can see Three focus rules existed for the whole stylesheet, so tabbing relied on the browser default ring, which on these dark custom-painted controls is close to invisible. `:focus-visible` keeps it to keyboard use so a dealer tapping Fold isn't left with a ring on it. That surfaced something small: `.holdemBtn` used `transition: all`, so the new outline faded in over 150ms and the ring lagged a step behind the Tab key. The transition names its properties now. Measured after: 16 of 16 tab stops show a ring immediately, and the press state from before still animates. Also - a mistyped negative stack read back as negative, and flowed into putIn() where Math.min(amount, -50) moves a negative amount and adds chips to the table. Clamped at zero. - accessible names for the chip colour, chip value and notes fields, which had none 11 new tests (99 total), covering negative stacks, negative and unreadable blinds, blinds nobody can cover, and a save that comes back corrupt, truncated, or from before a field existed. Playwright: all five overlays re-measured — focus moves in 5/5, Tab and Shift-Tab trapped 5/5, Escape closes 5/5, focus restored 5/5, role and aria-modal present 5/5. Shortcuts still fire when no dialog is open and are still blocked when one is. Touch guarantees from the earlier round unchanged: 0 of 49 controls on touch-action auto, 0 tap-highlighted. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_015bnp7xKUPFFSpwkYNFaPgb
This commit is contained in:
@@ -219,7 +219,11 @@ button,
|
||||
padding: 0.6rem 1rem;
|
||||
font-size: 0.85rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.15s ease;
|
||||
/* Named rather than `all`, so the focus ring isn't animated. With `all` the
|
||||
outline faded in over 150ms, which on keyboard navigation reads as the ring
|
||||
lagging a step behind the Tab key. Nothing else here changes. */
|
||||
transition: background 0.15s ease, border-color 0.15s ease, transform 0.15s ease,
|
||||
box-shadow 0.15s ease, color 0.15s ease, opacity 0.15s ease, filter 0.15s ease;
|
||||
text-decoration: none;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
@@ -2690,3 +2694,41 @@ body.hasActionBar #toastHost {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------- Keyboard focus ----------
|
||||
Three focus rules existed for the whole tool, so tabbing through it relied on
|
||||
whatever ring the browser draws by default — which on these dark, custom-painted
|
||||
controls is close to invisible. `:focus-visible` rather than `:focus` keeps it to
|
||||
keyboard use: a dealer tapping Fold shouldn't be left with a ring on it. */
|
||||
.holdemBtn:focus-visible,
|
||||
.linkBtn:focus-visible,
|
||||
.seatMenuBtn:focus-visible,
|
||||
.seatSheetClose:focus-visible,
|
||||
.pickerCard:focus-visible,
|
||||
.actionBtn:focus-visible,
|
||||
.raiseQuickBtn:focus-visible,
|
||||
.actionUndo:focus-visible,
|
||||
.toastAction:focus-visible,
|
||||
.toastClose:focus-visible,
|
||||
.potWizardChoice:focus-visible,
|
||||
.cardSlot:focus-visible,
|
||||
.subPanel summary:focus-visible,
|
||||
.seat:focus-visible,
|
||||
button:focus-visible,
|
||||
[role="button"]:focus-visible,
|
||||
input:focus-visible,
|
||||
select:focus-visible,
|
||||
textarea:focus-visible {
|
||||
outline: 2px solid #7fc4ff;
|
||||
outline-offset: 2px;
|
||||
/* The ring has to sit above the neighbouring seat that overlaps this one. */
|
||||
position: relative;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
/* A sheet with nothing focusable in it takes focus itself so its title is read out.
|
||||
That shouldn't paint a ring around the whole panel. */
|
||||
[role="dialog"]:focus,
|
||||
[role="alertdialog"]:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user