Restore keyboard focus indicator on bare .btn buttons - #14785
Conversation
The code tools button, website sidebar toggle, and website sidebar search button carry Bootstrap's btn class with no btn-* variant class. Bootstrap's .btn:focus-visible sets outline: 0 and substitutes a box-shadow that only the variant classes define, so these buttons took keyboard focus with no visible indicator, failing WCAG 2.4.7. Restore the browser's native focus ring on them with outline: revert. An outline also survives forced-colors mode, where box-shadow computes to none, and matches the code copy button's indicator (#12118).
✅ Snyk checks have passed. No issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
There was a problem hiding this comment.
Thanks for this, and for the very thorough issue that goes with it. I checked the claims in the description against the compiled CSS and a real browser rather than taking them on trust, and the diagnosis holds up in full: with the rule commented out the three buttons compute outline-style: none, box-shadow: none, and an empty --bs-btn-focus-shadow-rgb. I also ran the new spec locally in all three engines: nine pass with the fix and nine fail without it, so the assertion is doing real work.
One substantive request, on the .quarto-secondary-nav prefix, plus two small things. Details inline.
The rest checks out.
Drop the `.quarto-secondary-nav` prefix from the two nav button selectors. Both buttons are emitted in exactly one place, nav-before-body.ejs, and only ever inside `.quarto-secondary-nav`, so the prefix bought no scoping. It did make the two nav buttons harder to override than the code tools button: a site author's rule at single-class specificity won on the code tools button but lost on the nav buttons. All three now behave the same. Add a .gitignore to the bare-btn-focus fixture so a project render does not leave `_site/` and `.quarto/` untracked. The driver calls cleanoutput() without projectOutDir, so it cannot clean a project render.
|
Thanks @cderv — both changes are in, as 92a59ef. Prefix dropped. I rendered the same site twice, once per selector variant, with a user SCSS rule setting
Identical in Chromium, Firefox, and WebKit. Your finding reproduces exactly. I also checked the no-user-rule case against the fixtures: all nine button/engine combinations compute The specificity caveat is out of the PR description. Fixture |
There was a problem hiding this comment.
LGTM, thanks;
🤖 went through the diff again after and flagged something worth a mention. #14826, already on main, added its own focus-visible rule for the new sidebar-item-toggle buttons:
quarto-cli/src/resources/projects/website/navigation/quarto-nav.scss
Lines 414 to 418 in a8b5b2b
That one uses outline: 2px solid currentColor with outline-offset: 2px, while this PR uses outline: revert. Same problem, same sidebar region, two different conventions now living next to each other.
Not blocking, both fix the underlying issue and neither is wrong. Do you think we need a follow-up issue to converge on one approach ?
I am merging this anyway, but I figured it was good to mention in case we want common ways to fix axe problems.
Description
Closes #14774.
Three Quarto buttons carry Bootstrap's
btnclass with nobtn-*variant class: the code tools button, a website's sidebar toggle, and a website's sidebar search button. Bootstrap's.btn:focus-visiblerule setsoutline: 0plus a box-shadow that only the variant classes define. On these buttons the box-shadow computes tonone, so keyboard focus shows no indicator. This fails WCAG 2.2 SC 2.4.7 (Focus Visible).This PR adds one rule that restores the browser's native focus ring on the three buttons with
outline: revert.Reasons to prefer the native ring over Bootstrap's box-shadow halo:
none.outline(Withforced-colors: active, focus-rings may be invisible twbs/bootstrap#42062), so the fix does not depend on a mechanism that upstream is retiring.revertsupport (Safari before 15.4) drop the declaration, and behavior is unchanged from today.Site authors can still restyle the focus state, because user SCSS compiles after Quarto's rules. Each of the three selectors is a single class, the same specificity as Bootstrap's own rule. A plain override therefore wins on all three buttons. For example,
.quarto-search-button:focus-visible { outline: 3px dashed red; }replaces the ring on the sidebar search button. I confirmed this in a rendered site in Chromium, Firefox, and WebKit.No documentation change is needed: the fix adds no option and changes no documented behavior.
Tests
The new spec
html-focus-indicator-bare-btn.spec.tscovers all three buttons in Chromium, Firefox, and WebKit. It moves focus with real Tab presses (Option+Tab on WebKit, which ships Safari's default keyboard-navigation settings), then asserts that the computedoutline-styleis notnone. Real key presses are necessary because programmatic focus does not match:focus-visible. All nine tests fail on a build without the fix and pass with it. The fixtures are a standalonecode-toolsdocument and a minimal website with a collapsed sidebar.Checklist
I have (if applicable):
AI-assisted PR