A wide table cannot be scrolled from the keyboard, the current shape is a disabled button, and the failure alert can sit over the foot of the page #275

Closed
opened 2026-09-18 20:34:54 +00:00 by tiagoagueda · 0 comments
Owner

From an accessibility read of the templates and script on 2026-09-18. Everything here is keyboard reach; axe cannot see any of it, and tests/e2e/test_keyboard_and_focus.py does not look for it.

Twenty scroll boxes, none focusable

.scroll-x is overflow-x: auto (assets/css/app.css:413-415), used in nineteen templates: every list and table page, the board, the calendar, the report, the export and import pages, the server tables, two dashboard widgets and the sidebar's narrow-screen strip. None of the boxes carries tabindex. Once a table overflows, which ten chosen columns do at 1280 pixels on any laptop, the columns off the edge can only be reached by tabbing to a link that happens to be in them; a plain-text cell with no link never comes into view. Chromium 130+ makes a scroller keyboard-focusable only when it holds no focusable child, so a table full of links is exactly the case the browser does not rescue. SC 2.1.1 (keyboard, A). The accessibility page promises "only a data table scrolls across, in its own box", which is true and is the problem.

Fix: tabindex="0", role="region" and an aria-label naming the table on the box, in one shared partial rather than nineteen copies. Check: at 1280 with ten columns chosen, Tab to the box, press ArrowRight, assert scrollLeft moved.

The table-or-board switcher marks the current choice with disabled

applications/application_list.html:90-91 (#102): the current shape's button is aria-current="true" disabled, so it leaves the tab order and is drawn at 50% (app.css:225). It reads as unavailable; screen readers hear "current" and "dimmed" together, and the group has one reachable button. Fix: keep it enabled with aria-pressed="true" and a solid style, or aria-disabled="true" without the opacity. test_keyboard_and_focus: Tab reaches both.

The failure alert has no dismiss control and stays until the next successful request

base.html:74-76 renders the role="alert" region the script fills on a failed request; .page-alert is fixed inset-x-4 bottom-4 z-50 (app.css:755-758) and is cleared only by a later successful request (static/js/app.js:130-134). No close button, no Escape. At 200-400% zoom on a short viewport it and the sticky header together take a large share of the screen, and a focused pagination link at the foot of a long table can sit under it: SC 2.4.11 (focus not obscured, AA). Fix: a close button and Escape, scroll-padding-bottom matching its height, and keep the words in the live region so the announcement is not lost.

The column-resize handle is invisible until hovered

app.css:382-387 (bg-transparent hover:bg-brand-500/40), created by app.js:1465-1468 as a proper role="separator" with a value and arrow keys (#136); the keyboard half is done well. Pointer users who do not hover, touch users and keyboard users get no cue that a splitter exists, and the hover colour is discarded in forced colours. Fix: a resting 1px ink-300 line inside the 24px target, and a ButtonText inline-end border under forced colours.

From an accessibility read of the templates and script on 2026-09-18. Everything here is keyboard reach; axe cannot see any of it, and `tests/e2e/test_keyboard_and_focus.py` does not look for it. ## Twenty scroll boxes, none focusable `.scroll-x` is `overflow-x: auto` (`assets/css/app.css:413-415`), used in nineteen templates: every list and table page, the board, the calendar, the report, the export and import pages, the server tables, two dashboard widgets and the sidebar's narrow-screen strip. None of the boxes carries `tabindex`. Once a table overflows, which ten chosen columns do at 1280 pixels on any laptop, the columns off the edge can only be reached by tabbing to a link that happens to be in them; a plain-text cell with no link never comes into view. Chromium 130+ makes a scroller keyboard-focusable only when it holds no focusable child, so a table full of links is exactly the case the browser does not rescue. SC 2.1.1 (keyboard, A). The accessibility page promises "only a data table scrolls across, in its own box", which is true and is the problem. **Fix:** `tabindex="0"`, `role="region"` and an `aria-label` naming the table on the box, in one shared partial rather than nineteen copies. **Check:** at 1280 with ten columns chosen, Tab to the box, press ArrowRight, assert `scrollLeft` moved. ## The table-or-board switcher marks the current choice with `disabled` `applications/application_list.html:90-91` (#102): the current shape's button is `aria-current="true" disabled`, so it leaves the tab order and is drawn at 50% (`app.css:225`). It reads as *unavailable*; screen readers hear "current" and "dimmed" together, and the group has one reachable button. **Fix:** keep it enabled with `aria-pressed="true"` and a solid style, or `aria-disabled="true"` without the opacity. `test_keyboard_and_focus`: Tab reaches both. ## The failure alert has no dismiss control and stays until the next successful request `base.html:74-76` renders the `role="alert"` region the script fills on a failed request; `.page-alert` is `fixed inset-x-4 bottom-4 z-50` (`app.css:755-758`) and is cleared only by a later successful request (`static/js/app.js:130-134`). No close button, no Escape. At 200-400% zoom on a short viewport it and the sticky header together take a large share of the screen, and a focused pagination link at the foot of a long table can sit under it: SC 2.4.11 (focus not obscured, AA). **Fix:** a close button and Escape, `scroll-padding-bottom` matching its height, and keep the words in the live region so the announcement is not lost. ## The column-resize handle is invisible until hovered `app.css:382-387` (`bg-transparent hover:bg-brand-500/40`), created by `app.js:1465-1468` as a proper `role="separator"` with a value and arrow keys (#136); the keyboard half is done well. Pointer users who do not hover, touch users and keyboard users get no cue that a splitter exists, and the hover colour is discarded in forced colours. **Fix:** a resting 1px `ink-300` line inside the 24px target, and a `ButtonText` inline-end border under forced colours.
tiagoagueda added this to the 0.4.0 milestone 2026-09-18 20:34:54 +00:00
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
Postulo/postulo#275
No description provided.