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

EN 301 549 clauses

The EN 301 549 clause of each criterion, by version of the standard
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)
Before and after Example data

assets/base.css.liquid Changes to assets/base.css.liquid: 7 added, 0 removed

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)
adds a brand-coloured :focus-visible ring and scopes the outline resets Example data

Resources/app/storefront/src/scss/_variant-broken.scss Changes to Resources/app/storefront/src/scss/_variant-broken.scss: 5 added, 5 removed

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

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)
replaces the outline reset of the fixture theme with a brand-coloured outline Example data

assets/css/main.css Changes to assets/css/main.css: 9 added, 3 removed

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)
adds the outline, removes the outline: none rule and validates Example data

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

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

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 check

Or 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

Write to Validiscan

For a free check, give the address of your shop and its platform.

Validiscan uses what you send to answer you. Privacy notice