Expandable section with view/hide allStatusdevelopment
An example showing how to view or hide all sections of an accordion at the same time.
Guidance
This pattern is very similar to the expandable section pattern, but with the addition of a button to show and hide all of the sections at once. This is a useful shortcut for keyboard and screen reader users and follows a pattern currently in use in the GOV.UK design system.
Example
Dependencies and installation
Package | Installation |
---|---|
@stormid/toggle |
|
Code
<div class="expand-all js-expand-all"><button type="button" class="expandable-section__btn-all js-expandable-section__btn-all" aria-expanded="false"><span class="expandable-section__btn-all-view">View all <span class="visually-hidden">sections about Lorem Ipsum</span></span><span class="expandable-section__btn-all-hide">Hide all <span class="visually-hidden">sections about Lorem Ipsum</span></span></button><div class="expandable-section"><h2 class="expandable-section__heading"><button type="button" class="expandable-section__btn js-expandable-section__btn-1" aria-expanded="false" aria-controls="section-1">Section title</button></h2><div id="section-1" class="expandable-section__bd js-expandable-section-all" data-toggle="js-expandable-section__btn-1"><p>Aenean id posuere nunc. Donec diam nisl, rhoncus vel faucibus sed, porttitor sit amet ipsum. Donec at venenatis augue. Phasellus consequat lectus non augue vestibulum, at varius diam sagittis. Morbi nec purus augue. Etiam rutrum ullamcorper arcu vitae sollicitudin. Aliquam bibendum suscipit risus, at lacinia tortor efficitur ac.</p><p>Praesent vitae mi nec mauris vehicula ultricies nec ut felis. Nam vel nisi id nunc efficitur fermentum. Vestibulum mollis enim nec ultrices mattis. Curabitur placerat sed nisl lobortis iaculis. Fusce porttitor massa augue, sit amet faucibus enim finibus nec. Maecenas hendrerit metus in justo commodo viverra.</p></div></div><div class="expandable-section"><h2 class="expandable-section__heading"><button type="button" class="expandable-section__btn js-expandable-section__btn-2" aria-expanded="false" aria-controls="section-2">Section title</button></h2><div id="section-2" class="expandable-section__bd js-expandable-section-all" data-toggle="js-expandable-section__btn-2"><p>Aenean id posuere nunc. Donec diam nisl, rhoncus vel faucibus sed, porttitor sit amet ipsum. Donec at venenatis augue. Phasellus consequat lectus non augue vestibulum, at varius diam sagittis. Morbi nec purus augue. Etiam rutrum ullamcorper arcu vitae sollicitudin. Aliquam bibendum suscipit risus, at lacinia tortor efficitur ac.</p><p>Praesent vitae mi nec mauris vehicula ultricies nec ut felis. Nam vel nisi id nunc efficitur fermentum. Vestibulum mollis enim nec ultrices mattis. Curabitur placerat sed nisl lobortis iaculis. Fusce porttitor massa augue, sit amet faucibus enim finibus nec. Maecenas hendrerit metus in justo commodo viverra.</p></div></div><div class="expandable-section"><h2 class="expandable-section__heading"><button type="button" class="expandable-section__btn js-expandable-section__btn-3" aria-expanded="false" aria-controls="section-3">Section title</button></h2><div id="section-3" class="expandable-section__bd js-expandable-section-all" data-toggle="js-expandable-section__btn-3"><p>Aenean id posuere nunc. Donec diam nisl, rhoncus vel faucibus sed, porttitor sit amet ipsum. Donec at venenatis augue. Phasellus consequat lectus non augue vestibulum, at varius diam sagittis. Morbi nec purus augue. Etiam rutrum ullamcorper arcu vitae sollicitudin. Aliquam bibendum suscipit risus, at lacinia tortor efficitur ac.</p><p>Praesent vitae mi nec mauris vehicula ultricies nec ut felis. Nam vel nisi id nunc efficitur fermentum. Vestibulum mollis enim nec ultrices mattis. Curabitur placerat sed nisl lobortis iaculis. Fusce porttitor massa augue, sit amet faucibus enim finibus nec. Maecenas hendrerit metus in justo commodo viverra.</p></div></div><div class="expandable-section"><h2 class="expandable-section__heading"><button type="button" class="expandable-section__btn js-expandable-section__btn-4" aria-expanded="false" aria-controls="section-4">Section title</button></h2><div id="section-4" class="expandable-section__bd js-expandable-section-all" data-toggle="js-expandable-section__btn-4"><p>Aenean id posuere nunc. Donec diam nisl, rhoncus vel faucibus sed, porttitor sit amet ipsum. Donec at venenatis augue. Phasellus consequat lectus non augue vestibulum, at varius diam sagittis. Morbi nec purus augue. Etiam rutrum ullamcorper arcu vitae sollicitudin. Aliquam bibendum suscipit risus, at lacinia tortor efficitur ac.</p><p>Praesent vitae mi nec mauris vehicula ultricies nec ut felis. Nam vel nisi id nunc efficitur fermentum. Vestibulum mollis enim nec ultrices mattis. Curabitur placerat sed nisl lobortis iaculis. Fusce porttitor massa augue, sit amet faucibus enim finibus nec. Maecenas hendrerit metus in justo commodo viverra.</p></div></div></div>
import toggle from '@stormid/toggle';
const SELECTORS = {
NODE: '.js-expand-all',
TOGGLES: '.js-expandable-section-all',
BTN: '.js-expandable-section__btn-all'
}
const CLASSES = {
OPEN: 'is--open'
}
const checkAllToggles = (toggles) => {
return toggles.reduce((acc, tog) =>{
return acc && tog.getState().isOpen;
}, true);
}
const updateVewButton = (btns, allOpen) => {
btns.forEach((btn) => {
btn.setAttribute('aria-expanded', allOpen);
btn.classList.toggle(CLASSES.OPEN);
})
}
export const init = () => {
const nodes = Array.from(document.querySelectorAll(SELECTORS.NODE));
const initialised = []
nodes.forEach((node) => {
if(!node.querySelector(SELECTORS.TOGGLES)) return;
const state = {
allOpen: false,
toggles: toggle(Array.from(node.querySelectorAll(SELECTORS.TOGGLES)), {
focus: false,
local: true,
callback: () => {
const newOpenState = checkAllToggles(state.toggles);
if(newOpenState !== state.allOpen) {
updateVewButton(state.btns, newOpenState);
state.allOpen = newOpenState;
}
} }),
btns: Array.from(node.querySelectorAll(SELECTORS.BTN))
}
if(state.toggles.length) {
state.btns.forEach((btn) => {
btn.addEventListener('click', () => {
state.allOpen = !state.allOpen;
updateVewButton(state.btns, state.allOpen);
state.toggles.forEach((tog) => {
if(tog.getState().isOpen !== state.allOpen) tog.toggle();
})
})
})
};
initialised.push({
node: node,
getState: () => {
return state;
}
})
})
return initialised;
};
init();
Acceptance criteria
The following is a list of example acceptance criteria to test against when using this pattern. These critera should test that the specific markup requirements are met, and that the expanding section behaves visually and functionally as expected.
For validation in developer tools / web inspector
- An HTML
<button>
element is used to expand/collapse the sections - An
aria-expanded
attribute should be present on all toggle<button>
elements, including View/Hide all. The value of this should be 'true' when the section(s) are expanded, and false when a section is collapsed. - Individual toggle
<button>
elements should have anaria-controls
attribute. The value of this should match the ID of the section being expanded/collapsed. This does not apply to the View/hide all button. - The View/Hide button have accessible text, either through an aria label or screenreader only content
For visual validation
- All toggle buttons should have a clearly visible focus style which meets accessibility contrast requirements
- All toggle buttons should be no less than 44px x 44px in size (unless any of the allowed WCAG exceptions apply)
- Section toggle buttons should be appropriately labelled to describe their functionality. If the design requires no visible text, a label should be added as an
aria-label
attribute on the<button>
element - Each collapsed section should be hidden visually, hidden from keyboard access, and not read by screen readers
- Each expanded section should be visible, available for keyboard access, and read by screen readers
- The text of the View/Hide button should update to show the current state of the accordion once used.
For functional validation
- Activating the view/hide all button should expand or collapse all of the associated sections.
- All toggle buttons should be available to be tabbed to and activated via keyboard
- If all section toggles are opened manually, the 'View all' button should change to a 'Hide all' message
References
- https://www.w3.org/TR/wai-aria-practices-1.1/#disclosure
- https://www.w3.org/TR/wai-aria-practices-1.1/examples/disclosure/disclosure-faq.html
- https://inclusive-components.design/collapsible-sections/
- https://webaim.org/standards/wcag/checklist
- https://adrianroselli.com/2019/04/details-summary-are-not-insert-control-here.html
- https://www.scottohara.me//blog/2022/09/12/details-summary.html
- https://design-system.service.gov.uk/components/accordion/