Bugcrowd design system: Navigation

This version of the documentation is locked from receiving updates. We’re improving it as part of a wider site restructure. Please check back soon.

A encapsulation of links to other pages or parts within a page.

When building navigation sections, consider using the HTML5 <nav> element.

Guidance last updated: Sep 4, 2023

The main menu for the primary sections or views of the site or application.

Use to allow users to navigate between main sections of the site or app.

Navbar positioning

The navbar is built to sit inside the Header container, which is given a fixed position.

React reference implementations

There are two reference implementations — one for Portal and the other for Tracker.

These are available as React components:

These share the same Navbar component.

Menu contents

The menu contains:

  • The Bugcrowd ‘hex’ logo as a link to home
  • A primary navigation, broken up into:
    • PrimaryMenuMobile, as a dropdown menu accessible from a hamburger menu panel button, hidden at ≥md breakpoint
    • A copy of the menu, displayed only ≥md breakpoints
  • A user menu grouping, including (if signed-in):
    • An icon (bell) link to the user’s notifications + unread marker (Tracker only), which will also display the unread marker if it is passed the string ‘1000+’
    • The user’s main dropdown menu (UserMenu), as button holding their avatar

Active/current page or section handling

A LinkItem receives a modifier class if the user is currently on the page for said item. This also applies if the user is deeper within a given top-level section.

You can override/supplement this functionality by setting your own activePathMatcher using regular expressions.

For an example, see the ‘Researchers’ primary menu item defined in NavbarTrackerExample.

Navbar dependencies

  • Containers:
    • Grid
    • Header
    • Panels
  • Components:
    • Avatars
    • Buttons
    • Link lists
    • Managed
  • Patterns:
    • Dropdown menu panel
Guidance last updated: Sep 4, 2023

The secondary menu for sub-sections within a primary navigation section.

Usage

Do not use as a primary navigation.

You can wrap the subnav in a panel. Use the --lined panel variant to retain the bottom border.

Rendered example of Subnav


The following example shows the subnav used within a panel, sans the fluid container:

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod

tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

Haml markup example of Subnav

%nav.container-fluid.bc-subnav
  .container-fluid__container
    %ul.bc-subnav__list
      %li.bc-subnav__item.bc-subnav__item--active
        %a{href: "#"}
          Brief & scope
      %li.bc-subnav__item
        %a{href: "#"}
          Known issues
      %li.bc-subnav__item
        %a{href: "#"}
          Integrations
      %li.bc-subnav__item
        %a{href: "#"}
          Manage team
      %li.bc-subnav__item
        %a{href: "#"}
          Additional fields

%br

%p
  The following example shows the subnav used within a panel, sans the fluid
  container:

.bc-panel.bc-panel--lined
  %nav.bc-subnav
    .bc-panel__header
      %ul.bc-subnav__list
        %li.bc-subnav__item.bc-subnav__item--active
          %a{href: "#"}
            Brief & scope
        %li.bc-subnav__item
          %a{href: "#"}
            Known issues
        %li.bc-subnav__item
          %a{href: "#"}
            Integrations
        %li.bc-subnav__item
          %a{href: "#"}
            Manage team
        %li.bc-subnav__item
          %a{href: "#"}
            Additional fields
  .bc-panel__main
    %p Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
    tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
    quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
    consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
    cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat
    non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Guidance last updated: Sep 4, 2023

A navigational aid which displays the location a user is on within a website’s structure.

Breadcrumb items

The endpoints linked and their order should reflect the hierarchy of the website. That is, the current page a user is on should be the child of a page it was navigable from.

Do not compose breadcrumbs dynamically from a user’s browsing/path history.

Do not make the final breadcrumb — the current page — an anchor. This is to avoid confusion given it would link to the page the user is already on. An alternative would be to use an anchor in conjunction with aria-current="page", however this may still cause confusion.

Do not use breadcrumbs on the top-level page where there would only be the one item, eg the homepage.

Breadcrumb positioning

Breadcrumbs are best positioned within the page’s primary <header> element, when possible.

Breadcrumbs should sit between the primary navigation and the page title.

Visually, breadcrumbs should remain in proximity to primary navigation elements and the page title.

Breadcrumb markup

The breadcrumb container class should be applied on a <nav>, wrapping the list and its items.

Provide the <nav> with an aria-label (eg “Breadcrumb navigation”).

Use ordered lists (<ol>).

Use the --compact and --inline List component for the list styles.

Rendered example of Breadcrumbs

Haml markup example of Breadcrumbs

%nav.bc-breadcrumbs{'aria-label': 'Breadcrumb navigation'}
  %ol.bc-list.bc-list--inline.bc-list--compact
    %li
      %a{href: '#'}
        Home
    %li
      %a{href: '#'}
        Parent
    %li
      Current page

Stepped nav

Guidance last updated: Sep 4, 2023

A linear navigation component used for ordered steps.

List spacing

Use --compact or --spacious accordingly to space lists. See List for more information.

Stepped nav __step variants

  • Use --complete class for completed steps.
  • Use --current class for the selected step.

    Stepped nav anchor accessibility

  • Use aria-current="step" attribute for the selected step/page.

Handling disabled nav anchor steps

If steps are required to be completed in order and should not be skipped, implementers should disable anchors by setting:

  • href value to null
  • aria-disabled="true".

    Accordion content for each step

The accordion toggle button is designed to allow the user to expand nested accordions without navigating to each step/page.

When controlling accordion content with the button:

  • Use aria-controls attribute to link with accordion content
  • Set aria-expanded attribute to true when the accordion is visible, or false when hidden.

When displaying accordion content:

  • Set aria-hidden attribute to false to display accordion content, or set to false to hide content.
  • Assign a unique id to link accordion content with the button.

Rendered example of Stepped nav

How to make pancakes
  1. Crack egg into mixing bowl
  2. Add flour and milk
  3. Whisk everything together
  4. Heat oil in frypan
    • Use medium-high heat
    • Add a test pancake for the right temperature
  5. Ladle batter in a circular shape
  6. Cook and flip pancake
  7. Serve immediately

Haml markup example of Stepped nav

.bc-panel.bc-panel--lined.bc-panel--shadow
  .bc-panel__header
    %h5.bc-panel__title
      Set up your Bugcrowd account
  .bc-panel__main
    %ol.bc-stepped-nav.bc-list.bc-list--numbered
      %li.bc-stepped-nav__step.bc-stepped-nav__step--complete
        %a.bc-stepped-nav__link{'href':'#'}
          Add your email address
      %li.bc-stepped-nav__step.bc-stepped-nav__step--complete
        %a.bc-stepped-nav__link{'href':'#'}
          Choose a username
      %li.bc-stepped-nav__step.bc-stepped-nav__step--complete
        %a.bc-stepped-nav__link{'href':'#'}
          Upload an avatar image
      %li.bc-stepped-nav__step.bc-stepped-nav__step--complete
        %a.bc-stepped-nav__link{'href':'#'}
          Secure your account with 2FA
      %li.bc-stepped-nav__step.bc-stepped-nav__step--current
        %a.bc-stepped-nav__link{'href':'#', 'aria-current':'step'}
          Select a program to hack on
      %li.bc-stepped-nav__step.bc-stepped-nav__step--disabled
        %a.bc-stepped-nav__link{'aria-disabled':'true'}
          Start hacking
      %li.bc-stepped-nav__step.bc-stepped-nav__step--disabled
        %a.bc-stepped-nav__link{'aria-disabled':'true'}
          Submit your first vulnerability
      %li.bc-stepped-nav__step.bc-stepped-nav__step--disabled
        %a.bc-stepped-nav__link{'aria-disabled':'true'}
          Wait for your vulnerability to be triaged
      %li.bc-stepped-nav__step.bc-stepped-nav__step--disabled
        %a.bc-stepped-nav__link{'aria-disabled':'true'}
          Receive your first reward
      %li.bc-stepped-nav__step.bc-stepped-nav__step--disabled
        %a.bc-stepped-nav__link{'aria-disabled':'true'}
          Keep hacking!


.bc-panel.bc-panel--lined.bc-panel--shadow
  .bc-panel__header
    %h5.bc-panel__title
      How to make pancakes
  .bc-panel__main
    %ol.bc-stepped-nav.bc-list.bc-list--numbered
      %li.bc-stepped-nav__step.bc-stepped-nav__step--complete
        %a.bc-stepped-nav__link{'href':'#'}
          Crack egg into mixing bowl
        %button.bc-stepped-nav__button{'aria-controls':'accordion-content-1', 'aria-expanded':'false'}
          %span Expand for more
        %ul.bc-list.bc-list--compact.bc-stepped-nav__accordion-content{'aria-hidden':'true', 'id':'accordion-content-1'}
          %li
            Get a large enough bowl to mix
          %li
            Use a whisk to stir
      %li.bc-stepped-nav__step.bc-stepped-nav__step--complete
        %a.bc-stepped-nav__link{'href':'#'}
          Add flour and milk
        %button.bc-stepped-nav__button{'aria-controls':'accordion-content-2', 'aria-expanded':'false'}
          %span Expand for more
        %ul.bc-list.bc-list--compact.bc-stepped-nav__accordion-content{'aria-hidden':'true', 'id':'accordion-content-2'}
          %li
            Add flour slowly while mixing
      %li.bc-stepped-nav__step.bc-stepped-nav__step--complete
        %a.bc-stepped-nav__link{'href':'#'}
          Whisk everything together
        %button.bc-stepped-nav__button{'aria-controls':'accordion-content-3', 'aria-expanded':'false'}
          %span Expand for more
        %ul.bc-list.bc-list--compact.bc-stepped-nav__accordion-content{'aria-hidden':'true', 'id':'accordion-content-3'}
          %li
            Turn bowl on a slight angle to mix
          %li
            Ensure you get a thick consistency
      %li.bc-stepped-nav__step
        %a.bc-stepped-nav__link{'href':'#'}
          Heat oil in frypan
        %button.bc-stepped-nav__button{'aria-controls':'accordion-content-4', 'aria-expanded':'true'}
          %span Expand for more
        %ul.bc-list.bc-list--compact.bc-stepped-nav__accordion-content{'aria-hidden':'false', 'id':'accordion-content-4'}
          %li
            Use medium-high heat
          %li
            Add a test pancake for the right temperature
      %li.bc-stepped-nav__step.bc-stepped-nav__step--current
        %a.bc-stepped-nav__link{'href':'#', 'aria-current':'step'}
          Ladle batter in a circular shape
        %button.bc-stepped-nav__button{'aria-controls':'accordion-content-5', 'aria-expanded':'false'}
          %span Expand for more
        %ul.bc-list.bc-list--compact.bc-stepped-nav__accordion-content{'aria-hidden':'true', 'id':'accordion-content-5'}
          %li
            Use a icecream scoop for even sizes
          %li
            Avoid touching or moving the pancake while it cooks
      %li.bc-stepped-nav__step
        %a.bc-stepped-nav__link{'href':'#'}
          Cook and flip pancake
        %button.bc-stepped-nav__button{'aria-controls':'accordion-content-6', 'aria-expanded':'false'}
          %span Expand for more
        %ul.bc-list.bc-list--compact.bc-stepped-nav__accordion-content{'aria-hidden':'true', 'id':'accordion-content-6'}
          %li
            Use a turner or try your skills by flicking your wrist
          %li
            Turn over when you see bubbles appear
      %li.bc-stepped-nav__step
        %a.bc-stepped-nav__link{'href':'#'}
          Serve immediately
        %button.bc-stepped-nav__button{'aria-controls':'accordion-content-7', 'aria-expanded':'false'}
          %span Expand for more
        %ul.bc-list.bc-list--compact.bc-stepped-nav__accordion-content{'aria-hidden':'true', 'id':'accordion-content-7'}
          %li
            Keep them warm in the oven until they are ready to serve
          %li
            Cook panckes at the same time with a large frypan

Checklist assistant

Guidance last updated: Sep 4, 2023

A top-level helper to that gives the user a checklist of actions or tasks.

Use to compose high-level todo lists, such as important tasks for new users.

Avoid using this deeper within a process flow; present this to users in dashboards or top-level indexes.

Checklist assistant item complete variant

Use the --complete item variant for complete[d] checklist items.

Implementers must disable completed anchors by setting their:

  • href value to null
  • aria-disabled="true".

Rendered example of Checklist assistant

Haml markup example of Checklist assistant

.bc-checklist-assistant
  .bc-panel.bc-panel--action.bc-panel--shadow
    .bc-panel__header
      %h2.bc-panel__title
        Checklist
      .bc-progress-bar.bc-progress-bar--success.bc-progress-bar--sm
        .bc-progress-bar__bar{'role': 'progressbar', 'aria-valuemin': '0', 'aria-valuemax': '100', 'aria-valuenow': '60', 'aria-valuetext': '60% complete', 'aria-labelledby': 'checklist-progress-bar-output'}
          %span.bc-progress-bar__value{style: 'width:60%'}
          %span.bc-progress-bar__output{id: 'checklist-progress-bar-output'}
            %output
              60%
            complete
    .bc-panel__main
      %ul.bc-checklist-assistant__list.bc-list.bc-list--spacious
        %li.bc-checklist-assistant__item.bc-checklist-assistant__item--complete
          %a.bc-checklist-assistant__link{'aria-disabled':'true'}
            Verify your email address
        %li.bc-checklist-assistant__item.bc-checklist-assistant__item--complete
          %a.bc-checklist-assistant__link{'aria-disabled':'true'}
            Set up Two-Factor Authentication
        %li.bc-checklist-assistant__item
          %a.bc-checklist-assistant__link{href: '#'}
            Finish setting up your account
        %li.bc-checklist-assistant__item
          %a.bc-checklist-assistant__link{href: '#'}
            Set up your organization
        %li.bc-checklist-assistant__item.bc-checklist-assistant__item--complete
          %a.bc-checklist-assistant__link{'aria-disabled':'true'}
            Start an engagement
          %ul.bc-list.bc-list--compact.bc-list--bulleted.bc-hint
            %li
              %a{href: '#'}
                Add another engagement

Vertical nav

Guidance last updated: Sep 4, 2023

Vertical navigation used for third-level navigation.

This navigation uses anchors.

It supports nested lists up to 3 levels deep.

Tertiary nav internals

Current or active item

Set aria-current="page" on the currently active item anchor. This will style the current/active anchor.

A backup .bc-vertical-nav__link--active class is also provided if this is not possible.

Disabled items

If an item anchor is disabled, set aria-disabled="true" on the anchor and omit its href. This will style the disabled anchor.

A backup .bc-vertical-nav--disabled class is also provided if this is not possible.

Adding additional children

Use the provided ‘aside’ class to add content within the anchor, such as spinners or counts.

Small text can also be added, eg. “Unconfigured” for disabled items.

Keep additional content short.

Note: the anchor is a flex container so consider wrapping nested sibling text nodes in span tags.

Tertiary nav placement

Place the tertiary nav left to the main content space (eg. inside a sidebar).

If the tertiary nav is placed to the right of the main content space, append the --leftactive container variant to flip the active and hover bar effect to the left.

Rendered example of Vertical nav

Haml markup example of Vertical nav

%nav.bc-vertical-nav
  %ul.bc-vertical-nav__list
    %li
      %a.bc-vertical-nav__link{href: 'javascript:void(0)'}
        %span
          Amphibians
    %li
      %a.bc-vertical-nav__link{href: 'javascript:void(0)'}
        %span
          Birds
    %li
      %a.bc-vertical-nav__link{'aria-disabled': 'true'}
        %span
          Disabled navigation item
          %small.bc-helper-block
            Extinct
    %li
      %a.bc-vertical-nav__link{href: 'javascript:void(0)'}
        %span
          Reptiles
    %li
      %a.bc-vertical-nav__link{href: 'javascript:void(0)'}
        %span
          Mammals
      %ul
        %li
          %a.bc-vertical-nav__link{href: 'javascript:void(0)'}
            %span
              Prototheria (monotremes)
          %ul
            %li
              %a.bc-vertical-nav__link{href: 'javascript:void(0)'}
                %span
                  Platypus
            %li
              %a.bc-vertical-nav__link{href: 'javascript:void(0)', 'aria-current': 'page'}
                %span
                  Echidnas
                %span.bc-vertical-nav__link-aside
                  %span.bc-badge.bc-badge--counter
                    4
                    %span.bc-helper-sronly
                      items
        %li
          %a.bc-vertical-nav__link{href: 'javascript:void(0)'}
            %span
              Theria

Variants

--leftactive variant

Rendered example of Vertical nav
Haml markup example of Vertical nav --leftactive
%nav.bc-vertical-nav.bc-vertical-nav--leftactive
  %ul.bc-vertical-nav__list
    %li
      %a.bc-vertical-nav__link{href: 'javascript:void(0)'}
        %span
          Amphibians
    %li
      %a.bc-vertical-nav__link{href: 'javascript:void(0)'}
        %span
          Birds
    %li
      %a.bc-vertical-nav__link{'aria-disabled': 'true'}
        %span
          Disabled navigation item
          %small.bc-helper-block
            Extinct
    %li
      %a.bc-vertical-nav__link{href: 'javascript:void(0)'}
        %span
          Reptiles
    %li
      %a.bc-vertical-nav__link{href: 'javascript:void(0)'}
        %span
          Mammals
      %ul
        %li
          %a.bc-vertical-nav__link{href: 'javascript:void(0)'}
            %span
              Prototheria (monotremes)
          %ul
            %li
              %a.bc-vertical-nav__link{href: 'javascript:void(0)'}
                %span
                  Platypus
            %li
              %a.bc-vertical-nav__link{href: 'javascript:void(0)', 'aria-current': 'page'}
                %span
                  Echidnas
                %span.bc-vertical-nav__link-aside
                  %span.bc-badge.bc-badge--counter
                    4
                    %span.bc-helper-sronly
                      items
        %li
          %a.bc-vertical-nav__link{href: 'javascript:void(0)'}
            %span
              Theria

Tabbed nav

Guidance last updated: Sep 12, 2023

Tabs help switch between different sets of content at the same level of hierarchy.

Use to provide more visibility of sub sections of content there are in a page rather than dropdowns.

Tabbed navs stay at the top of the content container and control the content beneath it.

Do not use this pattern if users need to see multiple tabbed content sections simultaneously (cf. Accordions).

Using Tabbed navs

  • The first tab should be “active” by default — it should be the most important or relevant tab
  • Use simple and short labels
  • Avoid disabling any tab(s) — this is confusing to end users
  • Avoid long sets of tabs as they will wrap poorly at ≥md viewport breakpoint
  • Do not mix anchor and button tabs in the same tabbed nav

Disabled tabs

  • If an item anchor is disabled, set aria-disabled="true" on the anchor and omit its href. This will style the disabled anchor.
  • If an button is disabled, set disabled attribute to the button.

A backup .bc-tabbed-nav__item--disabled class is also provided if this is not possible.

Tabbed navs and JavaScript

Use JavaScript to dynamically change the active tab and its contents.

Two methods exist, using:

  • Anchors, for:
    • Separate web pages/endpoints
    • Content regions already existing on the page, linking each respective content container’s ID
  • Buttons for dynamic loading and/or hiding of content regions into the DOM.

When using anchors to link to other web pages set aria-current="page" on the current/selected anchor. Wrap the anchor list in a <nav>.

When using Javascript with either anchors or buttons to dynamically toggle tab content without page refreshes:

  • Set role="tablist" on the list
  • On each “Tab”:
    • Set role="tab" on each anchor or button
    • Set aria-controls on each anchor or button, linking its respective tabpanel ID
    • When using buttons, set the aria-selected boolean
    • When using anchors, set aria-current="true" on the current/selected tab.
  • On each “Tab panel”/content region:
    • Set role="tabpanel" on each content region
    • Set hidden on non-active content regions.

This is not required by using <button>s for dynamic route changes.


Usage guidance adapted from GOV.UK’s Tabs Design System entry and Chakra UI’s Tabs component.

Rendered example of Tabbed nav

“To apply” content.

“To apply” content.

Haml markup example of Tabbed nav

%nav.bc-tabbed-nav
  %ul.bc-list
    %li.bc-tabbed-nav__item.bc-tabbed-nav__item--active
      %a{href: '#to-apply-page-container', 'aria-current': 'page'}
        To apply
        %span.bc-badge.bc-badge--counter
          3
    %li.bc-tabbed-nav__item
      %a{href: '/foo'}
        Pending
        %span.bc-badge.bc-badge--counter.bc-badge--counter-muted
          3
    %li.bc-tabbed-nav__item
      %a{'aria-disabled': 'true'}
        Approved
        %span.bc-badge.bc-badge--counter.bc-badge--counter-muted
          2
    %li.bc-tabbed-nav__item
      %a{href: '/foobaz'}
        Rejected
        %span.bc-badge.bc-badge--counter.bc-badge--counter-muted
          2
    %li.bc-tabbed-nav__item.bc-tabbed-nav__item--disabled
      %a{'aria-disabled': 'true'}
        🏗️ Notes (disabled)

%section{id: 'to-apply'}
  %p “To apply” content.

%nav.bc-tabbed-nav
  %ul.bc-list{role: 'tablist'}
    %li.bc-tabbed-nav__item.bc-tabbed-nav__item--active
      %button{role: 'tab', id: 'toapply-button', type: 'button', 'aria-controls': 'toapply-container', 'aria-selected': 'true'}
        To apply
        %span.bc-badge.bc-badge--counter
          3
    %li.bc-tabbed-nav__item
      %button{role: 'tab', id: 'pending-button', type: 'button', 'aria-controls': 'pending-container', 'aria-selected': 'false'}
        Pending
        %span.bc-badge.bc-badge--counter.bc-badge--counter-muted
          3
    %li.bc-tabbed-nav__item
      %button{role: 'tab', type: 'button', 'aria-controls': 'approved-container', 'aria-selected': 'false'}
        Approved
        %span.bc-badge.bc-badge--counter.bc-badge--counter-muted
          2
    %li.bc-tabbed-nav__item
      %button{role: 'tab', type: 'button', 'aria-controls': 'rejected-container', 'aria-selected': 'false'}
        Rejected
        %span.bc-badge.bc-badge--counter.bc-badge--counter-muted
          2
    %li.bc-tabbed-nav__item.bc-tabbed-nav__item--disabled
      %button(disabled){role: 'tab', type: 'button', 'aria-controls': 'notes-container', 'aria-selected': 'false'}
        🏗️ Notes (disabled)

%div{role: 'tabpanel', id: 'toapply-container', 'aria-labelledby': 'toapply-button'}
  %p “To apply” content.

%div(hidden){role: 'tabpanel', id: 'pending-container', 'aria-labelledby': 'pending-button'}
  %p “Pending” content.

%div(hidden){role: 'tabpanel', id: 'approved-container', 'aria-labelledby': 'approved-button'}
  %p “Approved” content.

%div(hidden){role: 'tabpanel', id: 'rejected-container', 'aria-labelledby': 'rejected-button'}
  %p “Rejected” content.

%div(hidden){role: 'tabpanel', id: 'notes-container', 'aria-labelledby': 'notes-button'}
  %p “Notes” content.

Pagination

Guidance last updated: Sep 4, 2023

Use pagination controls to provide navigation for content that is paginated.

The pagination pattern has two functions:

  1. As an interactive navigation
  2. As an indicator of where the user is within the paginated list index, providing via <output>:
    • the current range of items presented on the page;
    • from the total number of items in the paginated index.

Pagination usability and accessibility

While <ol> would be suitable here given the linearity implied, use un-ordered lists (<ul>).

Non-visual assistive technologies (eg screen readers) commonly preface <ol> list items with their lexical position. This results in first announcing the lexical count of the list item, which will not align with the page count due to the [ellipsis] range indicators, and the First/Last/Prev/Next, controls.

Set aria-current="true" for the current numbered page anchor — this should also apply to the “First” and “Last” anchors when on those pages.

Set aria-hidden="true" on the list items that contain the range indicators, as these are entirely superfluous — and may cause confusion — to non-visual users. This does not visually hide the element.

Omit the jump control links — first/prev and next/last — when on the first and last page of listings respectively.

Provide a maximum of three pages prior to and after the current page; do not list every possible page.

Pagination in Single Page Applications

Use <button>s when paginating in a single page application.

Use ARIA to provide context for the navigation controls:

  • menubar role on the wrapping container, replacing or overriding the implicit <nav> role
  • menu role on the top-level wrapping <ul>
  • menuitem roles for the first/last and prev/next buttons
  • menuradioitem role for each numbered page button
  • aria-checked for the current numbered page button — this should also apply to the “First” and “Last” buttons when on those pages
  • aria-setsize for each numbered page button giving the total number
  • aria-posinset for each numbered page button giving its lexical position within the set.

Rendered example of Pagination

Haml markup example of Pagination

%nav.bc-pagination
  %span.bc-pagination__label{id: 'program-pagination-label'}
    Program listing pagination
  %ul.bc-pagination__list{role: 'navigation', 'aria-labelledby': 'program-pagination-label'}
    %li.bc-pagination__item.bc-pagination__item--prior
      %ul
        %li.bc-pagination__item.bc-pagination__item--first
          %a.bc-pagination__link{href: '/1'}
            First
        %li.bc-pagination__item.bc-pagination__item--prev
          %a.bc-pagination__link{href: '/3', rel: 'prev'}
            Prev
    %li.bc-pagination__item.bc-pagination__item--page
      %a.bc-pagination__link{href: '/1'}
        %span.bc-pagination__label Page
        1
    %li.bc-pagination__item.bc-pagination__item--page(aria-hidden='true')
      %span
        &hellip;
    %li.bc-pagination__item.bc-pagination__item--page
      %a.bc-pagination__link{href: '/3', rel: 'prev'}
        %span.bc-pagination__label Page
        3
    %li.bc-pagination__item.bc-pagination__item--page
      %a.bc-pagination__current(aria-current='true')
        %span.bc-pagination__label Page
        4
    %li.bc-pagination__item.bc-pagination__item--page
      %a.bc-pagination__link{href: '/5', rel: 'next'}
        %span.bc-pagination__label Page
        5
    %li.bc-pagination__item.bc-pagination__item--page(aria-hidden='true')
      %span
        &hellip;
    %li.bc-pagination__item.bc-pagination__item--page
      %a.bc-pagination__link{href: '/11'}
        %span.bc-pagination__label Page
        11
    %li.bc-pagination__item.bc-pagination__item--after
      %ul
        %li.bc-pagination__item.bc-pagination__item--next
          %a.bc-pagination__link{href: '/5', rel: 'next'}
            Next
        %li.bc-pagination__item.bc-pagination__item--last
          %a.bc-pagination__link{href: '/11'}
            Last
  %output.bc-pagination__count
    %span
      Viewing
    %span.bc-pagination__page-range
      76&thinsp;&ndash;&thinsp;100
    %span
      of
    %span.bc-pagination__total
      274


.bc-pagination{role: 'menubar'}
  %span.bc-pagination__label{id: 'program-pagination-label'}
    Program listing pagination
  %ul.bc-pagination__list{role: 'menu', 'aria-labelledby': 'program-pagination-label'}
    %li.bc-pagination__item.bc-pagination__item--prior
      %ul
        %li.bc-pagination__item.bc-pagination__item--first
          %button.bc-pagination__link{type: 'button', role: 'menuitem', 'aria-label': 'Go to the first page', 'aria-checked': 'false'}
            First
        %li.bc-pagination__item.bc-pagination__item--prev
          %button.bc-pagination__link{type: 'button', role: 'menuitem', 'aria-label': 'Go to the previous page', 'aria-checked': 'false'}
            Prev
    %li.bc-pagination__item.bc-pagination__item--page
      %button.bc-pagination__link{type: 'button', role: 'menuitemradio', 'aria-label': 'Go to Page 1', 'aria-checked': 'false', 'aria-setsize': '11', 'aria-posinset': '1'}
        %span.bc-pagination__label Page
        1
    %li.bc-pagination__item.bc-pagination__item--page(aria-hidden='true')
      %span
        &hellip;
    %li.bc-pagination__item.bc-pagination__item--page
      %button.bc-pagination__link{type: 'button', role: 'menuitemradio', 'aria-label': 'Go to Page 3', 'aria-checked': 'false', 'aria-setsize': '11', 'aria-posinset': '3'}
        %span.bc-pagination__label Page
        3
    %li.bc-pagination__item.bc-pagination__item--page
      %button.bc-pagination__current(disabled){type: 'button', role: 'menuitemradio', 'aria-label': 'Page 4', 'aria-checked': 'true', 'aria-setsize': '11', 'aria-posinset': '4'}
        %span.bc-pagination__label Page
        4
    %li.bc-pagination__item.bc-pagination__item--page
      %button.bc-pagination__link{type: 'button', role: 'menuitemradio', 'aria-label': 'Go to Page 5', 'aria-checked': 'false', 'aria-setsize': '11', 'aria-posinset': '5'}
        %span.bc-pagination__label Page
        5
    %li.bc-pagination__item.bc-pagination__item--page(aria-hidden='true')
      %span
        &hellip;
    %li.bc-pagination__item.bc-pagination__item--page
      %button.bc-pagination__link{type: 'button', role: 'menuitemradio', 'aria-label': 'Go to Page 11', 'aria-checked': 'false', 'aria-setsize': '11', 'aria-posinset': '11'}
        %span.bc-pagination__label Page
        11
    %li.bc-pagination__item.bc-pagination__item--after
      %ul
        %li.bc-pagination__item.bc-pagination__item--next
          %button.bc-pagination__link{type: 'button', role: 'menuitem', 'aria-label': 'Go to the next page', 'aria-checked': 'false'}
            Next
        %li.bc-pagination__item.bc-pagination__item--last
          %button.bc-pagination__link{type: 'button', role: 'menuitem', 'aria-label': 'Go to the last page', 'aria-checked': 'false'}
            Last
  -# TODO: add aria for <output>
  %output.bc-pagination__count
    %span
      Viewing
    %span.bc-pagination__page-range
      76&thinsp;&ndash;&thinsp;100
    %span
      of
    %span.bc-pagination__total
      274

Skip-to

Guidance last updated: Sep 4, 2023

Visually-hidden ‘skip-links’ that are keyboard accessible.

Use to provide keyboard-accessible jump points to main sections of a page.

Typically skip-links link users to

  • Main navigation (the primary <nav>)
  • Main content container (eg <main>)

The links are established by pointing to the ID for each endpoint.

Avoid ID collisions: Ensure the page content ID does not conflict with the ID the Modals rely on.

Testing the rendered example: Skip-to links are visually hidden until they receive focus. Use the keyboard by pressing Tab to bring the links into :focus.

Visual location of skip-links: Upon receiving :focus, skip-to links will always visually appear to the top left of the page, layered at the ‘overlay’ z-index context layer.

Guidance last updated: Sep 12, 2023

In-page Table of Contents-style navigation.

Nav ToC is our 4th-level navigation.

Use it for in-page Table of Contents, linking only to resources on the current page a user is on.

Do not link off-page using this navigation.

Nav ToC React documentation

ℹ️ This feature doesn’t have a corresponding React component yet.

Nav ToC HTML

ToC links should be in an ordered list (<ol>).

Links must be in the order in which they appear on the page.

Nested lists and links are not supported (see Rationale section).

Nav ToC design

Nav Toc is built to support both static templating and dynamic web pages.

Current active item styling (orange border) is only available on dynamic pages (see Accessibility section).

Nav ToC variants

2 variants exist:

  • .bc-nav-toc--lg: Increases component font-size to $bc-font-size--lg
    • Spacing and faux-border dimensions are set in ems to scale proportionally.
  • .bc-nav-toc--fixed: Sets position: fixed for layout placement in sidebars.

When to include “Back to top” links

Add a “Back to top” link within the navigation component only when it’s position: fixed.

When not using the fixed-position layout or static templating, optionally add a “Back to top” link to the header or footer of each section being linked to.

Nav ToC accessibility

To improve semantics, usability, and overall accessibility this component:

  • Uses a wrapping <nav> element
  • Gives a contextual HTML heading for this list of navigation links (eg. “On this page”)
  • Anchors must only link to (unique) IDs on the current page
  • Anchors must only use aria-current if the page’s routing is dynamically handled in JS:
    • Why? In static templating and URL pathing, when only the ID changes the aria-current attribute cannot be updated without JavaScript.
    • If the navigation is position: fixed as the user scrolls, update aria-current to match the scroll position.

“Back to top” icon accessibility

Directional icons are additive to the link text and can be confusing to assistive technologies.

Set aria-hidden on the icon to remove it from the Accessibility Tree, eg:

<div class='bc-hint'>
  <span aria-hidden='true'>
    &uarr;
  </span>
  <a href='#main'>
    Back to top
  </a>
</div>

Nav ToC rationale

Users are familiar with table of contents patterns, both in print and on the Web.

A table of contents helps users understand how longer pages are structured.

ToC links let users navigate to and between on-page content.

The list intentionally does not support nesting to keep the navigation simpler and to encourage thoughtful structuring of longer pages.

Anchors must only link to on-page content because mixing on-page and off-page links is inconsistent and confusing.

Despite being an ordered list the numbered list-style are not shown. If you need a lexically ordered navigation see the Stepped nav component.

Nav ToC see also

  • Vertical nav
  • Sub nav
  • Nav

Rendered example of Nav ToC

Haml markup example of Nav ToC

%nav.bc-nav-toc{id: 'page-toc'}
  %h2 On this page
  %ol.bc-nav-toc__list
    %li
      %a.bc-nav-toc__link{href: '#amphibians', 'aria-current': 'true'}
        Amphibians
    %li
      %a.bc-nav-toc__link{href: '#birds'}
        Birds
    %li
      %a.bc-nav-toc__link{href: '#reptiles'}
        Reptiles
    %li
      %a.bc-nav-toc__link{href: '#lipsum'}
        Overflow test: Lorem ipsum dolor sit, amet consectetur adipisicing elit

  .bc-hint
    %span{'aria-hidden': 'true'}
      &uarr;
    %a{href:'#main'}
      Back to top