Footer
footer.footer
Footer — the site footer: groups of links, then one line of small print.
Examples
<footer class="footer">
<div class="footer-groups">
<section class="footer-group">
<h2 class="footer-heading">This site</h2>
<nav class="nav nav-vertical nav-bare" aria-label="This site">
<ul class="nav-list">
<li><a class="nav-link" href="#sources">Sources</a></li>
<li><a class="nav-link" href="#about">How it is made</a></li>
<li><a class="nav-link" href="#feed">Feed</a></li>
</ul>
</nav>
</section>
<section class="footer-group">
<h2 class="footer-heading">The data</h2>
<p class="footer-text">Published by the city as open data, downloaded nightly. The
timetable in force is the one on the platform sign.</p>
</section>
<section class="footer-group">
<h2 class="footer-heading">Preferences</h2>
<div class="footer-actions">
<label class="field-label" for="demo-footer-theme">Theme</label>
<select class="select select-sm" id="demo-footer-theme">
<option value="system" selected>System</option>
<option value="light">Light</option>
<option value="dark">Dark</option>
</select>
</div>
</section>
</div>
<div class="footer-bottom">
<p class="footer-note">Everything happens in your browser: no data leaves this device.</p>
</div>
</footer>The sibling of navbar, at the other end of the page, with the same division of labour: this file is the frame, and the links inside it are the nav component.
The columns are auto-fit with a floor rather than a breakpoint, because a footer has two groups on one site and four on the next. .footer-text is capped at the narrow measure on purpose — the failure mode a footer invites is a paragraph of running prose at the bottom of every page, and the shape of the box makes five lines look as wrong as they read.
Tokens 12
Level 2, declared on .footer itself. Set any of them on that selector to restyle this component without touching the skin.
| Token | Default |
|---|---|
| --cluster-align | stretch |
| --cluster-space | var(--space-2) var(--space-5) |
| --footer-bg | transparent |
| --footer-border-color | var(--color-border-subtle) |
| --footer-color | var(--color-on-surface-muted) |
| --footer-column-size | 12rem |
| --footer-font-size | var(--font-size-sm) |
| --footer-gap | var(--space-10) |
| --footer-heading-color | var(--color-on-surface) |
| --footer-heading-font-size | var(--font-size-xs) |
| --footer-links-font-size | var(--footer-font-size) |
| --footer-padding-block | var(--space-12) |
Variants and states
Variants
.footer-flush
Inside it
.footer-actions.footer-bottom.select.btn.footer-group.footer-groups.footer-heading.footer-links.footer-note.footer-text.input.lang-switch
State it reads
None.
Before you ship it
What you have to do 5 requirements
- Give each group's
<nav>anaria-label. Two or three navigation landmarks in one footer are announced as "navigation, navigation, navigation" without one. Reuse the visible heading's text. - Use real headings.
.footer-headingstyles an<h2>; it does not make a<div>into one. A footer's groups are sections of the document, and a screen-reader user navigating by heading is how they get skipped or found. - Put a column inside it. This component has no
padding-inline, the same asheroandband: a full-bleed strip that indented its own paint away from the window edge would be the one thing a full-bleed strip must not do. Wrap the contents in acenter— otherwise the small print starts at the very edge of the screen, and on a wide window it sits 96px to the left of the column it is closing. - Use
frameon the<body>, and do not make it a flex column. This used to say the opposite: «the footer carriesmargin-block-start: auto, so a flex-column<body>withmin-block-size: 100dvhis all that is left to write». Both halves of that are wrong since 19 August 2026, and the sentence was teaching the defect it caused — a flex container collapses nothing, so the page's two boundaries added their margins instead of resolving them, measured at 523px where the design asks for 100. The footer reaches the bottom of a short page withposition: stickyinstead, which stays in normal flow.frameisdisplay: flow-rootwithmin-block-size: 100svb, and it is the containing block this footer's100svboffset is measured against — soframeis not optional decoration here, it is the other half of the mechanism. - Turn the gap off where the footer must sit right after the content, with
margin-block-start: 0. Inside a card or a panel, «a page's worth of air above the small print» is not what you mean. It is a real length now rather thanauto, so unlike before it is not a no-op in normal flow and this comes up more often than it used to.
src/css/components/footer.css · npx mostlycss add footer