Skip to content

Keyboard Navigation

19.3.1. Focus Management

All interactive UI elements must be accessible and operable via keyboard. The tabindex attribute must be used according to the following rules.

tabindex ValueMeaningUsage Rules
0Included in the natural Tab orderUsed to make non-interactive elements focusable
-1Excluded from Tab order, focusable programmaticallyUsed to move focus via script in modals, dynamic content, etc.
Positive (1 or higher)ProhibitedArbitrarily alters Tab order, causing unpredictable behavior
  • Native interactive elements such as <button>, <a>, and <input> receive focus by default, so tabindex must not be specified separately for them.
  • Binding click events to non-interactive elements such as <div> and <span> should be avoided. Use <button> elements instead.

A focus indicator (Focus Ring) must be visually displayed on all focusable elements.

css
/* Default focus indicator style */
:focus-visible {
  outline: 2px solid #2563eb;
  outline-offset: 2px;
}

/* Removing outline is prohibited */
/* Incorrect example: *:focus { outline: none; } */
  • The focus indicator must not be removed with outline: none. outline: none may be used only when replacing with a custom style, and an equivalent visual indicator must be provided.
  • Using :focus-visible to apply the indicator only to keyboard focus is recommended.

19.3.2. Tab Order

The order of Tab key navigation must follow the logical flow and visual layout order of the content.

  • The DOM order must match the visual order. Changing only the visual order with CSS properties such as order, flex-direction: row-reverse, and position: absolute causes a mismatch with the Tab order.
  • When layout must be rearranged with CSS, the DOM order itself must be changed to maintain the logical flow.
  • Dynamically inserted content (toasts, dropdowns, etc.) must be positioned so as not to disrupt the Tab order.
vue
<!-- Correct: DOM order = Visual order -->
<template>
  <nav>
    <a href="/home">Home</a>
    <a href="/about">About</a>
    <a href="/contact">Contact</a>
  </nav>
  <main>
    <h1>Page Title</h1>
    <p>Body content</p>
  </main>
</template>

<!-- Incorrect: Only visual order reversed via CSS -->
<!--
<template>
  <div class="flex flex-row-reverse">
    <button>Appears first but Tab order is last</button>
    <button>Appears last but Tab order is first</button>
  </div>
</template>
-->

19.3.3. Keyboard Shortcuts

Interactive components must support the following keyboard actions.

KeyActionApplicable To
EnterButton click, form submit, link activationButtons, forms, links
EscapeClose modal, close dropdown, cancel actionModals, dropdowns, popovers
SpaceToggle checkbox, button clickCheckboxes, buttons
Arrow keysSwitch tab panels, navigate menu items, select radio buttonsTabs, menus, radio groups
vue
<script setup lang="ts">
import { ref } from 'vue'

const isOpen = ref(false)

function handleKeydown(event: KeyboardEvent) {
  if (event.key === 'Escape') {
    isOpen.value = false
  }
}
</script>

<template>
  <div @keydown="handleKeydown">
    <button @click="isOpen = !isOpen">
      Open Menu
    </button>
    <ul v-if="isOpen" role="menu">
      <li role="menuitem" tabindex="0">Item 1</li>
      <li role="menuitem" tabindex="0">Item 2</li>
      <li role="menuitem" tabindex="0">Item 3</li>
    </ul>
  </div>
</template>
  • Keyboard actions must be handled with @keydown events. @keypress must not be used.
  • Using Vue's key modifiers (@keydown.escape, @keydown.enter) is recommended.
  • When providing custom keyboard shortcuts, they must not conflict with browser and operating system default shortcuts.

19.3.4. Skip Navigation

A skip navigation link must be provided to bypass repetitive navigation areas and jump directly to the main content.

vue
<template>
  <a
    href="#main-content"
    class="skip-nav"
  >
    Skip to main content
  </a>
  <header>
    <nav>
      <!-- Navigation items -->
    </nav>
  </header>
  <main id="main-content" tabindex="-1">
    <!-- Main content -->
  </main>
</template>

<style scoped>
.skip-nav {
  position: absolute;
  top: -100%;
  left: 0;
  z-index: 9999;
  padding: 8px 16px;
  background-color: #ffffff;
  color: #1a1a1a;
  font-weight: bold;
  text-decoration: none;
}

.skip-nav:focus {
  top: 0;
}
</style>
  • The skip navigation link must be placed as the first element at the very top of the page.
  • It must be hidden from view by default and displayed on screen only when it receives focus.
  • tabindex="-1" must be specified on the link target (#main-content) to enable it to receive focus.
  • When the navigation area is complex, additional shortcut links to the search area or sidebar may be provided.

TIENIPIA QUALIFIED STANDARD