All Projects → ianmcburnie → bones

ianmcburnie / bones

Licence: MIT license
Accessible HTML code patterns for common UI widgets such as tabs, menus, dialogs, etc.

Projects that are alternatives of or similar to bones

vue-focus-loop
Vue component that helps you to to trap focus in an element.
Stars: ✭ 23 (-70.89%)
Mutual labels:  accessibility, a11y
openacr
OpenACR is a digital native Accessibility Conformance Report (ACR). The initial development is based on Section 508 requirements. The main goal is to be able to compare the accessibility claims of digital products and services. A structured, self-validated, machine-readable documentation will provide for this.
Stars: ✭ 61 (-22.78%)
Mutual labels:  accessibility, a11y
agnostic-axe
Framework agnostic accessibility reporter, powered by axe-core
Stars: ✭ 80 (+1.27%)
Mutual labels:  accessibility, a11y
accessibility-resources
Screen reader and WCAG testing resources to maintain a consistent approach to testing and documenting behaviour.
Stars: ✭ 25 (-68.35%)
Mutual labels:  accessibility, a11y
jquery-accessible-simple-tooltip-aria
jQuery accessible simple tooltip window, using ARIA
Stars: ✭ 22 (-72.15%)
Mutual labels:  accessibility, a11y
Creatability Components
Web components for making creative tools more accessible.
Stars: ✭ 248 (+213.92%)
Mutual labels:  accessibility, a11y
accessible-name-automation-proof-of-concept
This is an experiment based on Accessibility Object Model (AOM). It tries to demonstrate that it is theoretically possible (in a certain way) to predict what the screen reader will say by focusing on semantic and non semantic elements with a bit of automated testing, thus reducing the need for manual testing.
Stars: ✭ 15 (-81.01%)
Mutual labels:  accessibility, a11y
Accessibilitysnapshot
Easy regression testing for iOS accessibility
Stars: ✭ 236 (+198.73%)
Mutual labels:  accessibility, a11y
cauldron
cauldron.dequelabs.com/
Stars: ✭ 50 (-36.71%)
Mutual labels:  accessibility, a11y
eufemia
DNB Design System
Stars: ✭ 38 (-51.9%)
Mutual labels:  accessibility, a11y
a11y-checker
Identifies accessibility issues in HTML markup.
Stars: ✭ 103 (+30.38%)
Mutual labels:  accessibility, a11y
makeup-js
Mono-repo for all vanilla JavaScript utility modules and headless ui
Stars: ✭ 28 (-64.56%)
Mutual labels:  accessibility, a11y
A11y Css Reset
A small set of global rules to make things accessible and reset default styling
Stars: ✭ 250 (+216.46%)
Mutual labels:  accessibility, a11y
accessibility-resources
A curated list of accessibility resources
Stars: ✭ 66 (-16.46%)
Mutual labels:  accessibility, a11y
Accessibility interview questions
A starting point for questions to ask someone that wants you to give them a job
Stars: ✭ 236 (+198.73%)
Mutual labels:  accessibility, a11y
chusho
A library of bare & accessible components and tools for Vue.js 3
Stars: ✭ 47 (-40.51%)
Mutual labels:  accessibility, a11y
Asqatasun
Mirror of Asqatasun ---> we've moved to GITLAB https://gitlab.com/asqatasun/Asqatasun - Opensource web site analyser, used for web accessibility "a11y"
Stars: ✭ 217 (+174.68%)
Mutual labels:  accessibility, a11y
Wai Tutorials
W3C WAI’s Web Accessibility Tutorials
Stars: ✭ 229 (+189.87%)
Mutual labels:  accessibility, a11y
ari
Accessible unstyled vue components
Stars: ✭ 22 (-72.15%)
Mutual labels:  accessibility, a11y
Tanaguru
Automated accessibility (a11y) testing tool, with emphasis on reliablity and automation
Stars: ✭ 116 (+46.84%)
Mutual labels:  accessibility, a11y

BONES

Widgets are stronger with bones. Does your widget have bones?

Contents

  1. Introduction
  2. Accordion
  3. Alert Dialog
  4. Breadcrumbs
  5. Carousel
  6. Checkbox
  7. Combobox
  8. Confirm Dialog
  9. Details
  10. Fake Menu
  11. Fake Tabs
  12. Flyout
  13. Fullscreen Dialog
  14. Icon Button
  15. Infotip
  16. Inline Notice
  17. Input Dialog
  18. Input Validation
  19. Lightbox Dialog
  20. Listbox
  21. Listbox Button
  22. Menu
  23. Menu Button
  24. Page Notice
  25. Pagination
  26. Panel Dialog
  27. Radio
  28. Select
  29. Switch
  30. Tabs
  31. Toast Dialog
  32. Tooltip
  33. Tourtip

Introduction

Bones provides lean, mean, semantic HTML markup for widgets; ensuring maximum Accessibility, SEO and Site Speed performance. Bones markup uses ARIA only where strictly necessary.

Bones is not intended to be an exhaustive set of instructions for creating accessible components. The primary intention of bones is to detail the structural and semantic markup requirements. For further guidance, please visit eBay MIND Patterns and/or WAI-ARIA Authoring Practices.

Bones advocates the Progressive Enhancement web-design strategy; building the web in a layered fashion that allows everyone to access the most important content and functionality.

Bones favors the the BEM (block, element, modifier) methodology and naming convention.

Accordion

The accordion is simply a list of details widgets. Each details summary should include a relevant heading-level tag.

<ul class="accordion" role="list" aria-roledescription="accordion">
    <li>
        <details class="details">
            <summary><h3>Panel 1</h3></summary>
            <ul>
                <li><a href="http://www.ebay.com">Item 1</a></li>
                <li><a href="http://www.ebay.com">Item 2</a></li>
                <li><a href="http://www.ebay.com">Item 3</a></li>
            </ul>
        </details>
    </li>
    <li>
        <details class="details">
            <summary><h3>Panel 2</h3></summary>
            <ul>
                <li><a href="http://www.ebay.com">Item 1</a></li>
                <li><a href="http://www.ebay.com">Item 2</a></li>
                <li><a href="http://www.ebay.com">Item 3</a></li>
            </ul>
        </details>
    </li>
</ul>

Alert Dialog

A lightbox dialog with a single button to acknowledge the alert content.

Try to bucket the main page content in an element that is not an ancestor of the dialog. This will vastly simplify the amount of DOM manipulation when implementing modal behaviour.

<body>
    <div>
        <!-- main page content -->
    </div>
    <div class="alert-dialog" role="alertdialog" aria-labelledby="alert-dialog-title" aria-modal="true" hidden>
        <div class="alert-dialog__window">
            <div class="alert-dialog__header">
                <h2 class="alert-dialog__title" id="dialog-title">Alert Dialog Title</h2>
            </div>
            <div class="alert-dialog__main">
                <!-- alert dialog content goes here -->
            </div>
            <div class="alert-dialog__footer">
                <button class="alert-dialog__acknowledge" type="button">OK</button>
            </div>
        </div>
    </div>
</body>

Breadcrumbs

The content is an ordered list of links inside a navigation landmark region.

<nav class="breadcrumbs" aria-labelledby="breadcrumbs_heading" role="navigation">
    <h2 class="clipped" id="breadcrumbs_heading">You are here</h2>
    <ol>
        <li>
            <a href="http://www.ebay.com">Great Grandparent Page</a>
            <svg focusable="false" height="10" width="10" aria-hidden="true">
                <use xlink:href="#icon-breadcrumb"></use>
            </svg>
        </li>
        <li>
            <a href="http://www.ebay.com">Grandparent Page</a>
            <svg focusable="false" height="10" width="10" aria-hidden="true">
                <use xlink:href="#icon-breadcrumb"></use>
            </svg>
        </li>
        <li>
            <a href="http://www.ebay.com">Parent Page</a>
            <svg focusable="false" height="10" width="10" aria-hidden="true">
                <use xlink:href="#icon-breadcrumb"></use>
            </svg>
        </li>
        <li>
            <a aria-current="page">Current Page</a>
        </li>
    </ol>
</nav>

While CSS can be used to generate a separator image or glyph using the ::after pseudo element, we find that inline SVG offers a more accessible and flexible approach.

Carousel

A carousel is a list of items. These items may contain anything - text, images, links, tiles, cards, etc. - but each item is responsible for managing its own markup and accessibility (e.g. tab-order, semantics, etc).

<div class="carousel" role="group" aria-labelledby="carousel-title" aria-roledescription="carousel">
    <button aria-label="Previous slide">
        <!-- SVG icon -->
    </button>
    <ul>
        <!-- onscreen items -->
        <li aria-hidden="false">...</li>
        <li aria-hidden="false">...</li>
        <li aria-hidden="false">...</li>
        <li aria-hidden="false">...</li>
        <li aria-hidden="false">...</li>
        <!-- offscreen items -->
        <li aria-hidden="true">...</li>
        <li aria-hidden="true">...</li>
        <li aria-hidden="true">...</li>
        <li aria-hidden="true">...</li>
        <li aria-hidden="true">...</li>
    </ul>
    <button aria-label="Next slide">
        <!-- SVG icon -->
    </button>
</div>

JavaScript must maintain the tabindex and aria-hidden state of items as they scroll in and out of view.

Checkbox

Native HTML checkboxes are 100% accessible by default.

To ensure correct grouping semantics, checkboxes should be placed inside of a fieldset with legend.

<fieldset>
    <legend>Auction Type</legend>
    <span>
        <input id="freeshipping" type="checkbox" name="freeshipping" />
        <label for="freeshipping">Free Shipping</label>
    </span>
    <span>
        <input id="endssoon" type="checkbox" name="endssoon" />
        <label for="endssoon">Ends soon</label>
    </span>
    <span>
        <input id="zerobids" type="checkbox" name="zerobids" />
        <label for="zerobids">Zero bids</label>
    </span>
</fieldset>

For vertically stacked checkboxes, simply switch the spans to divs.

Custom Checkbox Icon

A foreground SVG, combined with CSS, can be used as a facade over the real checkbox.

<span class="checkbox">
    <input class="checkbox__control" id="freeshipping" type="checkbox" name="freeshipping" />
    <span class="checkbox__icon" hidden>
        <svg aria-hidden="true" class="checkbox__unchecked" focusable="false">
            <use xlink:href="#icon-checkbox-unchecked"></use>
        </svg>
        <svg aria-hidden="true" class="checkbox__checked" focusable="false">
            <use xlink:href="#icon-checkbox-checked"></use>
        </svg>
    </span>
</span>

This markup assumes that the symbol definitions for #icon-checkbox-unchecked and #icon-checkbox-checked exist on the page. The hidden property ensures that the SVG icon is not visible alongside the native icon when the page is in a non-CSS state. This hidden property should be over-ridden by CSS.

Combobox

A textbox plus listbox combination.

Collapsed State

<div class="combobox" id="combobox-1">
    <span class="combobox__control">
        <input name="combobox-1-name" type="text" role="combobox" autocomplete="off" aria-expanded="false" aria-owns="combobox-1-listbox" />
    </span>
    <div class="combobox__overlay">
        <ul id="combobox-1-listbox" role="listbox">
            <li role="option" id="combobox-1-option-1">Option 1</li>
            <li role="option" id="combobox-1-option-2">Option 2</li>
            <li role="option" id="combobox-1-option-3">Option 3</li>
            ...
        </ul>
    </div>
</div>

Expanded State

<div class="combobox combobox--expanded" id="combobox-1">
    <span class="combobox__control">
        <input name="combobox-1-name" type="text" role="combobox" autocomplete="off" aria-expanded="true" aria-owns="combobox-1-listbox" />
    </span>
    <div class="combobox__overlay">
        <ul id="combobox-1-listbox" role="listbox">
            <li role="option" id="combobox-1-option-1">Option 1</li>
            <li role="option" id="combobox-1-option-2">Option 2</li>
            <li role="option" id="combobox-1-option-3">Option 3</li>
            ...
        </ul>
    </div>
</div>

JavaScript must update the aria-activedescendant attribute on the textbox to reflect the state of the currently active descendant listbox item.

Confirm Dialog

A lightbox dialog with buttons to cancel or confirm an action.

Try to bucket the main page content in an element that is not an ancestor of the dialog. This will vastly simplify the amount of DOM manipulation when implementing modal behaviour.

<body>
    <div>
        <!-- main page content -->
    </div>
    <div class="confirm-dialog" role="dialog" aria-labelledby="confirm-dialog-title" aria-modal="true" hidden>
        <div class="confirm-dialog__window">
            <div class="confirm-dialog__header">
                <h2 class="confirm-dialog__title" id="confirm-dialog-title">Confirm Dialog Title</h2>
            </div>
            <div class="confirm-dialog__main">
                <!-- confirm dialog content goes here -->
            </div>
            <div class="confirm-dialog__footer">
                <button class="confirm-dialog__cancel" type="button">Cancel</button>
                <button class="confirm-dialog__confirm" type="button">OK</button>
            </div>
        </div>
    </div>
</body>

Details

Uses the native HTML <details> tag.

<details class="details">
    <summary>Details</summary>
    <ul>
        <li><a href="http://www.ebay.com">Link 1</a></li>
        <li><a href="http://www.ebay.com">Link 2</a></li>
        <li><a href="http://www.ebay.com">Link 3</a></li>
    </ul>
</details>

Fake Menu

A fake menu is styled like a regular menu, but it contains a list of links and/or buttons instead of menu items.

<ul>
    <li>
        <a href="http://www.ebay.com">Link Text</a>
    </li>
    <li>
        <button type="button">Button Text</button>
    </li>
    <li>
        <a href="http://www.ebay.com">Link Text</a>
    </li>
</ul>

If the href value of a fake menu item matches the current page url, then add aria-current="page" to that anchor tag.

Fake-Menu Button

A button that opens a fake menu.

<div class="fake-menu">
    <button aria-expanded="false">Fake Menu</button>
    <div>
        <ul>
            <li>
                <a href="http://www.ebay.com">Link Text</a>
            </li>
            <li>
                <button type="button">Button Text</button>
            </li>
            <li>
                <a href="http://www.ebay.com">Link Text</a>
            </li>
        </ul>
    </div>
</div>

Fake Tabs

Fake tabs are simply a list of links styled to look like tabs.

<nav aria-labelledby="fake-tabs-title" class="fake-tabs" role="navigation">
    <h2 class="clipped" id="fake-tabs-title">Fake Tabs Heading</h2>
    <ul>
        <li>
            <a aria-current="page" href="http://www.ebay.com/1">Page 1</a>
        </li>
        <li>
            <a href="http://www.ebay.com/2">Page 2</a>
        </li>
        <li>
            <a href="http://www.ebay.com/3">Page 3</a>
        </li>
    </ul>
</nav>

Flyout

A flyout might open on click, focus or hover, on any kind of host button, input or link.

For correct reading order, and to minimize accessibility defects, the overlay element and its content should always immediately follow the host element in the DOM.

<div class="flyout">
    <button class="flyout__host" aria-expanded="false">Host Button</button>
    <div class="flyout__overlay">
        <!-- overlay content -->
    </div>
</div>

If the overlay element cannot be immediately adjacent to the button element, then an additional class will be required for styling purposes:

<div class="flyout flyout--expanded">
    <div>
        <button class="flyout__host" aria-expanded="true">Host Button</button>
    </div>
    <div class="flyout__overlay">
        <!-- overlay content -->
    </div>
</div>

Fullscreen Dialog

A fullscreen dialog takes over the entire screen.

Try to bucket the main page content in an element that is not an ancestor of the dialog. This will vastly simplify the amount of DOM manipulation when implementing modal behaviour.

<body>
    <div>
        <!-- main page content -->
    </div>
    <div class="fullscreen-dialog" role="dialog" aria-labelledby="dialog-title" aria-modal="true" hidden>
        <div class="fullscreen-dialog__window">
            <div id="dialog-title" class="fullscreen-dialog__header">
                <button aria-label="Close dialog" class="fullscreen-dialog__close" type="button">
                    <svg aria-hidden="true" focusable="false" height="16" width="16">
                        <use xlink:href="#icon-close"></use>
                    </svg>
                </button>
                <h2 class="large-text-1 bold-text">Fullscreen Dialog Title</h2>
            </div>
            <div class="fullscreen-dialog__main">
                <!-- dialog copy -->
            </div>
        </div>
    </div>
</body>

Note that Menu Button, Listbox Button, Tooltip & Combobox are special instances of flyouts, but follow the same general pattern and rules.

Icon Button

Using a hamburger menu as an example, for button behaviour:

<button class="icon-btn" type="button" aria-label="Menu">
    <svg class="icon icon--menu" focusable="false" width="16" height="16" aria-hidden="true">
        <use xlink:href="icons.svg#icon-menu"></use>
    </svg>
</button>

For link behaviour:

<a class="icon-link" href="http://www.ebay.com" aria-label="Menu">
    <svg class="icon icon--menu" focusable="false" width="16" height="16" aria-hidden="true">
        <use xlink:href="icons.svg#icon-menu"></use>
    </svg>
</a>

Infotip

An infotip is expanded and visible on click event of host element.

Infotip is a specific type of flyout. The markup becomes a little more convoluted due to the presence of a visual "pointer". This additional markup allows enough hooks for styling & masking with CSS.

<span class="infotip">
    <button class="infotip__host" type="button" aria-expanded="false" aria-label="Help">
        <svg focusable="false" width="16" height="16" aria-hidden="true">
            <use xlink:href="#icon-information-small"></use>
        </svg>
    </button>
    <div class="infotip__overlay">
        <span class="infotip__pointer infotip__pointer--bottom-center"></span>
        <div class="infotip__mask">
            <div class="infotip__cell">
                <span class="infotip__content">
                    <h3 class="infotip__heading">Infotip Title</h3>
                    <p>Infotip Copy</p>
                </span>
                <button class="infotip__close" type="button" aria-label="Dismiss infotip">
                    <svg focusable="false" height="24" width="24" aria-hidden="true">
                        <use xlink:href="#icon-close"></use>
                    </svg>
                </button>
            </div>
        </div>
    </div>
</span>

Inline-Notice

If the inline notice is rendered or updated on the client, wrap it within a live-region element.

<div class="inline-notice inline-notice--confirmation">
    <span class="inline-notice__header">
        <svg focusable="false" class="icon icon--confirmation-filled" height="16" width="16" role="img" aria-label="Confirmation">
            <use xlink:href="#icon-confirmation-filled"></use>
        </svg>
    </span>
    <span class="inline-notice__main">
        <p>Notice Copy</p>
    </span>
</div>

Input Dialog

A lightbox dialog with one or more form inputs.

Try to bucket the main page content in an element that is not an ancestor of the dialog. This will vastly simplify the amount of DOM manipulation when implementing modal behaviour.

<body>
    <div>
        <!-- main page content -->
    </div>
    <div class="input-dialog" role="dialog" aria-labelledby="input-dialog-title" aria-modal="true">
        <div class="input-dialog__window">
            <div class="input-dialog__header">
                <h2 class="input-dialog__title" id="input-dialog-title">Input Dialog Title</h2>
            </div>
            <div class="input-dialog__main">
                <label for="foo">Input Label</label>
                <input id="foo" type="text" name="foo" />
            </div>
            <div class="input-dialog__footer">
                <button class="input-dialog__cancel" type="button">Cancel</button>
                <button class="input-dialog__submit" type="button">Submit</button>
            </div>
        </div>
    </div>
</body>

Input Validation

Input validation messages rendered on the client are considered an enhancement to full, server-side form validation.

Valid State

The error message container is a live-region, and can be primed and ready in the server-side code, or injected dynamically at error-time with client-side JavaScript.

<div class="input-validation">
    <span>
        <label for="input1">Input 1</label>
        <input aria-describedby="input1-description" aria-invalid="false" id="input1" name="input1" type="text" />
    </span>
    <span aria-live="polite" class="input-validation__status" role="status">
        <span class="input-validation__description" id="input1-description" >
            <!-- this content should be empty in a valid state, populated in an invalid state -->
        </span>
    </span>
</div>

For Voiceover to correctly detect live-region updates, the updates must happen on direct-descendant(s) of the live-region, not on the live-region itself.

Invalid State

Changing the content of a directly-descendant element will trigger a live-region update.

<div class="input-validation">
    <span>
        <label for="input1">Input 1</label>
        <input aria-describedby="input1-description" aria-invalid="true" id="input1" name="input1" type="text" />
    </span>
    <span aria-live="polite" class="input-validation__status" role="status">
        <span class="input-validation__description" id="input1-description">
            <!-- this content should be empty in a valid state, populated in an invalid state -->
        </span>
    </span>
</div>

Notice that the aria-described attribute supplements the live-region, by using the same live-region content as a description for the input.

Lightbox Dialog

A lightbox dialog is always modal. Other types of lightbox dialog include: alert dialog, confirm dialog & input dialog.

Try to bucket the main page content in an element that is not an ancestor of the dialog. This will vastly simplify the amount of DOM manipulation when implementing modal behaviour.

<div class="lightbox-dialog" role="dialog" aria-labelledby="lightbox-dialog-title" aria-modal="true">
    <div class="lightbox-dialog__window">
        <div class="lightbox-dialog__header">
            <h2 class="lightbox-dialog__title" id="lightbox-dialog-title">Dialog Title</h2>
            <button aria-label="Close dialog" class="lightbox-dialog__close" type="button"></button>
        </div>
        <div class="lightbox-dialog__main">
            <!-- lightbox dialog content goes here -->
        </div>
    </div>
</div>

Listbox

The listbox pattern is intended as a JavaScript alternative to the multiselect state of the HTML select.

<span class="listbox">
    <div role="listbox" tabindex="0">
        <div class="listbox__option" role="option">
            <span>Option 1</span>
            <span class="listbox__status"></span>
        </div>
        <div class="listbox__option" role="option">
            <span>Option 2</span>
            <span class="listbox__status"></span>
        </div>
        <div class="listbox__option" role="option">
            <span>Option 3</span>
            <span class="listbox__status"></span>
        </div>
    </div>
</span>

When a single-select listbox receives focus for the first time, the first option should be selected if the user does not specify a selection.

An initial selection can be specified by applying the aria-selected state to one or more options.

Listbox Button

Opens a listbox via a host button.

<span class="listbox-button">
    <button class="listbox-button__button" aria-expanded="false" aria-haspopup="listbox">
        <span>
            <span>Option 1</span>
            <span class="listbox-button__icon-expand"></span>
        </span>
    </button>
    <div class="listbox-button__listbox" hidden>
        <div role="listbox" tabindex="0">
            <div class="listbox__option" role="option">
                <span>Option 1</span>
                <span class="listbox__status"></span>
            </div>
            <div class="listbox__option" role="option">
                <span>Option 2</span>
                <span class="listbox__status"></span>
            </div>
            <div class="listbox__option" role="option">
                <span>Option 3</span>
                <span class="listbox__status"></span>
            </div>
        </div>
    </div>
</span>

Menu

A menu contains one or more groups of commands (menuitem, menuitemradio, or menuitemcheckbox) that execute JavaScript.

Single Group

<div class="menu">
    <div role="menu">
        <div role="menuitem" tabindex="0">Button 1</div>
        <div role="menuitem" tabindex="-1">Button 2</div>
    </div>
</div>

Multiple Groups

<div class="menu">
    <div role="menu">
        <div role="presentation">
            <div role="menuitem" tabindex="0">Button 1</div>
            <div role="menuitem" tabindex="-1">Button 2</div>
        </div>
        <hr />
        <div role="presentation">
            <div aria-checked="true" role="menuitemradio" tabindex="-1">Radio Button 1</div>
            <div aria-checked="false" role="menuitemradio" tabindex="-1">Radio Button 2 </div>
            <div aria-checked="false" role="menuitemradio" tabindex="-1">Radio Button 3</div>
        </div>
        <hr />
        <div role="presentation">
            <div aria-checked="true" role="menuitemcheckbox" tabindex="-1">Checkbox 1</div>
            <div aria-checked="true" role="menuitemcheckbox"  tabindex="-1">Checkbox 2</div>
        </div>
    </div>
</div>

Menu Button

A menu button opens a menu in a flyout.

<div class="menu-button">
    <button aria-expanded="false" aria-haspopup="true" class="menu-button__button">
        <span>
            <span>Open Menu</span>
            <span class="menu-button__icon-expand"></span>
        </span>
    </button>
    <div class="menu-button__menu" hidden>
        <div role="menu">
            <div role="presentation">
                <div role="menuitem" tabindex="0">Button 1</div>
                <div role="menuitem" tabindex="-1">Button 2</div>
            </div>
            <hr />
            <div role="presentation">
                <div aria-checked="true" role="menuitemradio" tabindex="-1">Radio Button 1 (checked)</div>
                <div aria-checked="false" role="menuitemradio" tabindex="-1">Radio Button 2 </div>
                <div aria-checked="false" role="menuitemradio" tabindex="-1">Radio Button 3</div>
            </div>
            <hr />
            <div role="presentation">
                <div aria-checked="true" role="menuitemcheckbox" tabindex="-1">Checkbox 1 (checked)</div>
                <div aria-checked="true" role="menuitemcheckbox"  tabindex="-1">Checkbox 2 (checked)</div>
            </div>
        </div>
    </div>
</div>

Page Notice

Page notices are common and prominent visual landmarks on any website. A labelled landmark element (i.e. <section>) increases affordance for assistive technology.

A live-region ensure dynamic updates on the client are picked up by assistive technology. This live-region can be removed or set to "off" if the page notice is only ever rendered once on the server without any further client-side changes or updates.

<span aria-live="polite">
    <section class="page-notice page-notice--information" role="region" aria-label="Information">
        <div class="page-notice__header">
            <svg focusable="false" height="24" width="24" role="img" aria-label="Information">
                <use xlink:href="#icon-information"></use>
            </svg>
        </div>
        <div class="page-notice__main">
            <h2 class="page-notice__title">Title</h2>
            <p>Copy</p>
        </div>
        <div class="page-notice__footer">
            <a href="https://www.ebay.com" class="page-notice__cta">Call to action</a>
        </div>
    </section>
</span>

Pagination

Pagination is an important means of site navigation. A labelled landmark element (i.e. <nav>) increases affordance for assistive technology.

<nav class="pagination" aria-labelledby="pagination-heading" role="navigation">
    <span aria-live="off">
        <h2 id="pagination-heading" class="clipped">Results Pagination - Page 1</h2>
    </span>
    <a aria-disabled="true" aria-label="Previous Page" class="pagination__previous" href="1.html">
        <svg aria-hidden="true">
            <use xlink:href="#icon-chevron-light-left"></use>
        </svg>
    </a>
    <ol>
        <li>
            <a aria-current="page" href="1.html">1</a>
        </li>
        <li>
            <a href="2.html">2</a>
        </li>
        <li>
            <a href="3.html">3</a>
        </li>
    </ol>
    <a aria-label="Next Page" class="pagination__next" href="2.html">
        <svg aria-hidden="true">
            <use xlink:href="#icon-chevron-light-right"></use>
        </svg>
    </a>
</nav>

NOTE: The ARIA live-region need only be enabled (set from "off" to "polite") if client-side pagination is implemented.

Panel Dialog

A modal dialog that sits flush to one side of the screen.

Try to bucket the main page content in an element that is not an ancestor of the dialog. This will vastly simplify the amount of DOM manipulation when implementing modal behaviour.

<div aria-labelledby="dialog-title" aria-modal="true" class="panel-dialog" hidden id="dialog-left-panel-0" role="dialog">
    <div class="panel-dialog__window">
        <div class="panel-dialog__header">
            <h2 id="dialog-title" class="panel-dialog__title">Panel Dialog Title</h2>
            <button aria-label="Close dialog" class="icon-btn panel-dialog__close" type="button">
                <svg aria-hidden="true" class="icon icon--close" focusable="false" height="16" width="16">
                    <use xlink:href="#icon-close"></use>
                </svg>
            </button>
        </div>
        <div class="panel-dialog__main">
            <!-- content -->
        </div>
        <!-- optional -->
        <div class="panel-dialog__footer">
            <!-- content -->
        </div>
    </div>
</div>

Radio

Native HTML radio buttons are 100% accessible by default.

To ensure correct grouping semantics, radio buttons should be placed inside of a fieldset with legend.

<fieldset>
    <legend>Radio Group Title</legend>
    <span>
        <input id="radio-group1_input1" name="radio-group1" type="radio" value="1" checked />
        <label for="radio-group1_input1">Input 1</label>
    </span>
    <span>
        <input id="radio-group1_input2" name="radio-group1" type="radio" value="2" />
        <label for="radio-group1_input2">Input 2</label>
    </span>
    <span>
        <input id="radio-group1_input3" name="radio-group1" type="radio" value="3" />
        <label for="radio-group1_input3">Input 3</label>
    </span>
</fieldset>

For vertically stacked radios, simply switch the spans to divs.

Custom Radios

Foreground SVG can be used as a facade over the real radio.

<span class="radio" hidden>
    <input class="radio__control" id="radio-group1_input1" type="radio" value="3" name="radio-group-1" />
    <svg aria-hidden="true" class="radio__unchecked" focusable="false">
        <use xlink:href="#icon-radio-unchecked"></use>
    </svg>
    <svg aria-hidden="true" class="radio__checked" focusable="false">
        <use xlink:href="#icon-confirmation"></use>
    </svg>
</span>

The hidden property ensures that the SVG icon is not visible alongside the native icon when the page is in a non-CSS state. This hidden property should be overriden by CSS.

Select

A native HTML select is 100% accessible by default. The "button" can be styled with CSS; the "dropdown" not so much.

<span class="select">
    <select name="options">
        <option value="item1">Option 1</option>
        <option value="item2">Option 2</option>
        <option value="item3">Option 3</option>
    </select>
    <svg class="icon icon--dropdown" focusable="false" height="8" width="8" aria-hidden="true">
        <use xlink:href="#icon-dropdown"></use>
    </svg>
</span>

Switch

A switch is not a true form control. It typically executes JavaScript on the client when toggled (i.e. without a full page reload).

<span class="switch">
    <span class="switch__control" role="switch" tabindex="0" aria-checked="false"></span>
    <span class="switch__button"></span>
</span>

The following version of the switch uses a checkbox under the hood. It should be used if you require the switch to store data inside of a form. As mentioned above however, this is not the intended purpose of a switch. You may wish to consider using an actual checkbox instead.

<span class="switch">
    <input class="switch__control" role="switch" type="checkbox" aria-checked="false" />
    <span class="switch__button"></span>
</span>

Tabs

<div class="tabs" id="tabs-1">
    <h2 class="clipped">Tabs Heading</h2>
    <div class="tabs__items" role="tablist">
        <div class="tabs__item" role="tab" aria-controls="tabs-1-panel-1" aria-selected="true" id="tabs1_tab1" tabindex="0">
            <span>Tab 1</span>
        </div>
        <div class="tabs__item" role="tab" aria-controls="tabs-1-panel-2" aria-selected="false" id="tabs1_tab2" tabindex="-1">
            <span>Tab 2</span>
        </div>
        <div class="tabs__item" role="tab" aria-controls="tabs-1-panel-3" aria-selected="false" id="tabs1_tab3" tabindex="-1">
            <span>Tab 3</span>
        </div>
    </div>
    <div class="tabs__content">
        <div aria-labelledby="tabs1_tab1" class="tabs__panel" role="tabpanel" id="tabs-1-panel-1">
            <h3>Panel 1 heading</h3>
            <!-- panel content -->
        </div>
        <div aria-labelledby="tabs1_tab2" class="tabs__panel" role="tabpanel" hidden id="tabs-1-panel-2">
            <h3>Panel 2 heading</h3>
            <!-- panel content -->
        </div>
        <div aria-labelledby="tabs1_tab3" class="tabs__panel" role="tabpanel" hidden id="tabs-1-panel-3">
            <h3>Panel 3 heading</h3>
            <!-- panel content -->
        </div>
    </div>
</div>

Toast Dialog

Toast dialog is non-modal and should not steal or trap keyboard focus.

Try to position the dialog immediately after the currently focussed element (i.e. the document.activeElement).

<aside aria-label="Notification" aria-live="polite" aria-modal="false" class="toast-dialog" hidden role="dialog">
    <div class="toast-dialog__window">
        <div class="toast-dialog__header">
            <h2 class="toast-dialog__title">
                <!-- heading -->
            </h2>
            <button class="toast-dialog__close" type="button" aria-label="Close notification dialog">
                <svg focusable="false" height="24" width="24" aria-hidden="true">
                    <use xlink:href="#icon-close"></use>
                </svg>
            </button>
        </div>
        <div class="toast-dialog__main">
            <!-- content -->
        </div>
        <!-- optional footer with cta -->
        <div class="toast-dialog__footer">
            <a accesskey="a" class="toast-dialog__cta" href="http://www.ebay.com">Action</a>
        </div>
    </div>
</aside>

If using the optional footer with call-to-action link or button, the first letter of the CTA will define the accesskey property.

Tooltip

A tooltip is expanded and visible on hover and focus of host element.

Tooltip structure is almost identical to flyout structure. The markup becomes a little more convoluted due to the presence of a visual "pointer". This additional markup allows enough hooks for styling & masking with CSS.

<span class="tooltip">
    <button class="tooltip__host" aria-describedby="tooltip-0" aria-expanded="false" aria-label="Settings">
        <svg focusable="false" height="16" width="16" aria-hidden="true">
            <use xlink:href="#icon-settings"></use>
        </svg>
    </button>
    <div class="tooltip__overlay" id="tooltip-0" role="tooltip">
        <span class="tooltip__pointer"></span>
        <div class="tooltip__mask">
            <div class="tooltip__cell">
                <div class="tooltip__content">
                    <!-- content -->
                </div>
            </div>
        </div>
    </div>
</span>

Tourtip

A tourtip is expanded and visible on page load.

Tourtip structure is almost identical to flyout structure. The markup becomes a little more convoluted due to the presence of a visual "pointer". This additional markup allows enough hooks for styling & masking with CSS.

<div class="tourtip tourtip--expanded">
    <button class="tourtip__host" aria-label="Settings">
        <svg focusable="false" height="16" width="16" aria-hidden="true">
            <use xlink:href="#icon-settings"></use>
        </svg>
    </button>
    <div class="tourtip__overlay" role="region" aria-labelledby="tourtip-label">
        <span class="tourtip__pointer"></span>
        <div class="tourtip__mask">
            <div class="tourtip__cell">
                <span class="tourtip__content">
                    <h2 class="tourtip__heading" id="tourtip-label">Tourtip Title</h2>
                    <!-- content -->
                </span>
                <button class="tourtip__close" type="button" aria-label="Dismiss tip">
                    <svg focusable="false" height="24" width="24" aria-hidden="true">
                        <use xlink:href="#icon-close"></use>
                    </svg>
                </button>
            </div>
        </div>
    </div>
</div>
Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].