Falytom logoFALYTOM
Games
ToolsUI ComponentsServicesProductsAbout
LoginSign Up
menu
  1. Home
  2. /Web Development
  3. /Html
  4. /Keyboard Navigation
Your Progress0%
0 of 76 completed

Topics

Getting Started

  • What is HTML?
  • How the Web Works
  • Setting Up Your Environment
  • Your First HTML Page

HTML Document Structure

  • The DOCTYPE Declaration
  • The HTML Element
  • The Head Section
  • Meta Tags
  • The Body Element
  • HTML Comments

Text and Formatting

  • Headings (h1-h6)
  • Paragraphs
  • Line Breaks and Horizontal Rules
  • Text Formatting (Bold, Italic, etc.)
  • Quotations and Citations
  • Preformatted Text and Code
  • Subscript and Superscript

Links and Navigation

  • Creating Links
  • Absolute vs Relative URLs
  • Link Targets and Attributes
  • Email and Phone Links
  • Internal Page Links

Images and Media

  • Adding Images
  • Image Attributes
  • Image Formats
  • Figure and Figcaption
  • Responsive Images
  • Audio Elements
  • Video Elements
  • Embedding Content

Lists

  • Unordered Lists
  • Ordered Lists
  • Description Lists
  • Nested Lists

Tables

  • Table Basics
  • Table Headers and Footers
  • Spanning Columns and Rows
  • Table Captions
  • Styling Tables

Forms - Part 1 (Basics)

  • Introduction to Forms
  • Text Input Fields
  • Labels and Placeholders
  • Textarea Element
  • Checkboxes
  • Radio Buttons

Forms - Part 2 (Advanced)

  • Select Dropdowns
  • File Upload Inputs
  • HTML5 Input Types
  • Form Validation
  • Form Buttons
  • Fieldsets and Legends
  • Form Accessibility

Semantic HTML

  • What is Semantic HTML?
  • Header and Footer
  • Nav Element
  • Main and Article
  • Section and Aside
  • Div vs Semantic Elements
  • Time and Mark Elements

HTML5 Features

  • HTML5 Overview
  • Canvas Element
  • SVG in HTML
  • Geolocation API
  • Drag and Drop
  • Local Storage
  • Web Workers

Web Accessibility

  • Introduction to Accessibility
  • ARIA Attributes
  • Keyboard Navigation
  • Alt Text Best Practices
  • Color Contrast and Typography
  • Testing for Accessibility

Best Practices & SEO

  • HTML Best Practices
  • SEO Fundamentals
  • Meta Tags for SEO
  • Structured Data

Keyboard Navigation

Making your website accessible without a mouse

Keyboard navigation ensures that all functionality on your website can be accessed and operated using only a keyboard. This is essential for users with motor disabilities, screen reader users, power users, and anyone who prefers keyboard shortcuts over a mouse.

Why Keyboard Navigation Matters

Many users rely exclusively on keyboard navigation:

  • Screen reader users: Navigate primarily with keyboard
  • Motor disabilities: May not be able to use a mouse
  • Temporary injuries: Broken arm, wrist pain
  • Power users: Prefer keyboard for efficiency
  • Assistive technology users: Switch controls, voice commands

If your site isn't keyboard accessible, you're excluding a significant portion of your audience.

Essential Keyboard Keys

Users navigate websites using these primary keys:

  • Tab: Move forward through interactive elements
  • Shift + Tab: Move backward through interactive elements
  • Enter/Return: Activate links and buttons
  • Space: Activate buttons, check checkboxes, scroll page
  • Arrow keys: Navigate within components (menus, sliders, tabs)
  • Escape: Close dialogs, menus, or cancel actions
  • Home/End: Jump to beginning/end of content

Focus Management

Focus is the visual indicator showing which element is currently active for keyboard interaction. Proper focus management is crucial for keyboard accessibility.

Visible Focus Indicators

Always ensure users can see where they are on the page:

focus-styles.css
/* Good: Visible, high-contrast focus indicator */
a:focus,
button:focus,
input:focus {
  outline: 3px solid #0066cc;
  outline-offset: 2px;
}

/* Better: Custom focus style that matches your design */
button:focus {
  outline: none; /* Only remove if you replace it! */
  box-shadow: 0 0 0 3px rgba(0, 102, 204, 0.5);
  border-color: #0066cc;
}

/* Best: Use :focus-visible for mouse vs keyboard */
button:focus {
  outline: none;
}

button:focus-visible {
  outline: 3px solid #0066cc;
  outline-offset: 2px;
}

/* NEVER do this without replacement */
*:focus {
  outline: none; /* This removes all focus indicators! */
}

Naturally Focusable Elements

These HTML elements are focusable by default:

focusable-elements.html
<!-- Naturally keyboard accessible -->
<a href="/page">Link</a>
<button>Button</button>
<input type="text">
<textarea></textarea>
<select>
  <option>Option 1</option>
</select>

<!-- NOT naturally focusable -->
<div>Not focusable</div>
<span>Not focusable</span>
<p>Not focusable</p>

The tabindex Attribute

The tabindex attribute controls whether an element can receive focus and its position in the tab order:

tabindex-usage.html
<!-- tabindex="0" - Add to tab order in natural position -->
<div tabindex="0" role="button" onclick="handleClick()">
  Custom Button
</div>

<!-- tabindex="-1" - Programmatically focusable, not in tab order -->
<div tabindex="-1" id="modal-content">
  This can receive focus via JavaScript
</div>

<!-- tabindex="1" or higher - DON'T USE THIS -->
<!-- Creates confusing tab order -->
<input tabindex="1" type="text">
<button tabindex="2">Submit</button>

Important:

Never use positive tabindex values (1, 2, 3, etc.). They override the natural tab order and create a confusing experience. Only use tabindex="0" or tabindex="-1".

Keyboard Event Handlers

When creating custom interactive elements, handle keyboard events properly:

keyboard-handlers.html
<div class="custom-button" 
     tabindex="0" 
     role="button"
     id="myButton">
  Click me
</div>

<script>
  const btn = document.getElementById('myButton');
  
  // Handle keyboard events
  btn.addEventListener('keydown', function(event) {
    // Enter or Space should activate the button
    if (event.key === 'Enter' || event.key === ' ') {
      event.preventDefault(); // Prevent space from scrolling
      this.click();
    }
  });
  
  // Handle click for mouse users
  btn.addEventListener('click', function() {
    console.log('Button activated!');
    // Perform action
  });
</script>

Skip Links

Skip links allow keyboard users to bypass repetitive content like navigation menus:

skip-link.html
<!DOCTYPE html>
<html lang="en">
<head>
  <style>
    /* Hide skip link until focused */
    .skip-link {
      position: absolute;
      top: -40px;
      left: 0;
      background: #000;
      color: #fff;
      padding: 8px;
      text-decoration: none;
      z-index: 100;
    }
    
    .skip-link:focus {
      top: 0;
    }
  </style>
</head>
<body>
  <!-- Skip link is the first focusable element -->
  <a href="#main-content" class="skip-link">
    Skip to main content
  </a>
  
  <header>
    <nav>
      <!-- 20+ navigation links here -->
    </nav>
  </header>
  
  <main id="main-content" tabindex="-1">
    <h1>Page Content</h1>
    <p>Main content starts here...</p>
  </main>
</body>
</html>

Focus Trapping in Modals

When a modal opens, trap focus inside it so users can't accidentally tab outside:

focus-trap.html
<button id="openModal">Open Modal</button>

<div id="modal" 
     role="dialog" 
     aria-modal="true"
     aria-labelledby="modal-title"
     hidden>
  <h2 id="modal-title">Modal Title</h2>
  <p>Modal content</p>
  <button id="confirmBtn">Confirm</button>
  <button id="closeBtn">Close</button>
</div>

<script>
  const modal = document.getElementById('modal');
  const openBtn = document.getElementById('openModal');
  const closeBtn = document.getElementById('closeBtn');
  let previouslyFocused;

  function openModal() {
    // Save current focus
    previouslyFocused = document.activeElement;
    
    // Show modal
    modal.hidden = false;
    
    // Move focus to modal
    modal.querySelector('button').focus();
    
    // Trap focus
    trapFocus(modal);
    
    // Listen for Escape key
    document.addEventListener('keydown', handleEscape);
  }

  function closeModal() {
    modal.hidden = true;
    document.removeEventListener('keydown', handleEscape);
    
    // Return focus to trigger button
    if (previouslyFocused) {
      previouslyFocused.focus();
    }
  }

  function handleEscape(e) {
    if (e.key === 'Escape') {
      closeModal();
    }
  }

  function trapFocus(element) {
    const focusableElements = element.querySelectorAll(
      'a[href], button:not([disabled]), textarea, input, select'
    );
    
    const firstFocusable = focusableElements[0];
    const lastFocusable = focusableElements[focusableElements.length - 1];

    element.addEventListener('keydown', function(e) {
      if (e.key === 'Tab') {
        if (e.shiftKey) {
          // Shift + Tab
          if (document.activeElement === firstFocusable) {
            e.preventDefault();
            lastFocusable.focus();
          }
        } else {
          // Tab
          if (document.activeElement === lastFocusable) {
            e.preventDefault();
            firstFocusable.focus();
          }
        }
      }
    });
  }

  openBtn.addEventListener('click', openModal);
  closeBtn.addEventListener('click', closeModal);
</script>

Common Keyboard Patterns

Tab Component

accessible-tabs.html
<div class="tabs">
  <div role="tablist" aria-label="Sample Tabs">
    <button role="tab" 
            aria-selected="true" 
            aria-controls="panel1"
            id="tab1">
      Tab 1
    </button>
    <button role="tab" 
            aria-selected="false" 
            aria-controls="panel2"
            id="tab2">
      Tab 2
    </button>
    <button role="tab" 
            aria-selected="false" 
            aria-controls="panel3"
            id="tab3">
      Tab 3
    </button>
  </div>
  
  <div role="tabpanel" id="panel1" aria-labelledby="tab1">
    <p>Content for Tab 1</p>
  </div>
  
  <div role="tabpanel" id="panel2" aria-labelledby="tab2" hidden>
    <p>Content for Tab 2</p>
  </div>
  
  <div role="tabpanel" id="panel3" aria-labelledby="tab3" hidden>
    <p>Content for Tab 3</p>
  </div>
</div>

<script>
  const tabs = document.querySelectorAll('[role="tab"]');
  const panels = document.querySelectorAll('[role="tabpanel"]');

  tabs.forEach((tab, index) => {
    // Arrow key navigation
    tab.addEventListener('keydown', (e) => {
      let newIndex;
      
      if (e.key === 'ArrowRight') {
        newIndex = (index + 1) % tabs.length;
        tabs[newIndex].focus();
        activateTab(newIndex);
      } else if (e.key === 'ArrowLeft') {
        newIndex = (index - 1 + tabs.length) % tabs.length;
        tabs[newIndex].focus();
        activateTab(newIndex);
      } else if (e.key === 'Home') {
        tabs[0].focus();
        activateTab(0);
      } else if (e.key === 'End') {
        tabs[tabs.length - 1].focus();
        activateTab(tabs.length - 1);
      }
    });
    
    // Click activation
    tab.addEventListener('click', () => {
      activateTab(index);
    });
  });

  function activateTab(index) {
    tabs.forEach((t, i) => {
      if (i === index) {
        t.setAttribute('aria-selected', 'true');
        panels[i].hidden = false;
      } else {
        t.setAttribute('aria-selected', 'false');
        panels[i].hidden = true;
      }
    });
  }
</script>

Dropdown Menu

accessible-dropdown.html
<div class="dropdown">
  <button aria-haspopup="true" 
          aria-expanded="false" 
          aria-controls="menu1"
          id="menuButton">
    Menu
  </button>
  
  <ul role="menu" id="menu1" hidden>
    <li role="menuitem">
      <a href="#option1">Option 1</a>
    </li>
    <li role="menuitem">
      <a href="#option2">Option 2</a>
    </li>
    <li role="menuitem">
      <a href="#option3">Option 3</a>
    </li>
  </ul>
</div>

<script>
  const button = document.getElementById('menuButton');
  const menu = document.getElementById('menu1');
  const items = menu.querySelectorAll('[role="menuitem"]');
  let currentIndex = -1;

  button.addEventListener('click', toggleMenu);
  
  button.addEventListener('keydown', (e) => {
    if (e.key === 'ArrowDown' || e.key === 'Enter' || e.key === ' ') {
      e.preventDefault();
      openMenu();
      items[0].focus();
    }
  });

  function toggleMenu() {
    const expanded = button.getAttribute('aria-expanded') === 'true';
    if (expanded) {
      closeMenu();
    } else {
      openMenu();
    }
  }

  function openMenu() {
    button.setAttribute('aria-expanded', 'true');
    menu.hidden = false;
  }

  function closeMenu() {
    button.setAttribute('aria-expanded', 'false');
    menu.hidden = true;
    button.focus();
  }

  // Arrow key navigation in menu
  items.forEach((item, index) => {
    item.addEventListener('keydown', (e) => {
      if (e.key === 'ArrowDown') {
        e.preventDefault();
        const next = (index + 1) % items.length;
        items[next].focus();
      } else if (e.key === 'ArrowUp') {
        e.preventDefault();
        const prev = (index - 1 + items.length) % items.length;
        items[prev].focus();
      } else if (e.key === 'Escape') {
        closeMenu();
      }
    });
  });
</script>

Roving Tabindex Pattern

For groups of similar elements (like toolbars or radio groups), use roving tabindex to allow only one item to be in tab order:

roving-tabindex.html
<div role="toolbar" aria-label="Text formatting">
  <button tabindex="0">Bold</button>
  <button tabindex="-1">Italic</button>
  <button tabindex="-1">Underline</button>
  <button tabindex="-1">Strikethrough</button>
</div>

<script>
  const toolbar = document.querySelector('[role="toolbar"]');
  const buttons = toolbar.querySelectorAll('button');

  buttons.forEach((button, index) => {
    button.addEventListener('keydown', (e) => {
      let newIndex;
      
      if (e.key === 'ArrowRight') {
        newIndex = (index + 1) % buttons.length;
      } else if (e.key === 'ArrowLeft') {
        newIndex = (index - 1 + buttons.length) % buttons.length;
      } else {
        return;
      }
      
      // Move tabindex
      buttons.forEach(btn => btn.tabIndex = -1);
      buttons[newIndex].tabIndex = 0;
      buttons[newIndex].focus();
    });
  });
</script>

Testing Keyboard Accessibility

To test keyboard accessibility:

  1. Unplug your mouse and navigate using only keyboard
  2. Press Tab to move through interactive elements
  3. Check focus indicators are visible at all times
  4. Verify all functionality works with keyboard
  5. Test custom widgets with appropriate keys (arrows, Enter, Space, Escape)
  6. Check tab order is logical and predictable
  7. Ensure no keyboard traps where users get stuck

Keyboard Navigation Best Practices

  • Never remove focus indicators without providing visible alternatives
  • Keep tab order logical: Follow visual order of page
  • Avoid positive tabindex values: Use 0 or -1 only
  • Provide skip links: Let users bypass repetitive content
  • Trap focus in modals: Keep users from tabbing outside
  • Return focus appropriately: When closing dialogs or deleting items
  • Support standard keys: Enter, Space, Escape, Arrows
  • Use semantic HTML: Get keyboard support for free
  • Test regularly: Try navigating without a mouse

Key Takeaways

  • All functionality must be accessible via keyboard alone
  • Always provide visible focus indicators for keyboard users
  • Use tabindex="0" to add elements to tab order,tabindex="-1" for programmatic focus
  • Never use positive tabindex values
  • Implement skip links to bypass repetitive content
  • Trap focus within modal dialogs
  • Handle keyboard events (Enter, Space, Escape, Arrows) for custom widgets
  • Test your site by navigating without a mouse

What's Next?

Now that you understand keyboard navigation, you can ensure your website is accessible to all users. In the next lesson, we'll explore alt text best practices for images, learning how to write effective descriptions for screen reader users.

Know someone who'd find this guide helpful? Please share!

Previous
ARIA Attributes
Next
Alt Text Best Practices

Never Miss a New HTML Tutorial

Join 1,000+ developers learning HTML step-by-step. Get new tutorials, tips, and exclusive resources delivered to your inbox - completely FREE.

You might also like

UI Components

Ready-made components for your projects

Developer Tools

Boost productivity with handy generators

HTML Game

Learn HTML concepts through interactive gameplay

Join Our Web Development Training Program

Master frontend & backend development with hands-on projects.

Menu

Topics

Getting Started

  • What is HTML?
  • How the Web Works
  • Setting Up Your Environment
  • Your First HTML Page

HTML Document Structure

  • The DOCTYPE Declaration
  • The HTML Element
  • The Head Section
  • Meta Tags
  • The Body Element
  • HTML Comments

Text and Formatting

  • Headings (h1-h6)
  • Paragraphs
  • Line Breaks and Horizontal Rules
  • Text Formatting (Bold, Italic, etc.)
  • Quotations and Citations
  • Preformatted Text and Code
  • Subscript and Superscript

Links and Navigation

  • Creating Links
  • Absolute vs Relative URLs
  • Link Targets and Attributes
  • Email and Phone Links
  • Internal Page Links

Images and Media

  • Adding Images
  • Image Attributes
  • Image Formats
  • Figure and Figcaption
  • Responsive Images
  • Audio Elements
  • Video Elements
  • Embedding Content

Lists

  • Unordered Lists
  • Ordered Lists
  • Description Lists
  • Nested Lists

Tables

  • Table Basics
  • Table Headers and Footers
  • Spanning Columns and Rows
  • Table Captions
  • Styling Tables

Forms - Part 1 (Basics)

  • Introduction to Forms
  • Text Input Fields
  • Labels and Placeholders
  • Textarea Element
  • Checkboxes
  • Radio Buttons

Forms - Part 2 (Advanced)

  • Select Dropdowns
  • File Upload Inputs
  • HTML5 Input Types
  • Form Validation
  • Form Buttons
  • Fieldsets and Legends
  • Form Accessibility

Semantic HTML

  • What is Semantic HTML?
  • Header and Footer
  • Nav Element
  • Main and Article
  • Section and Aside
  • Div vs Semantic Elements
  • Time and Mark Elements

HTML5 Features

  • HTML5 Overview
  • Canvas Element
  • SVG in HTML
  • Geolocation API
  • Drag and Drop
  • Local Storage
  • Web Workers

Web Accessibility

  • Introduction to Accessibility
  • ARIA Attributes
  • Keyboard Navigation
  • Alt Text Best Practices
  • Color Contrast and Typography
  • Testing for Accessibility

Best Practices & SEO

  • HTML Best Practices
  • SEO Fundamentals
  • Meta Tags for SEO
  • Structured Data
Falytom logoFALYTOM

Learn technologies like HTML, CSS, JavaScript, React, NodeJS and more through interactive games, tutorials, components and free tools. Transform your businesses with AI chatbots, SEO-optimized websites, and professional development services.

© 2025 Tomilola Group
  • Facebook logo
  • Twitter logo
  • Instagram logo
  • LinkedIn logo
  • GitHub logo
  • YouTube logo