Skip to main content

Table of contents

The table of contents lets users navigate between pages of a guide or jump to sections on a page.

<aside class="ons-toc-container" role="complementary">
  <a class="ons-skip-link" href="#0">Skip to guide content</a>
  <nav class="ons-toc" aria-label="Pages in this guide">
    <h2 class="ons-toc__title ons-u-fs-r--b ons-u-mb-s">Contents</h2>
    <ol class="ons-list ons-u-mb-m ons-list--dashed">
      <li class="ons-list__item" aria-current="true">
        Overview</li>
      <li class="ons-list__item">
        <a href="#0" class="ons-list__link  ">Who should take part and why</a>
      </li>
      <li class="ons-list__item">
        <a href="#0" class="ons-list__link  ">How your information is used</a>
      </li>
      <li class="ons-list__item">
        <a href="#0" class="ons-list__link  ">The 2019 Census Rehearsal</a>
      </li>
      <li class="ons-list__item">
        <a href="#0" class="ons-list__link  ">Online census</a>
      </li>
    </ol>
  </nav>
</aside>
{% from "components/table-of-contents/_macro.njk" import onsTableOfContents %}

{{-
    onsTableOfContents({
        "title": 'Contents',
        "ariaLabel": 'Pages in this guide',
        "skipLink": {
            "url": "#0",
            "text": 'Skip to guide content'
        },
        "itemsList": [
            {
                "url": '#0',
                "text": 'Overview',
                "current": true
            },
            {
                "url": '#0',
                "text": 'Who should take part and why'
            },
            {
                "url": '#0',
                "text": 'How your information is used'
            },
            {
                "url": '#0',
                "text": 'The 2019 Census Rehearsal'
            },
            {
                "url": '#0',
                "text": 'Online census'
            }
        ]
    })
}}
Name Type Required Description
Title string true A title for the TOC, for example, “Contents”
ariaLabel string false Accessible label to provide context to contents, for example, “Links to page sections”. Defaults to Table of contents
skipLink Skip link (ref) true Settings for the skip to content link that allows users to avoid reading out the TOC on each page
lists Array<list> true An array of list items to render.

Lists

Name Type Required Description
listHeading string false A heading that can be added between TOC link list items
listHeadingHidden string false Accessible hidden text to provide context to the heading
itemsList Array<itemsList> true An array of list item links

itemsList

Name Type Required Description
url string true URL that contains the id of the content heading to link to
text string true Text to display for the list item
{% macro onsTableOfContents(params) %}
  {% from "components/lists/_macro.njk" import onsList %}
  {% from "components/skip-to-content/_macro.njk" import onsSkipToContent %}
  
  {% if params.skipLink is defined and params.skipLink %}
    <aside class="ons-toc-container" role="complementary">
      {{
          onsSkipToContent({
              "url": params.skipLink.url,
              "text": params.skipLink.text
          })
      }}
    {% endif %}

    <nav class="ons-toc" aria-label="{{ params.ariaLabel | default('Table of contents') }}">
      <h2 class="ons-toc__title ons-u-fs-r--b ons-u-mb-s">{{ params.title }}</h2>
      {% if params.lists is defined and params.lists %}
        {% for list in params.lists %}
          {% if list.listHeading is defined and list.listHeading %}
            <h3 class="ons-u-fs-r ons-u-mb-xs">{{ list.listHeading }}<span class="ons-u-vh"> {{ list.listHeadingHidden }}</span>:</h3>
          {% endif %}
          {{
              onsList({
                  "element": 'ol',
                  "classes": 'ons-u-mb-m',
                  "variants": 'dashed',
                  "itemsList": list.itemsList
              })
          }}
        {% endfor %}
      {% elif params.itemsList is defined and params.itemsList %}
        {{
            onsList({
                "element": 'ol',
                "classes": 'ons-u-mb-m',
                "variants": 'dashed',
                "itemsList": params.itemsList
            })
        }}
      {% endif %}
    </nav>
  </aside>
{% endmacro %}
.ons-toc {
  &-container {
    border-bottom: 1px solid $color-grey-15;
    margin-bottom: 2rem;
    padding-bottom: 1rem;
  }

  &__link-active {
    color: $color-text-link-hover;
    text-decoration: underline solid $color-text-link-hover 2px;
  }
}

When to use this component

Use this component at the top of a long page of content broken up by subheadings, or at the top of each page of a guide.

Do not use the in-page variant of this component if the height the table of contents will be greater than the height of the browser‘s viewport. The list will not scroll independently of the page content because research shows some users find this confusing.

Variants

Table of contents - grouped

<aside class="ons-toc-container" role="complementary">
  <a class="ons-skip-link" href="#0">Skip to guide content</a>
  <nav class="ons-toc" aria-label="Links to page sections">
    <h2 class="ons-toc__title ons-u-fs-r--b ons-u-mb-s">Contents</h2>
    <h3 class="ons-u-fs-r ons-u-mb-xs">Household questions<span class="ons-u-vh"> help topics</span>:</h3>
    <ol class="ons-list ons-u-mb-m ons-list--dashed">
      <li class="ons-list__item">
        <a href="#household1" class="ons-list__link  ">Household and who lives here</a>
      </li>
      <li class="ons-list__item">
        <a href="#household2" class="ons-list__link  ">Housing, sex and GI</a>
      </li>
    </ol>
    <h3 class="ons-u-fs-r ons-u-mb-xs">Individual questions<span class="ons-u-vh"> help topics</span>:</h3>
    <ol class="ons-list ons-u-mb-m ons-list--dashed">
      <li class="ons-list__item">
        <a href="#individual1" class="ons-list__link  ">Name, date of birth and marital status</a>
      </li>
      <li class="ons-list__item">
        <a href="#individual2" class="ons-list__link  ">Language, religion and ethnicity</a>
      </li>
      <li class="ons-list__item">
        <a href="#individual3" class="ons-list__link  ">Address, health and sexual orientation</a>
      </li>
      <li class="ons-list__item">
        <a href="#individual4" class="ons-list__link  ">Education and employment</a>
      </li>
    </ol>
  </nav>
</aside>
{% from "components/table-of-contents/_macro.njk" import onsTableOfContents %}

{{-
    onsTableOfContents({
        "title": 'Contents',
        "ariaLabel": 'Links to page sections',
        "skipLink": {
            "url": '#0',
            "text": 'Skip to guide content'
        },
        "lists": [
            {
                "listHeading": 'Household questions',
                "listHeadingHidden": 'help topics',
                "itemsList": [
                    {
                        "url": '#household1',
                        "text": 'Household and who lives here'
                    },
                    {
                        "url": '#household2',
                        "text": 'Housing, sex and GI'
                    }
                ]
            },
            {
                "listHeading": 'Individual questions',
                "listHeadingHidden": 'help topics',
                "itemsList": [
                    {
                        "url": '#individual1',
                        "text": 'Name, date of birth and marital status'
                    },
                    {
                        "url": '#individual2',
                        "text": 'Language, religion and ethnicity'
                    },
                    {
                        "url": '#individual3',
                        "text": 'Address, health and sexual orientation'
                    },
                    {
                        "url": '#individual4',
                        "text": 'Education and employment'
                    }
                ]
            }
        ]
    })
}}
Name Type Required Description
Title string true A title for the TOC, for example, “Contents”
ariaLabel string false Accessible label to provide context to contents, for example, “Links to page sections”. Defaults to Table of contents
skipLink Skip link (ref) true Settings for the skip to content link that allows users to avoid reading out the TOC on each page
lists Array<list> true An array of list items to render.

Lists

Name Type Required Description
listHeading string false A heading that can be added between TOC link list items
listHeadingHidden string false Accessible hidden text to provide context to the heading
itemsList Array<itemsList> true An array of list item links

itemsList

Name Type Required Description
url string true URL that contains the id of the content heading to link to
text string true Text to display for the list item
{% macro onsTableOfContents(params) %}
  {% from "components/lists/_macro.njk" import onsList %}
  {% from "components/skip-to-content/_macro.njk" import onsSkipToContent %}
  
  {% if params.skipLink is defined and params.skipLink %}
    <aside class="ons-toc-container" role="complementary">
      {{
          onsSkipToContent({
              "url": params.skipLink.url,
              "text": params.skipLink.text
          })
      }}
    {% endif %}

    <nav class="ons-toc" aria-label="{{ params.ariaLabel | default('Table of contents') }}">
      <h2 class="ons-toc__title ons-u-fs-r--b ons-u-mb-s">{{ params.title }}</h2>
      {% if params.lists is defined and params.lists %}
        {% for list in params.lists %}
          {% if list.listHeading is defined and list.listHeading %}
            <h3 class="ons-u-fs-r ons-u-mb-xs">{{ list.listHeading }}<span class="ons-u-vh"> {{ list.listHeadingHidden }}</span>:</h3>
          {% endif %}
          {{
              onsList({
                  "element": 'ol',
                  "classes": 'ons-u-mb-m',
                  "variants": 'dashed',
                  "itemsList": list.itemsList
              })
          }}
        {% endfor %}
      {% elif params.itemsList is defined and params.itemsList %}
        {{
            onsList({
                "element": 'ol',
                "classes": 'ons-u-mb-m',
                "variants": 'dashed',
                "itemsList": params.itemsList
            })
        }}
      {% endif %}
    </nav>
  </aside>
{% endmacro %}
.ons-toc {
  &-container {
    border-bottom: 1px solid $color-grey-15;
    margin-bottom: 2rem;
    padding-bottom: 1rem;
  }

  &__link-active {
    color: $color-text-link-hover;
    text-decoration: underline solid $color-text-link-hover 2px;
  }
}

Table of contents - in-page navigation

Use this variant to help users quickly jump to sections of a long page of content.

As the user scrolls down the page, the table of contents will remain accessible at the top of the viewport and will highlight the subheading of the section in view.

How to use this variant

To use this variant on a page, you need to:

  • place the table of contents in the first of two columns of a grid layout and add the class .ons-grid__col--sticky@m to the div
  • add the class .ons-js-toc-container to the <div> which wraps both columns
  • wrap each section of content in the second column in a <section> tag with a unique id. For example: <section id="section1">
  • set a URI fragment as the url value of each link in the table of contents list, matching the id of the section it links to. For example #section1
  • start each section of content with an <h2> heading matching each link in the table of contents list
Important information:
You will need to open the following example in a new tab to see it highlight the section in view
<div class="ons-page__container ons-container">
  <div class="ons-grid ons-js-toc-container">
    <div class="ons-grid__col ons-grid__col--sticky@m ons-col-4@m">
      <nav class="ons-toc" aria-label="Sections in this page">
        <h2 class="ons-toc__title ons-u-fs-r--b ons-u-mb-s">Contents</h2>
        <ol class="ons-list ons-u-mb-m ons-list--dashed">
          <li class="ons-list__item">
            <a href="#section1" class="ons-list__link  ">What is the census?</a>
          </li>
          <li class="ons-list__item">
            <a href="#section2" class="ons-list__link  ">The online census has now closed</a>
          </li>
          <li class="ons-list__item">
            <a href="#section3" class="ons-list__link  ">What happens after Census Day</a>
          </li>
          <li class="ons-list__item">
            <a href="#section4" class="ons-list__link  ">The census in Northern Ireland and Scotland</a>
          </li>
          <li class="ons-list__item">
            <a href="#section5" class="ons-list__link  ">The last census</a>
          </li>
        </ol>
      </nav>
      </aside>
    </div>
    <div class="ons-grid__col ons-col-7@m ons-push-1@m">
      <section id="section1">
        <h2>What is the census?</h2>
        <p>The census is a survey that happens every 10 years and gives us a picture of all the people and households in England and Wales.</p>
        <p>Your answers to the census questions will help organisations make decisions on planning and funding public services in your area, including transport, education and healthcare.</p>
      </section>
      <section id="section2">
        <h2>The online census has now closed</h2>
        <p>Census Day was on Sunday 21 March 2021.</p>
        <p>If you still have a paper census questionnaire, fill it in as soon as you can and return it to FREEPOST, Census 2021.</p>
      </section>
      <section id="section3">
        <h2>What happens after Census Day</h2>
        <p>Two follow-up surveys, the Census Coverage Survey and the Census Quality Survey, help to improve the quality and accuracy of the census results.</p>
        <p>Over the next year, we work hard to process the answers from all the census questionnaires. This is so that we can publish the initial Census 2021 findings one year after the census and the main releases two years after the census.</p>
        <p>To make sure the Census 2021 statistics we publish meet the needs of those who use them, we also run an outputs consultation.</p>
      </section>
      <section id="section4">
        <h2>The census in Northern Ireland and Scotland</h2>
        <p>The Office for National Statistics (ONS) runs the census in England and Wales.</p>
        <p>If you live in Northern Ireland, visit the Northern Ireland Statistics Agency (NISRA) census website to find out about the census for Northern Ireland.</p>
        <p>If you live in Scotland, visit the National Records of Scotland census website to find out how to take part in the census for Scotland.</p>
      </section>
      <section id="section5">
        <h2>The last census</h2>
        <p>The last census took place in 2011. Many people and organisations used information from the 2011 Census in a variety of ways.</p>
        <p>For example, Bristol City Council used it to inform decisions on how to fund local housing improvements. It was also essential for the charity Redbridge Council for Voluntary Services to help people from ethnic minority groups learn more about dementia.</p>
        <p>To read more about the benefits that information from the 2011 Census helped to create, visit the Office for National Statistics (ONS) website.</p>
        <p>The Census Coverage Survey (CCS) was a short, separate survey that we at the Office for National Statistics (ONS) started six to eight weeks after Census Day.</p>
        <p>The CCS helps make sure everyone is counted and our census results are as accurate as possible. It asked similar questions to the main census, just fewer of them, at addresses in a selection of postcodes across England and Wales.</p>
        <p>The CCS has now closed.</p>
      </section>
    </div>
  </div>
</div>
{% from "components/table-of-contents/_macro.njk" import onsTableOfContents %}
<div class="ons-page__container ons-container">
    <div class="ons-grid ons-js-toc-container">
        <div class="ons-grid__col ons-grid__col--sticky@m ons-col-4@m">
            {{-
                onsTableOfContents({
                    "title": 'Contents',
                    "ariaLabel": 'Sections in this page',
                    "itemsList": [
                        {
                            "url": '#section1',
                            "text": 'What is the census?'
                        },
                        {
                            "url": '#section2',
                            "text": 'The online census has now closed'
                        },
                        {
                            "url": '#section3',
                            "text": 'What happens after Census Day'
                        },
                        {
                            "url": '#section4',
                            "text": 'The census in Northern Ireland and Scotland'
                        },
                        {
                            "url": '#section5',
                            "text": 'The last census'
                        }
                    ]
                })
            }}
        </div>
        <div class="ons-grid__col ons-col-7@m ons-push-1@m">
            <section id="section1">
                <h2>What is the census?</h2>
                <p>The census is a survey that happens every 10 years and gives us a picture of all the people and households in England and Wales.</p>

                <p>Your answers to the census questions will help organisations make decisions on planning and funding public services in your area, including transport, education and healthcare.</p>
            </section>
            <section id="section2">
                <h2>The online census has now closed</h2>
                <p>Census Day was on Sunday 21 March 2021.</p>

                <p>If you still have a paper census questionnaire, fill it in as soon as you can and return it to FREEPOST, Census 2021.</p>
            </section>
            <section id="section3">
                <h2>What happens after Census Day</h2>
                <p>Two follow-up surveys, the Census Coverage Survey and the Census Quality Survey, help to improve the quality and accuracy of the census results.</p>

                <p>Over the next year, we work hard to process the answers from all the census questionnaires. This is so that we can publish the initial Census 2021 findings one year after the census and the main releases two years after the census.</p>

                <p>To make sure the Census 2021 statistics we publish meet the needs of those who use them, we also run an outputs consultation.</p>
            </section>
            <section id="section4">
                <h2>The census in Northern Ireland and Scotland</h2>
                <p>The Office for National Statistics (ONS) runs the census in England and Wales.</p>

                <p>If you live in Northern Ireland, visit the Northern Ireland Statistics Agency (NISRA) census website to find out about the census for Northern Ireland.</p>

                <p>If you live in Scotland, visit the National Records of Scotland census website to find out how to take part in the census for Scotland.</p>
            </section>
            <section id="section5">
                <h2>The last census</h2>
                <p>The last census took place in 2011. Many people and organisations used information from the 2011 Census in a variety of ways.</p>

                <p>For example, Bristol City Council used it to inform decisions on how to fund local housing improvements. It was also essential for the charity Redbridge Council for Voluntary Services to help people from ethnic minority groups learn more about dementia.</p>

                <p>To read more about the benefits that information from the 2011 Census helped to create, visit the Office for National Statistics (ONS) website.</p>
                <p>The Census Coverage Survey (CCS) was a short, separate survey that we at the Office for National Statistics (ONS) started six to eight weeks after Census Day.</p>

                <p>The CCS helps make sure everyone is counted and our census results are as accurate as possible. It asked similar questions to the main census, just fewer of them, at addresses in a selection of postcodes across England and Wales.</p>

                <p>The CCS has now closed.</p>
            </section>
        </div>
    </div>
</div>

Name Type Required Description
Title string true A title for the TOC, for example, “Contents”
ariaLabel string false Accessible label to provide context to contents, for example, “Links to page sections”. Defaults to Table of contents
skipLink Skip link (ref) true Settings for the skip to content link that allows users to avoid reading out the TOC on each page
lists Array<list> true An array of list items to render.

Lists

Name Type Required Description
listHeading string false A heading that can be added between TOC link list items
listHeadingHidden string false Accessible hidden text to provide context to the heading
itemsList Array<itemsList> true An array of list item links

itemsList

Name Type Required Description
url string true URL that contains the id of the content heading to link to
text string true Text to display for the list item
{% macro onsTableOfContents(params) %}
  {% from "components/lists/_macro.njk" import onsList %}
  {% from "components/skip-to-content/_macro.njk" import onsSkipToContent %}
  
  {% if params.skipLink is defined and params.skipLink %}
    <aside class="ons-toc-container" role="complementary">
      {{
          onsSkipToContent({
              "url": params.skipLink.url,
              "text": params.skipLink.text
          })
      }}
    {% endif %}

    <nav class="ons-toc" aria-label="{{ params.ariaLabel | default('Table of contents') }}">
      <h2 class="ons-toc__title ons-u-fs-r--b ons-u-mb-s">{{ params.title }}</h2>
      {% if params.lists is defined and params.lists %}
        {% for list in params.lists %}
          {% if list.listHeading is defined and list.listHeading %}
            <h3 class="ons-u-fs-r ons-u-mb-xs">{{ list.listHeading }}<span class="ons-u-vh"> {{ list.listHeadingHidden }}</span>:</h3>
          {% endif %}
          {{
              onsList({
                  "element": 'ol',
                  "classes": 'ons-u-mb-m',
                  "variants": 'dashed',
                  "itemsList": list.itemsList
              })
          }}
        {% endfor %}
      {% elif params.itemsList is defined and params.itemsList %}
        {{
            onsList({
                "element": 'ol',
                "classes": 'ons-u-mb-m',
                "variants": 'dashed',
                "itemsList": params.itemsList
            })
        }}
      {% endif %}
    </nav>
  </aside>
{% endmacro %}
.ons-toc {
  &-container {
    border-bottom: 1px solid $color-grey-15;
    margin-bottom: 2rem;
    padding-bottom: 1rem;
  }

  &__link-active {
    color: $color-text-link-hover;
    text-decoration: underline solid $color-text-link-hover 2px;
  }
}

Help improve this component

Let us know how we could improve this component or share your user research findings. Discuss ‘Table of contents’ component on GitHub