I have
Bug description
Quarto emits buttons that carry the btn class with no btn-* variant class. Such a button takes keyboard focus, matches :focus-visible, and shows no focus indicator at all. I found three:
| Button |
Appears when |
button.code-tools-button |
code-tools is on, at any viewport width |
button.quarto-btn-toggle |
a website sidebar collapses (below 992px) |
button.quarto-search-button |
a website has sidebar search, below 992px |
This fails WCAG 2.2 Success Criterion 2.4.7 Focus Visible (Level AA): "Any keyboard operable user interface has a mode of operation where the keyboard focus indicator is visible." axe-core has no rule for a missing focus indicator, so a clean axe report does not cover this.
Cause
Bootstrap removes the native ring and substitutes its own (_buttons.scss#L69-L79):
&:focus-visible {
outline: 0;
box-shadow: var(--#{$prefix}btn-focus-box-shadow);
}
--bs-btn-focus-box-shadow references --bs-btn-focus-shadow-rgb (#L21). Only the btn-* variant classes define that property. On a bare .btn the reference is invalid, so box-shadow computes to none, and outline: 0 has already removed the native ring.
Bootstrap documents this behavior. Its Base class section says that by default ".btn controls have a transparent border and background color, and lack any explicit focus and hover styles", and that the class "is intended to be used in conjunction with our button variants, or to serve as a basis for your own custom styles". It then gives this instruction:
If you are using the .btn class on its own, remember to at least define some explicit :focus and/or :focus-visible styles.
Quarto uses .btn on its own in the places below, and defines no such styles for them.
The emission sites I found:
Quarto's .btn:focus:not(:focus-visible) rule is not involved. It is scoped to mouse input and does not apply to keyboard focus.
The other Quarto buttons I checked are unaffected, because they either carry a variant class (btn-primary, btn-dark, btn-quarto) or are not a .btn at all (code-copy-button, callout-btn-toggle). .btn-close is a separate component with a working focus shadow. I did not audit bslib components, dashboards, or revealjs, so there may be more.
Related: #12118 fixed the same symptom on the code copy button, which is not a .btn and had a different cause. #26 is the origin of the mouse-versus-keyboard rule above.
Steps to reproduce
- Save the document below as
index.qmd.
- Run
quarto render index.qmd.
- Open
index.html, and press Tab.
The first tab stop is the "Code" button, at the top right. It takes focus with no indicator.
---
title: "Focus indicator test"
format:
html:
code-tools: true
theme: cosmo
---
Press Tab. Focus moves to the Code button, which shows no indicator.
```r
1 + 1
```
For the two navigation buttons, run quarto create project website focus-test, replace _quarto.yml with the file below, render, then narrow the window under 992px and press Tab.
project:
type: website
website:
title: "focus-test"
search:
location: sidebar
navbar:
left:
- href: index.qmd
text: Home
sidebar:
style: floating
search: true
contents:
- index.qmd
- about.qmd
format:
html:
theme: cosmo
Actual behavior
All three buttons take focus with no indicator. Computed outline is none 0px and computed box-shadow is none, in light and dark themes. --bs-btn-focus-shadow-rgb is unset on all three.
Expected behavior
Each button shows a visible keyboard focus indicator, as the surrounding controls do.
Your environment
- IDE: none, Quarto CLI at the command line
- OS: macOS 26.5.2 (build 25F84)
- Browser: Chromium 151.0.7922.34
- Also present in Quarto 99.9.9, built from
main at f9dd09cd2aee9f107a6c3f497e9210d55ceeebca
Quarto check output
Quarto 1.10.18
[✓] Checking environment information...
Quarto cache location: /Users/charlottewickham/Library/Caches/quarto
[✓] Checking versions of quarto binary dependencies...
Pandoc version 3.10.0: OK
Dart Sass version 1.101.0: OK
Deno version 2.7.14: OK
Typst version 0.15.1: OK
[✓] Checking versions of quarto dependencies......OK
[✓] Checking Quarto installation......OK
Version: 1.10.18
Path: /Applications/quarto/bin
[✓] Checking tools....................OK
TinyTeX: v2026.04
Chrome Headless Shell: 150.0.7871.115
VeraPDF: 1.28.2
[✓] Checking LaTeX....................OK
Using: TinyTex
Path: /Users/charlottewickham/Library/TinyTeX/bin/universal-darwin
Version: 2026
[✓] Checking Chrome Headless....................OK
Using: Chrome Headless Shell installed by Quarto
Path: /Users/charlottewickham/Library/Application Support/quarto/chrome-headless-shell/chrome-headless-shell-mac-arm64/chrome-headless-shell
Version: 150.0.7871.115
[✓] Checking basic markdown render....OK
[✓] Checking R installation...........OK
Version: 4.6.0
Path: /Library/Frameworks/R.framework/Versions/4.6/Resources
LibPaths:
- /Users/charlottewickham/Library/R/arm64/4.6/library
- /Library/Frameworks/R.framework/Versions/4.6/Resources/library
knitr: 1.51
rmarkdown: 2.31
[✓] Checking Knitr engine render......OK
[✓] Checking Python 3 installation....OK
Version: 3.12.2
Path: /Users/charlottewickham/.pyenv/versions/3.12.2/bin/python3
Jupyter: 5.9.1
Kernels: python3
[✓] Checking Jupyter engine render....OK
[✓] Checking Julia installation...
AI helped with this investigation. The work was grounded in a local clone of quarto-cli, as described in CONTRIBUTING.md.
I have
Bug description
Quarto emits buttons that carry the
btnclass with nobtn-*variant class. Such a button takes keyboard focus, matches:focus-visible, and shows no focus indicator at all. I found three:button.code-tools-buttoncode-toolsis on, at any viewport widthbutton.quarto-btn-togglebutton.quarto-search-buttonThis fails WCAG 2.2 Success Criterion 2.4.7 Focus Visible (Level AA): "Any keyboard operable user interface has a mode of operation where the keyboard focus indicator is visible." axe-core has no rule for a missing focus indicator, so a clean axe report does not cover this.
Cause
Bootstrap removes the native ring and substitutes its own (
_buttons.scss#L69-L79):--bs-btn-focus-box-shadowreferences--bs-btn-focus-shadow-rgb(#L21). Only thebtn-*variant classes define that property. On a bare.btnthe reference is invalid, sobox-shadowcomputes tonone, andoutline: 0has already removed the native ring.Bootstrap documents this behavior. Its Base class section says that by default "
.btncontrols have a transparent border and background color, and lack any explicit focus and hover styles", and that the class "is intended to be used in conjunction with our button variants, or to serve as a basis for your own custom styles". It then gives this instruction:Quarto uses
.btnon its own in the places below, and defines no such styles for them.The emission sites I found:
codetools.ts#L163-L166—btn code-tools-button, plusdropdown-togglewhen the menu form is usednav-before-body.ejs#L66—quarto-btn-toggle btnnav-before-body.ejs#L86—btn quarto-search-buttonQuarto's
.btn:focus:not(:focus-visible)rule is not involved. It is scoped to mouse input and does not apply to keyboard focus.The other Quarto buttons I checked are unaffected, because they either carry a variant class (
btn-primary,btn-dark,btn-quarto) or are not a.btnat all (code-copy-button,callout-btn-toggle)..btn-closeis a separate component with a working focus shadow. I did not audit bslib components, dashboards, or revealjs, so there may be more.Related: #12118 fixed the same symptom on the code copy button, which is not a
.btnand had a different cause. #26 is the origin of the mouse-versus-keyboard rule above.Steps to reproduce
index.qmd.quarto render index.qmd.index.html, and press Tab.The first tab stop is the "Code" button, at the top right. It takes focus with no indicator.
For the two navigation buttons, run
quarto create project website focus-test, replace_quarto.ymlwith the file below, render, then narrow the window under 992px and press Tab.Actual behavior
All three buttons take focus with no indicator. Computed
outlineisnone 0pxand computedbox-shadowisnone, in light and dark themes.--bs-btn-focus-shadow-rgbis unset on all three.Expected behavior
Each button shows a visible keyboard focus indicator, as the surrounding controls do.
Your environment
mainatf9dd09cd2aee9f107a6c3f497e9210d55ceeebcaQuarto check output
AI helped with this investigation. The work was grounded in a local clone of
quarto-cli, as described in CONTRIBUTING.md.