Skip to content

Accordions

The accordion consists of a number of collapsed sections, each with a button to expand that section's content

Version:
0.1.0
Status:
Published

Introduction

Accordions serves a similar purpose to Tabs: they collapse content into an interactive overview of itself.

Arguably, the interaction paradigm of the accordion is simpler than a standard tab interface[1] since it does not rely on either programmatic focus management or arrow key navigation. Instead, each button precedes each closed 'drawer' in the source. Keyboard and screen reader browsing is a case of simply pressing the button (or 'handle') and moving directly into the revealed content (the open 'drawer').

Accordions are also a better solution in terms of responsive design: their visual structure is not dependent on elements ('tabs') appearing in a horizontal line. Some tab interfaces are designed to collapse into an accordion-like visual structure, with one tab per line. This is problematic since it makes the tab interface appear, but not behave, like an accordion.

The Reference implementation is designed to accept different types of content and enhance it into an accordion. Depending on the context and quantity of your content, the semantics of the markup should differ.

For example, content that represents the major sections of a page's content might be marked up with <section>s and headings:

<h1>Main heading of page</h1>
<section>
	<h2>Section 1</h2>
	<p>Some text...</p>
	<img src="http://www.example.com/path/to/image" alt="description">
	<p>Some more text...</p>
</section>
<section>
	<h2>Section 2</h2>
	<p>Some text here...</p>
	<p>Additional text...</p>
</section>
<section>
	<h2>Section 3</h2>
	<img src="http://www.example.com/path/to/image" alt="another description">
	<blockquote>A quotation from somewhere</blockquote>
</section>

Wrapping this set of <section>s in a class="gel-accordion" element produces the following enhanced markup when the JavaScript runs:

<h1>Main heading of page</h1>
<div class="gel-accordion">
	<section>
		<h2 class="gel-accordion__handle">
			<button aria-expanded="false" type="button">
				<span>Section 1</span>
				<svg viewBox="0 0 32 32" class="gel-icon gel-icon--text" focusable="false" aria-hidden="true">
				<path d="M16 29L32 3h-7.2L16 18.3 7.2 3H0"></path></svg>
			</button>
		</h2>
		<div class="gel-accordion__drawer" hidden>
			<p>Some text...</p><img src="http://www.example.com/path/to/image" alt="description">
			<p>Some more text...</p>
		</div>
	</section>
	<section>
		<h2 class="gel-accordion__handle">
			<button aria-expanded="false" type="button">
				<span>Section 2</span>
				<svg viewBox="0 0 32 32" class="gel-icon gel-icon--text" focusable="false" aria-hidden="true">
				<path d="M16 29L32 3h-7.2L16 18.3 7.2 3H0"></path></svg>
			</button>
		</h2>
		<div class="gel-accordion__drawer" hidden>
			<p>Some text here...</p>
			<p>Additional text...</p>
		</div>
	</section>
	<section>
		<h2 class="gel-accordion__handle">
			<button aria-expanded="false" type="button">
				<span>Section 3</span>
				<svg viewBox="0 0 32 32" class="gel-icon gel-icon--text" focusable="false" aria-hidden="true">
				<path d="M16 29L32 3h-7.2L16 18.3 7.2 3H0"></path></svg>
			</button>
		</h2>
		<div class="gel-accordion__drawer" hidden>
			<img src="http://www.example.com/path/to/image" alt="another description">
			<blockquote>A quotation from somewhere</blockquote>
		</div>
	</section>
</div>
  • class="gel-accordion__drawer" and hidden: All the content except the 'handle' is grouped inside this element so its visibility can be toggled easily. The drawer is hidden by default.
  • <button> and aria-expanded: The visibility of the drawer (see above) is affected by the toggle button. The aria-expanded state is set to either false (drawer closed; default on page load) or true (drawer open)
  • SVG: An SVG based on the GEL Iconography gel-icon-down. This has focusable="false" to remove it from focus order, and aria-hidden="true" to hide it from browsers/assistive technologies that erroneously expose it.

Short form content

More concise content, such as questions with one or two sentence answers, would be better marked up as a list. The <ul> would take the gel-accordion class.

<ul class="gel-accordion">
	<li>
		<p><strong>Question 1<strong></p>
		<p>Answer 1</p>
	</li>
	<li>
		<p><strong>Question 2<strong></p>
		<p>Answer 2</p>
	</li>
	<li>
		<p><strong>Question 3<strong></p>
		<p>Answer 3</p>
	</li>
</ul>

Whether section and heading or list semantics are suitable, there are certain structural rules for the progressive enhancement to take place successfully using the Reference implementation script:

  • Accordion items must be wrapped in a common gel-accordion element
  • Each item must have at least two elements
  • The first element must not be a <button> (since its own contents will become wrapped in a <button>)
if (section.handle.nodeName === 'BUTTON') {
	console.error('The first child of each accordion element must not be a <button>');
	return;
}

The visual design of a generic accordion is specified in the GEL Accordion documentation.

The gel-icon-down arrow must point downwards in the closed (aria-expanded="false") state, and upwards in the open (aria-expanded="true") state. For the sake of code brevity, this is achieved in the Reference implementation using a simple CSS transform.

.gel-accordion__handle [aria-expanded="true"] svg {
  transform: rotate(180deg);
}

The text appears on the left of the button and the SVG on the right (thanks to justify-content: space-between). Some margin is added to the left of the SVG to separate it from the button's text. The flex-shrink: 0 declaration prevents the SVG from erroneously becoming proportionately narrower when the total space is diminished.

.gel-accordion__handle button {
	display: flex;
	justify-content: space-between;
	align-items: center;
}

.gel-accordion__handle svg {
  margin-left: 0.5rem;
  flex-shrink: 0;
}

High contrast

How the component looks with a Windows High Contrast Mode theme active:

Borders help demarcate the accordion where high contrast mode is running

An accordion designed with progressive enhancement offers pre-rendered structured content, expanded and available by default where JavaScript is not available. The Reference implementation wraps and hides each accordion item's content, and provides a button to each item for toggling the expanded/collapsed state.

The virtue of accordion interaction is in its simplicity. As someone browses the page, instead of encountering lots of content through which they have to scroll, they encounter buttons to reveal content. If a button's label piques their interest, they can press the handle/button to reveal the content (secreted in a 'drawer' below that button).

Each button simply toggles the display (and availability to assistive technologies) of its associated content (the 'drawer'). The toggle button's state is communicated to screen readers with aria-expanded[3]. This is how it is implemented in the Reference implementation:

// The open method
self.constructor.prototype.open = function (section) {
	section.button.setAttribute('aria-expanded', 'true');
	section.drawer.hidden = false;
}

// The close method
self.constructor.prototype.close = function (section) {
	section.button.setAttribute('aria-expanded', 'false');
	section.drawer.hidden = true;
}

Each accordion item's handle immediately precedes its drawer. In the open/expanded state this makes it trivial and intuitive for the user to 'reach' the drawer's content. Some implementations include aria-controls to reference the controlled (drawer) element. Thanks to the proximate source order this is not necessary. Be aware that aria-controls is only supported in JAWS at the time of writing[4].

The reference implementation includes two further methods: openAll and closeAll. These allow you to implement buttons for opening or closing every accordion item simultaneously.

// Support for opening all the drawers at once
var openAllButton = document.getElementById('openAll');
openAllButton.addEventListener('click', function () {
	accordion.openAll();
});

// Support for closing all the drawers at once
var closeAllButton = document.getElementById('closeAll');
closeAllButton.addEventListener('click', function () {
	accordion.closeAll();
});

The closeAll functionality can be particularly useful for people who have opened a number of items one after the other, and are subsequently finding it easy to get lost among the expanded content.

Page fragments

Sometimes it's beneficial to identify page sections with ids, so that links to those specific page fragments can be shared. The following implementation ensures that JavaScript enhancement does not break this basic browser behaviour.

On both the DOMContentLoaded and hashchange events, if an accordion item—or any element inside an accordion item—has the id that matches the page's hash, that item is entered into its open state and it (or the matching element it contains) is focused.

this.hashHandle = function () {
	var id = window.location.hash.substring(1);
	var target = document.getElementById(id);
	this.sections.forEach(function (section) {
		if (section.elem.contains(target)) {
			this.open(section);
			target.tabIndex = -1;
			target.focus();
		}
	}.bind(this));
}

Reference implementation

How can I purchase BBC DVDs/Merchandise?

It is no longer possible to buy DVDs, Blu-ray discs, games, toys or other BBC related products direct from the BBC in the UK.

We will continue to publish a range of DVDs, Blu-ray discs and other merchandising which can be bought from a wide range of retailers both online, on the high street as well as in supermarkets.

If you are a student/journalist/member of the public and wish to view an archive TV programme, please contact the BFI’s National Archive.

BBC Studios YouTube enquiries/feedback

Enquiries and feedback about our YouTube channels can be emailed to the team at: BBCStudios.YouTube.Channels@bbc.com

Where can I buy mugs, games, toys, and other merchandise?

BBC Studios works with external organisations to create a range of great BBC merchandise of BBC favourites such as Doctor Who and CBeebies, You can buy these products from many high street and online retailers as well as supermarkets.

DVDs for educational or training purposes?

It is sometimes possible to arrange for legitimate educational establishments to obtain a copy of a programme. For more information, please contact BBC Studios Learning

Can I get a copy of a BBC programme?

If you are interested in purchasing a copy of a programme or series you have watched on our channels, please check commercial availability via online or local retailers. Please note that not all programmes are commercially available.

For a number of reasons related to rights and resources, the BBC is unable to loan out programme material to members of the public and it does not hold a stock of review copies to give away.

If you are a student/journalist/member of the public and wish to view an archive TV programme, please contact the BFI’s National Archive.

If you, or your company, organisation or property has been featured in a BBC programme, you may be able to purchase a copy. If you would like to request a copy please fill out the form on the BBC website.

Open in new window

This topic does not yet have any related research available.

Further reading, elsewhere on the Web


  1. Tabs — ARIA Authoring Practices, https://www.w3.org/TR/wai-aria-practices-1.1/#tabpanel ↩︎

  2. The Second Rule Of ARIA Use — W3C, https://www.w3.org/TR/using-aria/#second ↩︎

  3. aria-expanded (state) — W3C, https://www.w3.org/TR/wai-aria-1.0/states_and_properties#aria-expanded ↩︎

  4. aria-controls Is Poop — heydonworks.com, http://www.heydonworks.com/article/aria-controls-is-poop ↩︎

  5. Give Control — Inclusive Design Principles, https://inclusivedesignprinciples.org/#give-control ↩︎

Copyright © 2021 BBC. This content is published under the Open Government Licence, unless otherwise noted.