焦点指示器与键盘导航
应用 ring 工具类显示清晰的焦点样式,使用 focus-visible 避免点击时显示焦点,并确保所有交互元素都可通过键盘访问。
焦点指示器与键盘导航 是 CoddyKit 上的免费 Tailwind CSS Academy 课时。 这是第 2 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Tailwind CSS Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Tailwind CSS Academy 课程共包含 4 节课。
本课时的部分内容尚未翻译,以英文显示。
Why Keyboard Navigation Matters
Not all users navigate with a mouse. Users with motor disabilities rely on keyboards, switch controls, or voice navigation. Power users prefer keyboard shortcuts for efficiency. Screen reader users navigate primarily with the keyboard. WCAG 2.4.3 Focus Order requires all functionality to be operable via keyboard, and WCAG 2.4.7 requires that keyboard-focused elements have a visible focus indicator. Tailwind provides utilities to implement both requirements.
Default Focus Styles and Their Problems
Browsers provide default focus indicators (typically a blue outline), but many designers remove them globally with outline: none or outline: 0 because they appear on mouse clicks too. This is an accessibility disaster — removing the outline means keyboard users have no visual cue showing which element has focus. Tailwind's modern approach distinguishes between mouse focus and keyboard focus using the focus-visible pseudo-class.
/* NEVER do this globally */
* {
outline: none; /* destroys keyboard accessibility */
}
/* WRONG approach in Tailwind */
<button class='focus:outline-none'>...</button>
/* Removes focus for keyboard users too */
/* BETTER: suppress only for mouse users */
<button class='focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500'>
This button shows ring for keyboard users only
</button>Tailwind's focus-visible Variant
The focus-visible: variant targets the :focus-visible CSS pseudo-class, which the browser applies when focus was received via keyboard navigation (Tab, arrow keys) but NOT when focus was received via mouse click. This is exactly what designers want — no focus ring on mouse click, but a clear ring when tabbing. Use focus-visible:ring-2 focus-visible:ring-blue-500 as a standard focus style for interactive elements.
<!-- Standard button with focus-visible ring -->
<button
class='
px-4 py-2 bg-blue-600 text-white rounded-lg font-medium
focus:outline-none
focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2
'
>
Submit
</button>
<!-- Link with focus-visible style -->
<a
href='/docs'
class='
text-blue-700 underline
focus:outline-none
focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-1 focus-visible:rounded
'
>
Documentation
</a>Ring Utilities for Focus Indicators
Tailwind's ring-* utilities create a CSS box-shadow-based outline that appears outside the element's border without affecting layout. ring-2 sets a 2px ring, ring-blue-500 sets its color, and ring-offset-2 adds a 2px gap between the element's border and the ring, creating a clear separation that makes the focus indicator more visible on colored backgrounds.
<!-- Ring width options -->
<button class='ring-1 ...'>1px ring</button>
<button class='ring-2 ...'>2px ring (recommended)</button>
<button class='ring-4 ...'>4px ring (high visibility)</button>
<!-- Ring with offset (recommended for buttons) -->
<button class='focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2'>
With offset gap
</button>
<!-- Ring offset on dark background -->
<div class='bg-gray-900 p-4'>
<button class='
bg-blue-600 text-white px-4 py-2 rounded
focus-visible:ring-2 focus-visible:ring-blue-400
focus-visible:ring-offset-2 focus-visible:ring-offset-gray-900
'>Dark bg button</button>
</div>Tab Order and tabindex
The keyboard Tab order should follow the visual reading order of the page (top to bottom, left to right in LTR layouts). Use semantic HTML elements like button, a, and input that are natively focusable in the correct order. Avoid using tabindex to force unnatural focus orders. Use tabindex='0' to make a non-interactive element (like a custom component) keyboard focusable, and tabindex='-1' to remove an element from the tab sequence while keeping it programmatically focusable.
<!-- Native elements: in tab order automatically -->
<button>First</button> <!-- tab stop 1 -->
<a href='/'>Second</a> <!-- tab stop 2 -->
<input type='text' /> <!-- tab stop 3 -->
<!-- Custom component: add to tab order with tabindex='0' -->
<div
role='button'
tabindex='0'
class='focus-visible:ring-2 focus-visible:ring-blue-500'
onKeyDown={(e) => e.key === 'Enter' && handleClick()}
>
Custom interactive element
</div>
<!-- Remove from tab order but allow programmatic focus -->
<div tabindex='-1' ref={modalPanelRef}>
Modal content (focused by JS, not Tab)
</div>Focus Management in SPAs
In Single Page Applications, route changes and dynamic content updates do not trigger a page reload, so the browser does not reset focus. After navigation, programmatically move focus to the new page's main heading or skip-link target. After a modal opens, move focus inside it. After it closes, return focus to the triggering element. Use React's useRef and element.focus() to implement these patterns.
// Focus management after route change (React/Next.js)
import { useEffect, useRef } from 'react';
import { usePathname } from 'next/navigation';
function MainContent({ children }) {
const mainRef = useRef(null);
const pathname = usePathname();
useEffect(() => {
// Move focus to main content on route change
mainRef.current?.focus();
}, [pathname]);
return (
<main
ref={mainRef}
tabIndex={-1} // focusable but not in tab order
className='focus:outline-none'
>
{children}
</main>
);
}Skip Navigation Links
A skip navigation link is a visually hidden link at the top of the page that becomes visible when focused, allowing keyboard users to jump directly to the main content area without tabbing through all navigation links. This is required by WCAG 2.4.1 (Bypass Blocks). Tailwind's sr-only and focus:not-sr-only (or focus:ring) utilities implement this elegantly.
<!-- Skip nav: hidden until focused by keyboard -->
<a
href='#main-content'
class='
sr-only
focus:not-sr-only focus:fixed focus:top-4 focus:left-4 focus:z-50
focus:bg-white focus:text-blue-700 focus:font-semibold
focus:px-4 focus:py-2 focus:rounded-lg focus:shadow-lg
focus:ring-2 focus:ring-blue-500
'
>
Skip to main content
</a>
<nav><!-- navigation --></nav>
<main id='main-content' tabIndex={-1} className='focus:outline-none'>
<!-- page content -->
</main>Arrow Key Navigation Patterns
Some interactive components should respond to arrow keys in addition to Tab. Toolbars and menu bars use left/right arrows to move between items. Select menus and listboxes use up/down arrows. Tab panels use arrow keys to switch tabs. Implementing arrow key navigation in custom components requires JavaScript key event handlers that move focus between related interactive elements.
// Arrow key navigation for a custom tab list
function TabList({ tabs }) {
const tabRefs = useRef([]);
const handleKeyDown = (e, index) => {
let newIndex;
if (e.key === 'ArrowRight') newIndex = (index + 1) % tabs.length;
if (e.key === 'ArrowLeft') newIndex = (index - 1 + tabs.length) % tabs.length;
if (e.key === 'Home') newIndex = 0;
if (e.key === 'End') newIndex = tabs.length - 1;
if (newIndex !== undefined) {
e.preventDefault();
tabRefs.current[newIndex]?.focus();
}
};
return (
<div role='tablist'>
{tabs.map((tab, i) => (
<button
key={tab.id}
ref={el => tabRefs.current[i] = el}
role='tab'
onKeyDown={e => handleKeyDown(e, i)}
className='focus-visible:ring-2 focus-visible:ring-blue-500'
>
{tab.label}
</button>
))}
</div>
);
}High Visibility Focus Styles for Accessibility
WCAG 2.4.11 (Focus Appearance, WCAG 2.2) requires that focus indicators meet minimum size and contrast requirements. A focus ring should be at least 2px thick and have at least a 3:1 contrast ratio against both the focused element and the adjacent background. Tailwind's ring-2 with a suitably contrasting color meets this requirement. For high-contrast mode support, consider using outline instead of box-shadow which Windows High Contrast Mode respects.
/* High contrast mode compatible focus style */
@media (forced-colors: active) {
button:focus-visible,
a:focus-visible,
input:focus-visible {
outline: 3px solid ButtonText;
outline-offset: 2px;
}
}
/* Tailwind equivalent for standard mode */
<button
class='
focus:outline-none
focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600
/* Fallback for High Contrast Mode uses CSS above */
'
>
Accessible button
</button>Testing Keyboard Navigation
Test keyboard navigation manually: put down the mouse entirely and navigate your entire application using only Tab, Shift+Tab, Enter, Space, and arrow keys. Every interactive element should be reachable, every action should be performable, and every focused element should have a visible indicator. Tools like axe DevTools can catch missing focus indicators automatically, but manual testing reveals logical flow issues that automated tools miss.
/*
Keyboard navigation test checklist:
Tab through entire page:
[ ] Every interactive element is reachable
[ ] Every focused element shows a visible indicator
[ ] Tab order follows visual reading order
[ ] No focus traps outside intentional ones (modals)
Activate elements:
[ ] Enter activates buttons and links
[ ] Space activates checkboxes and toggles
[ ] Arrow keys navigate menus, tabs, selects
Modal behavior:
[ ] Focus enters modal on open
[ ] Tab stays inside modal while open
[ ] Escape closes modal
[ ] Focus returns to trigger on close
Automated:
axe-core, Lighthouse accessibility audit
*/Creating a Global Focus Style Baseline
Define a consistent focus style as a global baseline in your CSS rather than repeating it on every element. Use addBase in a Tailwind plugin or add it in the @layer base block of your global CSS. This ensures all natively focusable elements get a consistent, accessible focus indicator by default, and custom components only need to opt in to the same pattern.
/* globals.css — consistent focus baseline */
@layer base {
:focus-visible {
outline: 2px solid #3b82f6; /* blue-500 */
outline-offset: 2px;
border-radius: 0.25rem;
}
/* Remove outline only for mouse users */
:focus:not(:focus-visible) {
outline: none;
}
}
/* Components that need ring instead of outline: */
/* Still override per-element with focus-visible:ring-2 etc. */
<button class='focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500'>Quick Check
Test your understanding of Tailwind CSS Mastery concepts from this lesson.
Lesson Recap
In this lesson you learned: focus-visible: shows focus rings only for keyboard users, ring-2 with ring-offset-2 creates accessible and visually clear focus indicators, and skip navigation links with sr-only allow keyboard users to bypass repetitive navigation. Next up we cover ARIA attributes and how to use Tailwind's sr-only utility for screen reader support.
常见问题解答
「焦点指示器与键盘导航」课时是免费的吗?
是的 — 「焦点指示器与键盘导航」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Tailwind CSS Academy 课程的其余内容,请升级到 CoddyKit PRO。 Tailwind CSS Academy 课程共包含 4 节课。
「焦点指示器与键盘导航」这节课中我会学到什么?
应用 ring 工具类显示清晰的焦点样式,使用 focus-visible 避免点击时显示焦点,并确保所有交互元素都可通过键盘访问。 你通过在浏览器中直接运行的动手代码来练习 Tailwind CSS Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Tailwind CSS Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Tailwind CSS Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 4 节。
「焦点指示器与键盘导航」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Tailwind CSS Academy 课中编写并运行代码吗?
能。每节 Tailwind CSS Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- 颜色对比度与易读文本
- 焦点指示器与键盘导航
- ARIA 属性与屏幕阅读器
- 无障碍表单组件