Keyboard focus must be visible
Keyboard users, including many people with motor impairments, move through the shop with the Tab key. When the theme hides the focus outline they cannot see where they are, so they cannot tell which link, size or button they are about to activate.
- Rule id
- vs-focus-visible
- Source
- Validiscan check
- Impact
- Serious
- Fix tier
- AUTO
- on Shopify, Shopware, WooCommerce and Magento
- Status
- runs
What the check does
Tabs through every focusable element and compares the computed outline, box-shadow, border and background before and after focus; an element that looks the same when focused fails (2.4.7).
The check collects links with an href, form controls, summary, audio and video with controls, elements with a tabindex and contenteditable content. It skips iframe, object, embed, negative tabindex values and disabled, hidden or zero-size elements, and keeps the first 150 in document order.
It first presses Tab once, so that Chromium treats the following focus moves as keyboard focus. It then focuses each element in turn and records the outline, box-shadow, borders, background, text color, text-decoration-line, filter, transform and opacity, unfocused and focused, with CSS transitions off. The record also covers ::before, ::after, the parent for :focus-within styles, and the first five descendants.
Elements that do not take focus or do not match :focus-visible are left out. Only identical records become a finding, so a focus style that changes nothing but an unrecorded property, such as text-shadow, is reported. The check does not measure the size or contrast of a change; it does not judge whether a focus style is easy to see.
How to fix it
Add a theme-wide :focus-visible style, such as a 2 to 3 pixel outline with an offset, in a brand colour with at least 3:1 contrast against the surrounding background, and remove outline: none rules that are not replaced by an equally visible style.
WCAG success criteria
- 2.4.7 Focus Visible, level AA
EN 301 549 clauses
| WCAG criterion | Version 3.2.1 | Version 4.1.1 |
|---|---|---|
| 2.4.7 | 9.2.4.7 | 9.2.4.7 |
Where a recipe fixes it
Shopify: recipe focus-visible-styles
Show the before and after (1)
assets/base.css.liquid Changes to assets/base.css.liquid: 7 added, 0 removed
| Line before | Line after | Change | Code |
|---|---|---|---|
| 1866 | 1866 | } | |
| 1867 | 1867 | ||
| 1868 | 1868 | /* ----------------------------------------------------------------- motion */ | |
| 1869 | Added | ||
| 1870 | Added | /* validiscan:focus-visible-styles */ | |
| 1871 | Added | :focus-visible { | |
| 1872 | Added | outline: 2px solid #1f4d3a; | |
| 1873 | Added | outline-offset: 2px; | |
| 1874 | Added | } | |
| 1875 | Added | /* /validiscan:focus-visible-styles */ |
Shopware: recipe focus-visible-styles
Show the before and after (1)
Resources/app/storefront/src/scss/_variant-broken.scss Changes to Resources/app/storefront/src/scss/_variant-broken.scss: 5 added, 5 removed
| Line before | Line after | Change | Code |
|---|---|---|---|
| 21 | 21 | } | |
| 22 | 22 | ||
| 23 | 23 | /* "Remove the ugly blue ring" (vs-focus-visible). */ | |
| 24 | Removed | .header-actions-btn:focus, | |
| 25 | Removed | .header-search-btn:focus, | |
| 26 | Removed | .main-navigation-link:focus, | |
| 27 | Removed | .product-name:focus, | |
| 28 | Removed | .footer-link:focus { | |
| 24 | Added | .header-actions-btn:focus:not(:focus-visible), | |
| 25 | Added | .header-search-btn:focus:not(:focus-visible), | |
| 26 | Added | .main-navigation-link:focus:not(:focus-visible), | |
| 27 | Added | .product-name:focus:not(:focus-visible), | |
| 28 | Added | .footer-link:focus:not(:focus-visible) { | |
| 29 | 29 | outline: 0; | |
| 30 | 30 | box-shadow: none; | |
| 31 | 31 | } |
Resources/app/storefront/src/scss/base.scss Changes to Resources/app/storefront/src/scss/base.scss: 8 added, 0 removed
| Line before | Line after | Change | Code |
|---|---|---|---|
| 1708 | 1708 | margin: 8px 0 0; | |
| 1709 | 1709 | font-size: 0.875rem; | |
| 1710 | 1710 | } | |
| 1711 | Added | ||
| 1712 | Added | /* validiscan:focus-visible-styles */ | |
| 1713 | Added | /* Visible keyboard focus (WCAG 2.4.7): brand colour sw-color-brand-primary, 8.9:1 on #ffffff. */ | |
| 1714 | Added | :focus-visible { | |
| 1715 | Added | outline: 2px solid #6b3f1d; | |
| 1716 | Added | outline-offset: 2px; | |
| 1717 | Added | } | |
| 1718 | Added | /* /validiscan:focus-visible-styles */ |
WooCommerce: recipe focus-visible-styles
Show the before and after (1)
assets/css/main.css Changes to assets/css/main.css: 9 added, 3 removed
| Line before | Line after | Change | Code |
|---|---|---|---|
| 137 | 137 | box-shadow: 0 0 0 2px var(--kb-ink); | |
| 138 | 138 | } | |
| 139 | 139 | ||
| 140 | Removed | /* VALIDISCAN-SEED vs-focus-visible: "remove the ugly focus ring" - nothing replaces it. */ | |
| 141 | Removed | :focus { | |
| 142 | Removed | outline: none !important; | |
| 140 | Added | /* validiscan:focus-visible-styles */ | |
| 141 | Added | :focus-visible { | |
| 142 | Added | outline: 2px solid var(--kb-focus); | |
| 143 | Added | outline-offset: 2px; | |
| 143 | 144 | } | |
| 144 | 145 | ||
| 146 | Added | .kb-topbar :focus-visible { | |
| 147 | Added | outline-color: #fff; | |
| 148 | Added | } | |
| 149 | Added | /* /validiscan:focus-visible-styles */ | |
| 150 | Added | ||
| 145 | 151 | /* Buttons ------------------------------------------------------------------ */ | |
| 146 | 152 | ||
| 147 | 153 | .button, |
Magento: recipe focus-visible-styles
Show the before and after (1)
app/design/frontend/Validiscan/fixture/web/css/source/_theme.less Changes to app/design/frontend/Validiscan/fixture/web/css/source/_theme.less: 10 added, 12 removed
| Line before | Line after | Change | Code |
|---|---|---|---|
| 1079 | 1079 | color: #fff; | |
| 1080 | 1080 | } | |
| 1081 | 1081 | ||
| 1082 | Removed | // VALIDISCAN-SEEDED vs-focus-visible: the theme removes the focus ring from links and actions. | |
| 1083 | Removed | .navigation a:focus, | |
| 1084 | Removed | .header.links a:focus, | |
| 1085 | Removed | .product-item-link:focus, | |
| 1086 | Removed | .footer a:focus, | |
| 1087 | Removed | .action:focus, | |
| 1088 | Removed | .logo:focus, | |
| 1089 | Removed | .block-promo:focus, | |
| 1090 | Removed | .journal-link:focus { | |
| 1091 | Removed | outline: none; | |
| 1092 | Removed | } | |
| 1093 | Removed | ||
| 1094 | 1082 | // VALIDISCAN-SEEDED vs-headings-landmarks: the home page title is display:none, so the page has no h1. | |
| 1095 | 1083 | .cms-index-index .page-title-wrapper { | |
| 1096 | 1084 | display: none; | |
| 1264 | 1252 | height: 18px; | |
| 1265 | 1253 | stroke-width: 3; | |
| 1266 | 1254 | } | |
| 1255 | Added | ||
| 1256 | Added | /* validiscan:focus-visible-styles */ | |
| 1257 | Added | :focus-visible { | |
| 1258 | Added | outline: 2px solid #1979c3; | |
| 1259 | Added | outline-offset: 2px; | |
| 1260 | Added | } | |
| 1261 | Added | .panel.wrapper :focus-visible { | |
| 1262 | Added | outline-color: #a1d0f3; | |
| 1263 | Added | } | |
| 1264 | Added | /* /validiscan:focus-visible-styles */ |
app/design/frontend/Validiscan/fixture/web/css/styles-m.css Changes to app/design/frontend/Validiscan/fixture/web/css/styles-m.css: 10 added, 11 removed
| Line before | Line after | Change | Code |
|---|---|---|---|
| 1058 | 1058 | color: #fff; | |
| 1059 | 1059 | } | |
| 1060 | 1060 | ||
| 1061 | Removed | .navigation a:focus, | |
| 1062 | Removed | .header.links a:focus, | |
| 1063 | Removed | .product-item-link:focus, | |
| 1064 | Removed | .footer a:focus, | |
| 1065 | Removed | .action:focus, | |
| 1066 | Removed | .logo:focus, | |
| 1067 | Removed | .block-promo:focus, | |
| 1068 | Removed | .journal-link:focus { | |
| 1069 | Removed | outline: none; | |
| 1070 | Removed | } | |
| 1071 | Removed | ||
| 1072 | 1061 | .cms-index-index .page-title-wrapper { | |
| 1073 | 1062 | display: none; | |
| 1074 | 1063 | } | |
| 1232 | 1221 | height: 18px; | |
| 1233 | 1222 | stroke-width: 3; | |
| 1234 | 1223 | } | |
| 1224 | Added | ||
| 1225 | Added | /* validiscan:focus-visible-styles */ | |
| 1226 | Added | :focus-visible { | |
| 1227 | Added | outline: 2px solid #1979c3; | |
| 1228 | Added | outline-offset: 2px; | |
| 1229 | Added | } | |
| 1230 | Added | .panel.wrapper :focus-visible { | |
| 1231 | Added | outline-color: #a1d0f3; | |
| 1232 | Added | } | |
| 1233 | Added | /* /validiscan:focus-visible-styles */ |
Start with a free check of your shop.
Send your shop’s address. Validiscan scans it and replies with every failure the scan can prove, with the page and the code.
Request a free checkOr write to info@validiscan.com.
What the free check covers, and what it leaves out- You send
- your shop’s address
- Pages scanned
- up to 15
- You get
- every failure the scan can prove, with the page and the code
- On your shop
- public pages only; no login, nothing bought, no forms sent
- What it is not
- a full audit