Skip to main content
/
/
/
/
Table of contents

Table of contents

Combining a few of our components and CSS classes you can create a responsive and accessible table of contents.

Style links without underline (until hovered or focused) with the .bf-title-link class.

<a class="bf-title-link" href="#heading_id">Link to Heading</a>
<a class="bf-title-link" href="#heading_id">Link to Heading</a>

Style the <ol> list as a TOC with the .bf-toc class.

<ol class="bf-toc"> <li><a class="bf-title-link" href="#heading_id">Link to Heading</a></li> </ol>
<ol class="bf-toc"> <li><a class="bf-title-link" href="#heading_id">Link to Heading</a></li> </ol>

Nested lists will inherit the TOC styling. Combined with links it will look something like this:

Scroll to a page section

Native HTML anchors allow you to link to a page section.

<a href="#my-heading">This link will scroll to</a> <h3 id="my-heading">The element with matching `id`</h3>
<a href="#my-heading">This link will scroll to</a> <h3 id="my-heading">The element with matching `id`</h3>

Highlight active heading

Optionally wrap the content in an element with .bf-hightlight-target to highlight the currently active header.

<article class="bf-highlight-target"> <a href="#my-heading">This link will scroll to</a> <h3 id="my-heading">The element with matching `id`</h3> </article>
<article class="bf-highlight-target"> <a href="#my-heading">This link will scroll to</a> <h3 id="my-heading">The element with matching `id`</h3> </article>

Responsive components

Depending on the width required for your page content, you can probably placed the table of contents on the right hand side in a sticky container, replaced by an accordion for small screens.

1. Sticky box for desktop

We provide a <Sticky> component to create a box that follows scrollable content, then sticks to the top.

<Sticky> <ol className="bf-toc">...</ol> </Sticky>
<Sticky> <ol className="bf-toc">...</ol> </Sticky>

2. Accordion for mobile

For mobile screens, we don't have sufficient space for the sticky box, instead we can use a full-width <Accordion> preceding the content.

<Accordion> <Accordion.Item title="On this page"> <ol className="bf-toc">...</ol> </Accordion.Item> </Accordion>
<Accordion> <Accordion.Item title="On this page"> <ol className="bf-toc">...</ol> </Accordion.Item> </Accordion>

3. Breakpoint CSS

Since we don't want to show all of these elements for all screens, we can use some breakpoint classes.

The medium keyword breaks at 960px viewport width.

4. All of the above

Here we've also added an "On this page" header in the sticky box.

const toc = ( <ol className='bf-toc'> <li> <a className="bf-title-link">Link</a> </li> {/* more items... */} </ol> ); // placed in a right-hand column layout <Sticky className='from-medium'> <header className='bfc-base-2 bf-medium bf-strong'>On this Page</header> {toc} </Sticky> // placed preceding content <Accordion className="to-medium"> <Accordion.Item title="On this page">{toc}</Accordion.Item> </Accordion>
const toc = ( <ol className='bf-toc'> <li> <a className="bf-title-link">Link</a> </li> {/* more items... */} </ol> ); // placed in a right-hand column layout <Sticky className='from-medium'> <header className='bfc-base-2 bf-medium bf-strong'>On this Page</header> {toc} </Sticky> // placed preceding content <Accordion className="to-medium"> <Accordion.Item title="On this page">{toc}</Accordion.Item> </Accordion>

Live demo

Try resizing the preview pane to see mobile/desktop version, or open in codesanbox.io and resize window.

import { useState } from "react";
import { faList } from "@fortawesome/free-regular-svg-icons";

import Sticky from "@intility/bifrost-react/Sticky";
import Accordion from "@intility/bifrost-react/Accordion";
import Button from "@intility/bifrost-react/Button";
import Icon from "@intility/bifrost-react/Icon";

export default function TocDemo() {
  const toc = (
    <ol className="bf-toc">
      <li>
        <a href="#scrolldown" className="bf-title-link">
          Scroll downwards
        </a>
      </li>
      <li>
        <a href="#secondSection" className="bf-title-link">
          Second section
        </a>
        <ol>
          <li>
            <a href="#firstSubSection" className="bf-title-link">
              First sub-section
            </a>
          </li>
          <li>
            <a href="#secondSubSection" className="bf-title-link">
              Second sub-section
            </a>
          </li>
        </ol>
      </li>
      <li>
        <a href="#scrollup" className="bf-title-link">
          Scroll upwards
        </a>
      </li>
    </ol>
  );

  return (
    <>
      <div className="bf-content bf-highlight-target">
        <h1>
          <span className="from-medium">Desktop demo</span>
          <span className="to-medium">Mobile demo</span>
        </h1>
        <p>
          On desktop screens over 960px you should get a sticky box, but on
          smaller screens a floating button instead, which opens a drawer from
          bottom.
        </p>
        <p>
          Resize your browser window to test{" "}
          <span className="from-medium">mobile</span>
          <span className="to-medium">desktop</span> mode.
        </p>
        <Accordion className="to-medium">
          <Accordion.Item title="On this page">{toc}</Accordion.Item>
        </Accordion>
        <div className="toc-demo-layout">
          <Sticky className="from-medium bfl-grid">
            <header className="bfc-base-2 bf-medium bf-strong">
              On this Page
            </header>
            {toc}
          </Sticky>
          <div>
            <h2 id="scrolldown">Scroll downwards</h2>
            <p>
              Lorem ipsum dolor sit amet. Eos Quis laudantium 33 consequatur
              dicta eum expedita dolore aut nostrum nihil. Est dolorem
              consequuntur et accusamus fuga et repellendus facilis et incidunt
              explicabo. 33 galisum sapiente non provident itaque et itaque
              dolores non maiores doloribus a suscipit eveniet! 33 illum labore
              id veniam sint cum deserunt omnis est deserunt facere et eaque
              exercitationem et pariatur velit?
            </p>
            <p>
              Qui minima doloribus ut dolore iure qui debitis voluptatibus aut
              dolore nostrum hic consequatur. Sed consequatur nemo qui ipsum
              enim et velit architecto sit perferendis repellendus.
            </p>
            <p>
              Sed deserunt voluptatum et voluptate voluptatem aut labore
              reprehenderit vel corporis sapiente. 33 omnis magnam et ratione
              saepe aut dicta veritatis rem voluptatem necessitatibus aut
              voluptates autem est sunt delectus aut commodi doloribus.
            </p>
            <p>
              Lorem ipsum dolor sit amet. Eos Quis laudantium 33 consequatur
              dicta eum expedita dolore aut nostrum nihil. Est dolorem
              consequuntur et accusamus fuga et repellendus facilis et incidunt
              explicabo. 33 galisum sapiente non provident itaque et itaque
              dolores non maiores doloribus a suscipit eveniet! 33 illum labore
              id veniam sint cum deserunt omnis est deserunt facere et eaque
              exercitationem et pariatur velit?
            </p>
            <h2 id="secondSection">Second section</h2>
            <p>
              Lorem ipsum dolor sit amet. Eos Quis laudantium 33 consequatur
              dicta eum expedita dolore aut nostrum nihil. Est dolorem
              consequuntur et accusamus fuga et repellendus facilis et incidunt
              explicabo. 33 galisum sapiente non provident itaque et itaque
              dolores non maiores doloribus a suscipit eveniet! 33 illum labore
              id veniam sint cum deserunt omnis est deserunt facere et eaque
              exercitationem et pariatur velit?
            </p>
            <h3 id="firstSubSection">First sub-section</h3>
            <p>
              Sed deserunt voluptatum et voluptate voluptatem aut labore
              reprehenderit vel corporis sapiente. 33 omnis magnam et ratione
              saepe aut dicta veritatis rem voluptatem necessitatibus aut
              voluptates autem est sunt delectus aut commodi doloribus.
            </p>
            <h3 id="secondSubSection">Second sub-section</h3>
            <p>
              Sed deserunt voluptatum et voluptate voluptatem aut labore
              reprehenderit vel corporis sapiente. 33 omnis magnam et ratione
              saepe aut dicta veritatis rem voluptatem necessitatibus aut
              voluptates autem est sunt delectus aut commodi doloribus.
            </p>
            <h2 id="scrollup">Scroll upwards</h2>
            <p>
              Qui minima doloribus ut dolore iure qui debitis voluptatibus aut
              dolore nostrum hic consequatur. Sed consequatur nemo qui ipsum
              enim et velit architecto sit perferendis repellendus.
            </p>
            <p>
              Sed deserunt voluptatum et voluptate voluptatem aut labore
              reprehenderit vel corporis sapiente. 33 omnis magnam et ratione
              saepe aut dicta veritatis rem voluptatem necessitatibus aut
              voluptates autem est sunt delectus aut commodi doloribus.
            </p>
            <p>
              <a href="#top">Scroll to top</a>
            </p>
          </div>
        </div>
      </div>
      <style>{`
      .toc-demo-layout h2:first-child { margin-top: 12px }
      @media (min-width: 960px) {
        .toc-demo-layout {
          display: grid;
          grid-template-columns: 1fr auto;
          gap: 1rem;
          margin-block: 2rem 10rem;
          place-items: start;
        }
        .toc-demo-layout > :first-child {
          grid-column-start: 2;
        }
        .toc-demo-layout > :last-child {
          grid-row-start: 1;
          grid-column-start: 1;
        }
      }
      `}</style>
    </>
  );
}