Loading Developer Playground

Loading ...

Skip to main content
ARIA ROLEWidget Roles

menuitemcheckbox

A checkable menuitem with three possible values: true, false, or mixed. Used for toggleable options within dropdown menus.

Parent Roles
menu, menubar
Required Attribute
aria-checked
Keyboard Support
Enter, Space
Focus Management
tabindex="-1"

Overview

The menuitemcheckbox role defines a checkable menu item within a menu. Unlike a regular menuitem, it maintains a checked state that can be toggled by the user.

This role supports three states via aria-checked:

  • true - Item is checked/selected
  • false - Item is unchecked/deselected
  • mixed - Partially checked (for parent items with some children checked)

When to Use menuitemcheckbox

Use menuitemcheckbox when you need toggleable options in a dropdown menu where multiple options can be selected simultaneously. Common examples include text formatting menus (Bold, Italic, Underline) and view option menus (Show Toolbar, Show Sidebar).

Live Demo: Interactive Menu Checkboxes

Text Formatting Menu

Current selections: Italic

View Options Menu

File Selection (Mixed State Example)

Notice the "Select All" shows a mixed state (−) when only some files are selected.

Try with keyboard: Open a menu, then use / to navigate, Enter or Space to toggle, and Escape to close.

Code Examples

Basic Usage

<!-- Basic menuitemcheckbox in a dropdown menu -->
<button 
  aria-haspopup="menu" 
  aria-expanded="false"
  id="menu-button"
>
  Format Options
</button>

<div role="menu" aria-labelledby="menu-button">
  <div
    role="menuitemcheckbox"
    aria-checked="false"
    tabindex="-1"
  >
    Bold
  </div>
  <div
    role="menuitemcheckbox"
    aria-checked="true"
    tabindex="-1"
  >
    Italic
  </div>
  <div
    role="menuitemcheckbox"
    aria-checked="false"
    tabindex="-1"
  >
    Underline
  </div>
</div>

Mixed State (Tri-State)

<!-- menuitemcheckbox with mixed state (tri-state) -->
<div role="menu" aria-label="File Selection">
  <!-- Parent checkbox with mixed state -->
  <div
    role="menuitemcheckbox"
    aria-checked="mixed"
    tabindex="-1"
  >
    Select All Files
  </div>
  
  <!-- Child checkboxes -->
  <div
    role="menuitemcheckbox"
    aria-checked="true"
    tabindex="-1"
  >
    document.pdf
  </div>
  <div
    role="menuitemcheckbox"
    aria-checked="false"
    tabindex="-1"
  >
    image.png
  </div>
  <div
    role="menuitemcheckbox"
    aria-checked="true"
    tabindex="-1"
  >
    notes.txt
  </div>
</div>

<!-- 
  aria-checked="mixed" indicates partial selection:
  - When some (but not all) children are selected
  - Screen readers announce "partially checked" or "mixed"
-->

Keyboard Support

<!-- Accessible menuitemcheckbox with keyboard support -->
<script>
  const menu = document.querySelector('[role="menu"]');
  const items = menu.querySelectorAll('[role="menuitemcheckbox"]');
  let currentIndex = 0;
  
  menu.addEventListener('keydown', (e) => {
    switch (e.key) {
      case 'ArrowDown':
        e.preventDefault();
        currentIndex = (currentIndex + 1) % items.length;
        items[currentIndex].focus();
        break;
        
      case 'ArrowUp':
        e.preventDefault();
        currentIndex = (currentIndex - 1 + items.length) % items.length;
        items[currentIndex].focus();
        break;
        
      case 'Home':
        e.preventDefault();
        currentIndex = 0;
        items[currentIndex].focus();
        break;
        
      case 'End':
        e.preventDefault();
        currentIndex = items.length - 1;
        items[currentIndex].focus();
        break;
        
      case 'Enter':
      case ' ':
        e.preventDefault();
        toggleCheckbox(items[currentIndex]);
        break;
        
      case 'Escape':
        e.preventDefault();
        closeMenu();
        break;
    }
  });
  
  function toggleCheckbox(item) {
    const current = item.getAttribute('aria-checked');
    // For mixed state, clicking toggles to true
    const newState = current === 'true' ? 'false' : 'true';
    item.setAttribute('aria-checked', newState);
  }
</script>

React Component

// React menuitemcheckbox Component
import { useState, useRef, useEffect, useCallback } from 'react';

interface MenuItem {
  id: string;
  label: string;
  checked: boolean | 'mixed';
}

function MenuWithCheckboxes() {
  const [isOpen, setIsOpen] = useState(false);
  const [items, setItems] = useState<MenuItem[]>([
    { id: 'bold', label: 'Bold', checked: false },
    { id: 'italic', label: 'Italic', checked: true },
    { id: 'underline', label: 'Underline', checked: false },
  ]);
  const [focusedIndex, setFocusedIndex] = useState(0);
  const menuRef = useRef<HTMLDivElement>(null);
  const buttonRef = useRef<HTMLButtonElement>(null);

  const toggleItem = useCallback((id: string) => {
    setItems(prev => prev.map(item => ({
      ...item,
      checked: item.id === id 
        ? (item.checked === true ? false : true)
        : item.checked
    })));
  }, []);

  const handleKeyDown = (e: React.KeyboardEvent) => {
    switch (e.key) {
      case 'ArrowDown':
        e.preventDefault();
        setFocusedIndex(prev => (prev + 1) % items.length);
        break;
      case 'ArrowUp':
        e.preventDefault();
        setFocusedIndex(prev => 
          (prev - 1 + items.length) % items.length
        );
        break;
      case 'Enter':
      case ' ':
        e.preventDefault();
        toggleItem(items[focusedIndex].id);
        break;
      case 'Escape':
        setIsOpen(false);
        buttonRef.current?.focus();
        break;
    }
  };

  // Focus first item when menu opens
  useEffect(() => {
    if (isOpen) {
      setFocusedIndex(0);
      menuRef.current?.focus();
    }
  }, [isOpen]);

  return (
    <div className="relative">
      <button
        ref={buttonRef}
        aria-haspopup="menu"
        aria-expanded={isOpen}
        onClick={() => setIsOpen(!isOpen)}
      >
        Format Options
      </button>

      {isOpen && (
        <div
          ref={menuRef}
          role="menu"
          aria-label="Format Options"
          tabIndex={-1}
          onKeyDown={handleKeyDown}
          className="absolute mt-1 bg-white shadow-lg rounded"
        >
          {items.map((item, index) => (
            <div
              key={item.id}
              role="menuitemcheckbox"
              aria-checked={item.checked}
              tabIndex={index === focusedIndex ? 0 : -1}
              onClick={() => toggleItem(item.id)}
              className={`px-4 py-2 flex items-center gap-2 ${
                index === focusedIndex ? 'bg-blue-100' : ''
              }`}
            >
              <span aria-hidden="true">
                {item.checked === true ? '☑' : 
                 item.checked === 'mixed' ? '▣' : '☐'}
              </span>
              {item.label}
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

Grouped Menu Items

<!-- Grouped menuitemcheckbox items -->
<div role="menu" aria-label="Document Settings">
  <!-- Group: Text Formatting -->
  <div role="group" aria-label="Text Formatting">
    <div role="presentation" class="menu-group-label">
      Text Formatting
    </div>
    <div role="menuitemcheckbox" aria-checked="true" tabindex="-1">
      Bold
    </div>
    <div role="menuitemcheckbox" aria-checked="false" tabindex="-1">
      Italic
    </div>
  </div>
  
  <!-- Separator -->
  <div role="separator"></div>
  
  <!-- Group: View Options -->
  <div role="group" aria-label="View Options">
    <div role="presentation" class="menu-group-label">
      View Options
    </div>
    <div role="menuitemcheckbox" aria-checked="true" tabindex="-1">
      Show Toolbar
    </div>
    <div role="menuitemcheckbox" aria-checked="false" tabindex="-1">
      Show Sidebar
    </div>
  </div>
</div>

<!-- 
  Use role="group" to semantically group related items
  Use role="separator" between groups
  Group labels should use role="presentation" 
-->

Keyboard Support

EnterToggles the checkbox state
SpaceToggles the checkbox state
Moves focus to next menu item
Moves focus to previous menu item
HomeMoves focus to first menu item
EndMoves focus to last menu item
EscapeCloses the menu and returns focus to trigger

Required & Supported Attributes

Required Attributes

aria-checked

Required. Indicates the current checked state of the menu item. Must be one of: true, false, or mixed.

Supported Attributes

aria-checkedRequired

Required. Current checked state (true/false/mixed)

aria-disabled

Indicates item is disabled and not interactive

aria-label

Accessible name for the menu item

aria-labelledby

References element(s) that label the item

aria-describedby

References element(s) that describe the item

tabindex

Should be -1 (focus managed by menu container)

Best Practices

Always include aria-checked attribute with a valid value

Use within a menu or menubar parent container

Provide visual indication of checked state (checkmark icon)

Support both Enter and Space key for toggling

Use tabindex="-1" and manage focus via arrow keys

Update aria-checked immediately when toggled

Use mixed state for parent items with partially selected children

×

Don't use for mutually exclusive options (use menuitemradio instead)

×

Don't place outside of a menu/menubar context

×

Don't forget to provide visible focus indicators

×

Don't rely only on color to indicate checked state

menuitemcheckbox vs menuitemradio

menuitemcheckbox

  • Multiple items can be checked
  • Supports mixed state
  • Example: Bold, Italic, Underline
  • Uses checkbox-style visual

menuitemradio

  • Only one item can be selected
  • No mixed state
  • Example: Font size, Theme selection
  • Uses radio-style visual

Common Use Cases

Text Formatting

Bold, Italic, Underline, Strikethrough

View Options

Show/hide toolbars, sidebars, panels

Filter Menus

Multiple category selection in filters

File Selection

Batch file operations with select all

Notification Settings

Toggle different notification types

Layer Visibility

Show/hide layers in design tools

Column Visibility

Data table column toggles

Feature Toggles

Enable/disable multiple features

Accessibility Notes

Screen Reader Announcements

Screen readers announce menuitemcheckbox elements as "checkbox menu item" followed by the label and state. For example: "Bold, checkbox menu item, checked" or "Select All, checkbox menu item, mixed". The mixed state is announced as "partially checked" or "mixed" depending on the screen reader.

Focus Management

Individual menu items should have tabindex="-1" and focus should be managed programmatically using arrow keys. The currently focused item can use tabindex="0" for roving tabindex pattern. When the menu opens, focus should move to the first item.

Visual Indicators

Always provide clear visual indicators for each state. Use a checkmark (✓) for checked, an empty box for unchecked, and a horizontal line (−) or partially filled box for mixed. Ensure these indicators are not relying solely on color - use shape and icons as well.

Related Roles & Attributes

Specifications & Resources