Bones
Accessible HTML code patterns for common UI widgets such as tabs, menus, dialogs, etc.
Install / Use
/learn @ianmcburnie/BonesREADME
BONES
Widgets are stronger with bones. Does your widget have bones?
Contents
- Introduction
- Accordion
- Alert Dialog
- Breadcrumbs
- Carousel
- Checkbox
- Combobox
- Confirm Dialog
- Details
- Fake Menu
- Fake Tabs
- Icon Button
- Infotip
- Inline Notice
- Input Dialog
- Input Validation
- Lightbox Dialog
- Listbox
- Listbox Button
- Menu
- Menu Button
- Page Notice
- Pagination
- Panel Dialog
- Popover
- Radio
- Select
- Switch
- Tabs
- Toast Dialog
- Tooltip
- 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>
Related Skills
node-connect
344.4kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
99.2kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
344.4kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
344.4kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
Security Score
Audited on Feb 4, 2026
