Our navigation component is a collection of pill-shaped buttons that respond gracefully to various window sizes and parent containers.

Class Applies to Description
.s-navigation N/A Base parent container for navigation
.s-navigation__vertical .s-navigation Renders the navigation vertically
.s-navigation__muted .s-navigation Secondary navigation style. To be used on pages that already have a primary navigation
.s-navigation__scroll .s-navigation When the navigation items overflow the width of the component, enable horizontal scrolling. By default, navigation items will wrap. This should not be applied to vertical navigations.
.s-navigation__sm .s-navigation Tightens up the overall spacing and reduces the text size
.s-navigation--item Child of .s-navigation The individual pill-shaped link in a navigation
.is-selected .s-navigation--item Applies to a navigation item that’s currently selected / active
.s-navigation--item__dropdown .s-navigation--item Adds a small caret that indicates a dropdown

Care should be taken to only include at most one primary and one secondary navigation per page. Using multiple navigations with the same style can cause user confusion.

Forcing a navigation to scroll is an established pattern on mobile devices, so it may be appropriate to use it in that context. Wrapping tends to make more sense on larger screens, where the user isn’t forced to scroll passed a ton of navigation chrome.

<nav aria-label="…">
<ul class="s-navigation">
<li><a href="…" class="s-navigation--item is-selected"></a></li>
<li><a href="…" class="s-navigation--item"></a></li>
<li><a href="…" class="s-navigation--item"></a></li>
<li><a href="…" class="s-navigation--item"></a></li>
<li><a href="…" class="s-navigation--item"></a></li>
</ul>
</nav>
<nav aria-label="…">
<ul class="s-navigation s-navigation__muted">
<li><a href="…" class="s-navigation--item is-selected"></a></li>
<li><a href="…" class="s-navigation--item"></a></li>
<li><a href="…" class="s-navigation--item"></a></li>
<li><a href="…" class="s-navigation--item"></a></li>
<li><a href="…" class="s-navigation--item"></a></li>
</ul>
</nav>
<nav aria-label="…">
<ul class="s-navigation s-navigation__scroll">
<li><a href="…" class="s-navigation--item is-selected"></a></li>
<li><a href="…" class="s-navigation--item"></a></li>
<li><a href="…" class="s-navigation--item"></a></li>
<li><a href="…" class="s-navigation--item"></a></li>
<li><a href="…" class="s-navigation--item"></a></li>
</ul>
</nav>
<nav aria-label="…">
<ul class="s-navigation s-navigation__scroll">
<li><a href="…" class="s-navigation--item is-selected"></a></li>
<li><a href="…" class="s-navigation--item"></a></li>
<li><a href="…" class="s-navigation--item s-navigation--item__dropdown"></a></li>
</ul>
</nav>
<nav aria-label="…">
<ul class="s-navigation s-navigation__sm">
<li><a href="…" class="s-navigation--item is-selected"></a></li>
<li><a href="…" class="s-navigation--item"></a></li>
<li><a href="…" class="s-navigation--item"></a></li>
<li><a href="…" class="s-navigation--item"></a></li>
<li><a href="…" class="s-navigation--item"></a></li>
</ul>
</nav>

Stacks also provides a vertical variation with support for section headers.

<nav aria-label="…">
<ul class="s-navigation s-navigation__vertical">
<li><a href="…" class="s-navigation--item is-selected"></a></li>
<li><a href="…" class="s-navigation--item"></a></li>
<li><a href="…" class="s-navigation--item"></a></li>
<li><a href="…" class="s-navigation--item"></a></li>
<li><a href="…" class="s-navigation--item"></a></li>
</ul>
</nav>
<nav aria-label="…">
<ul class="s-navigation s-navigation__vertical">
<li class="s-navigation--title"></li>
<li><a href="…" class="s-navigation--item is-selected"></a></li>
<li><a href="…" class="s-navigation--item"></a></li>

<li class="s-navigation--title"></li>
<li><a href="…" class="s-navigation--item"></a></li>
<li><a href="…" class="s-navigation--item"></a></li>
<li><a href="…" class="s-navigation--item"></a></li>
<li><a href="…" class="s-navigation--item"></a></li>
<li><a href="…" class="s-navigation--item"></a></li>
</ul>
</nav>

This component can be used for in-page navigation patterns, using button elements in place of link. When doing so, it may be appropriate to annotate elements with role="tablist" to indicate the dynamic behavior.

<nav aria-label="…">
<div role="tablist" class="s-navigation">
<button type="button" class="s-navigation--item is-selected" role="tab" aria-selected="true"></button>
<button type="button" class="s-navigation--item" role="tab" aria-selected="false"></button>
<button type="button" class="s-navigation--item" role="tab" aria-selected="false"></button>
</div>
</nav>

Stacks provides a Stimulus controller for interactive tab-style navigation within a single document.

Attribute Applied to Description
data-controller="s-navigation-tablist" .s-navigation Wires up the element to the tooltip controller.
role="tablist" .s-navigation Indicates to accessibility tools that the navigation element is a tab list.
role="tab" .s-navigation--item Indicates to accessibility tools that the navigation item is a tab. Used by the controller to automatically connect mouse and keyboard events.
class="s-navigation--item is-selected" Initially selected .s-navigation--item elements Provides visual indication that the tab is selected.
aria-selected="{true|false}" .s-navigation--item Indicates to accessibility tools which tab is selected. Used by the controller to track the selected tab.
id="{TAB_ID}" .s-navigation--item A unique id for the tab. Will be used by the tab panel to refer back to the button via aria-labelledby="{TAB_ID}".
aria-controls="{PANEL_ID}" .s-navigation--item A unique id for the panel element corresponding to the tab.
tabindex="-1" Initially unselected .s-navigation--item elements Indicates to the browser that the element should be excluded from tab navigation. When interacting with the control, arrow keys will be used to switch tabs.
role="tabpanel" Tab panel elements Indicates to accessibility tools that the element is a panel corresponding to a tab.
id="{PANEL_ID}" Tab panel elements A unique id for the panel. Used by the tab to identify the panel that will be shown when it is active, via aria-controls="{PANEL_ID}".
aria-labelledby="{TAB_ID}" Tab panel elements Refers to the id of the tab element that corresponds to the panel.
class="d-none" Initially hidden tab panel elements Suppressed display of tab panels.
Event Element Description
s-navigation-tablist:select Controller element Default preventable Fires immediately before a new tab is selected. Calling .preventDefault() cancels the display of the tab and keeps the state unchanged.
s-navigation-tablist:selected Controller element Fires immediately after the new tag has been selected.
event.detail Applicable events Description
oldTab s-navigation-tablist:* Contains HTMLElement | null representing the previously selected tab, if one was selected. This is the element with role="tab", not the panel itself which can be found using the tab's aria-controls attribute.
newTab s-navigation-tablist:* Contains HTMLElement representing the newly selected tab. This is the element with role="tab", not the panel itself which can be found using the tab's aria-controls attribute.
<nav aria-label="…">
<div class="s-navigation" role="tablist" data-controller="s-navigation-tablist">
<button type="button"
role="tab"
id="tab-question"
aria-selected="true"
aria-controls="panel-question"
class="s-navigation--item is-selected">
Task</button>
<button type="button"
role="tab"
id="tab-answers"
aria-selected="false"
aria-controls="panel-answers"
tabindex="-1"
class="s-navigation--item">
Answers</button>
</div>
</nav>

<div id="panel-question" aria-labelledby="tab-question">

</div>

<div id="panel-answers" aria-labelledby="tab-answers" class="d-none">

</div>
Review the following question

Answers

Potential Duplicate A

Potential Duplicate B

Deploys by Netlify