Bug description
Three places in the CP put a focusable element inside a control that already claims an interactive role. Assistive technology can reach the inner element even when it is meant to be hidden, producing a focus stop that announces nothing.
1. Login "Remember me"
<button role="checkbox" aria-label="Remember me" tabindex="4" aria-checked="false">
<input type="checkbox" name="remember" tabindex="-1" style="position:absolute; …">
</button>
axe: nested-interactive — "Using a negative tabindex on an element inside an interactive control does not prevent assistive technologies from focusing the element." The inner input additionally trips the label rule, since it has no associated label of its own.
https://github.com/statamic/cms/blob/v6.31.0/resources/js/pages/auth/Login.vue#L115
https://github.com/statamic/cms/blob/v6.31.0/resources/js/components/ui/Checkbox/Item.vue#L113-L124
2. Blueprint builder tab trigger
<button role="tab" id="reka-tabs-v-6-trigger-main" aria-selected="true" tabindex="-1">
axe: nested-interactive — element has focusable descendants.
3. Blueprint builder tablist
<div role="tablist" aria-orientation="horizontal" tabindex="0">
… contains div[tabindex] …
</div>
axe: aria-required-children (critical) — "Element has children which are not allowed: div[tabindex]".
WCAG 2.1 SC 4.1.2 Name, Role, Value and SC 1.3.1 Info and Relationships (both Level A).
How to reproduce
composer create-project statamic/statamic
- For (1): visit
/cp/auth/login and inspect the Remember me control
- For (2) and (3): visit
/cp/fields/blueprints/collections/{handle}/{blueprint}/edit and inspect the tab strip
- Or run axe-core against either page
Suggested fix
- Render the hidden native checkbox as a sibling of the
role="checkbox" button rather than a child.
- Remove the stray
tabindex from the tablist's non-tab children so the tablist only contains role="tab" elements, and drop the focusable descendant inside the tab trigger.
While on the login form: it also uses positive tabindex values 1–6, which pull those controls ahead of the skip links in the tab order. The DOM is already in the intended order, so those can just be removed.
Environment
Environment
Laravel Version: 13.30.1
PHP Version: 8.4.23
Composer Version: 2.10.2
Environment: local
Debug Mode: ENABLED
Maintenance Mode: OFF
Timezone: UTC
Locale: en
Cache
Config: NOT CACHED
Events: NOT CACHED
Routes: NOT CACHED
Views: CACHED
Drivers
Broadcasting: log
Cache: file
Database: sqlite
Logs: stack / single
Mail: log
Queue: sync
Session: file
Storage
public/storage: NOT LINKED
Statamic
Addons: 0
License Key: Not set
Sites: 1
Stache Watcher: Enabled (auto)
Static Caching: Disabled
Version: 6.31.0 PRO
Installation
Fresh statamic/statamic site via CLI
Additional details
Found during a WCAG 2.1 AA audit of a vanilla composer create-project statamic/statamic install with no addons and no custom code. Tested with axe-core 4.x on Chromium 153 at 1440x1000, plus manual keyboard traversal. Source references point at v6.31.0.
Bug description
Three places in the CP put a focusable element inside a control that already claims an interactive role. Assistive technology can reach the inner element even when it is meant to be hidden, producing a focus stop that announces nothing.
1. Login "Remember me"
axe:
nested-interactive— "Using a negative tabindex on an element inside an interactive control does not prevent assistive technologies from focusing the element." The inner input additionally trips thelabelrule, since it has no associated label of its own.https://github.com/statamic/cms/blob/v6.31.0/resources/js/pages/auth/Login.vue#L115
https://github.com/statamic/cms/blob/v6.31.0/resources/js/components/ui/Checkbox/Item.vue#L113-L124
2. Blueprint builder tab trigger
axe:
nested-interactive— element has focusable descendants.3. Blueprint builder tablist
axe:
aria-required-children(critical) — "Element has children which are not allowed: div[tabindex]".WCAG 2.1 SC 4.1.2 Name, Role, Value and SC 1.3.1 Info and Relationships (both Level A).
How to reproduce
composer create-project statamic/statamic/cp/auth/loginand inspect the Remember me control/cp/fields/blueprints/collections/{handle}/{blueprint}/editand inspect the tab stripSuggested fix
role="checkbox"button rather than a child.tabindexfrom the tablist's non-tab children so the tablist only containsrole="tab"elements, and drop the focusable descendant inside the tab trigger.While on the login form: it also uses positive
tabindexvalues 1–6, which pull those controls ahead of the skip links in the tab order. The DOM is already in the intended order, so those can just be removed.Environment
Installation
Fresh statamic/statamic site via CLI
Additional details
Found during a WCAG 2.1 AA audit of a vanilla
composer create-project statamic/statamicinstall with no addons and no custom code. Tested with axe-core 4.x on Chromium 153 at 1440x1000, plus manual keyboard traversal. Source references point atv6.31.0.