Tailwind CSS Academy · 강의

포커스 표시기와 키보드 탐색

링 유틸리티를 적용하여 포커스 스타일을 눈에 띄게 표시하고, 클릭할 때 포커스가 표시되지 않도록 focus-visible을 사용하며, 모든 상호작용 요소에 키보드로 접근할 수 있게 합니다.

레슨 2/413개 단계

포커스 표시기와 키보드 탐색은(는) CoddyKit의 무료 Tailwind CSS Academy 강의입니다. 이것은 4개 중 2번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 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 튜터와 함께 HTML을(를) 배우세요 — 무료

브라우저에서 실제 코드를 작성하고 실행하며, 24/7 AI 튜터로부터 즉각적인 도움을 받고, 웹이나 앱에서 중단한 부분부터 계속 학습하세요.

코스
30
레슨
120

자주 묻는 질문

“포커스 표시기와 키보드 탐색” 강의는 무료인가요?

네 — “포커스 표시기와 키보드 탐색” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Tailwind CSS Academy 강의 전체를 잠금 해제할 수 있습니다. Tailwind CSS Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

“포커스 표시기와 키보드 탐색”에서 뭘 배우나요?

링 유틸리티를 적용하여 포커스 스타일을 눈에 띄게 표시하고, 클릭할 때 포커스가 표시되지 않도록 focus-visible을 사용하며, 모든 상호작용 요소에 키보드로 접근할 수 있게 합니다. 브라우저에서 직접 실행하는 실습 코드로 Tailwind CSS Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

Tailwind CSS Academy을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 Tailwind CSS Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 2번째 강의입니다.

“포커스 표시기와 키보드 탐색” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 Tailwind CSS Academy 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 Tailwind CSS Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. 색상 대비와 읽기 쉬운 텍스트
  2. 포커스 표시기와 키보드 탐색
  3. ARIA 속성과 스크린 리더
  4. 접근성 높은 폼 컴포넌트
← Tailwind CSS Academy(으)로 돌아가기