Navigation
Components that help users orient themselves and move through a site or application. All navigation components are CSS-only at their core — no JavaScript required for the shipped functionality.
Breadcrumbs stable
Shows users where they are within a site hierarchy and lets them navigate back to parent sections. Colors adapt automatically to any surface or theme context.
Full reference — Breadcrumbs
Anatomy
Wrap an <ol> in a <nav> with
aria-label="Breadcrumb". Each list item gets .breadcrumb-item.
The current page's link carries aria-current="page". The ›
separator is generated by CSS — never in the DOM.
<nav aria-label="Breadcrumb">
<ol class="breadcrumb" role="list">
<li class="breadcrumb-item"><a href="/">Home</a></li>
<li class="breadcrumb-item"><a href="/docs">Docs</a></li>
<li class="breadcrumb-item">
<a href="/docs/start" aria-current="page">Getting started</a>
</li>
</ol>
</nav> Usage guidance
Use breadcrumbs when the site has two or more levels of hierarchy and users are likely to have arrived deep in the tree from search, a direct link, or a previous session. Skip them on flat sites, landing pages, or anywhere the hierarchy has fewer than two meaningful levels.
| Situation | Guidance |
|---|---|
| Current page link | Keep as an <a> with aria-current="page". Avoid plain <span> — it breaks keyboard navigation consistency. |
| Depth | Show the full path from root. Truncating intermediate crumbs hides context without saving meaningful space. |
| Placement | Render above the page <h1>, below the primary navigation bar. |
| Mobile | .breadcrumb uses flex-wrap: wrap so long paths reflow naturally. |
Accessibility
| Attribute | Where | Why |
|---|---|---|
aria-label="Breadcrumb" | <nav> | Distinguishes this landmark from the primary navigation when both appear on the page. |
role="list" | <ol> | Restores list semantics in Safari/VoiceOver, which removes them from any <ol> with list-style: none. |
aria-current="page" | Last <a> | Tells screen readers which link represents the current page. |
| CSS separator | ::after | Generated via content: '›' / '' — the empty alt value suppresses screen-reader announcement. |
CSS reference
Shipped in dist/farn-components.css. Colors are semantic — adapt automatically under data-theme and data-surface.
.breadcrumb {
display: flex;
flex-wrap: wrap;
align-items: center;
list-style: none;
padding: 0;
margin: 0;
font-size: 0.875rem;
}
.breadcrumb-item {
display: flex;
align-items: center;
color: var(--color-text-secondary);
}
.breadcrumb-item:not(:last-child)::after {
content: '›'; /* fallback */
content: '›' / ''; /* modern: suppresses screen-reader announcement */
margin: 0 var(--space-xs);
}
.breadcrumb-item a {
color: inherit;
text-decoration: none;
transition: color var(--duration-fast) var(--ease-out);
}
.breadcrumb-item a:hover,
.breadcrumb-item a[aria-current="page"] {
color: var(--color-text);
} Accordion stable
Expandable content panels built on native <details> /
<summary> — no JavaScript required. Animated height in Chrome 131+
via interpolate-size: allow-keywords; instant reveal in all other browsers.
What is Farn?
Farn is a token-first design system built around Iron Night and Birch Mist palettes. It ships three CSS artifacts: a full bundle, a tokens-only build for teams with their own reset, and an opt-in component classes file.
How do I install it?
Install via npm: npm install farn-theme. Then import the CSS bundle that matches your setup — farn-theme for the full bundle or farn-theme/tokens if you manage your own reset.
Does it support dark mode?
Yes. Set data-theme="dark" or data-theme="light" on any element. All semantic tokens update automatically — no additional class toggling required.
Full reference — Accordion
Anatomy
Wrap one or more <details> elements in a .accordion
container. Each <details> must contain exactly one
<summary> and a .accordion-panel div.
<div class="accordion">
<details>
<summary>Question one</summary>
<div class="accordion-panel">
<p>Answer text.</p>
</div>
</details>
<details>
<summary>Question two</summary>
<div class="accordion-panel">
<p>Answer text.</p>
</div>
</details>
</div> Open by default
Add the open attribute to any <details> to render it expanded on load — the standard HTML boolean attribute, no extra classes needed.
<details open>
<summary>Expanded on load</summary>
<div class="accordion-panel">…</div>
</details> Usage guidance
| Situation | Guidance |
|---|---|
| FAQs and help content | Ideal fit. Users scan summaries to find the answer they need, then expand only the relevant panel. |
| Settings panels | Group related settings under a collapsible heading to reduce visual noise without burying options. |
| Progressive disclosure | Use when secondary details are genuinely optional — not to shorten a page that is already the right length. |
| Multiple open at once | Native <details> allows any number of panels open simultaneously. To enforce exclusive-open, close sibling <details> on the toggle event with a small JS listener. |
| Critical content | Do not hide content that every user needs in a closed accordion. Use open for important panels, or a different layout. |
Animation
In Chrome 131+ the panel height animates using interpolate-size: allow-keywords combined with the ::details-content pseudo-element. Older browsers get an instant reveal. The + icon rotates 45° on open. Both transitions are disabled under prefers-reduced-motion: reduce.
Token reference
| Token | Default | Controls |
|---|---|---|
--accordion-border | var(--color-border) | Outer border and item dividers |
--accordion-radius | var(--radius-md) | Outer corner radius |
--accordion-summary-bg | transparent | Summary row background (idle) |
--accordion-summary-hover-bg | var(--color-bg-panel) | Summary row background (hover) |
--accordion-panel-bg | transparent | Panel content background |
--accordion-font-size | 0.9375rem | Summary text size |
CSS reference
Shipped in dist/farn-components.css. All colors are semantic — adapt to data-theme and data-surface automatically.
.accordion {
border: 1px solid var(--accordion-border);
border-radius: var(--accordion-radius);
overflow: hidden;
}
.accordion-panel {
padding: 0 var(--space-md) var(--space-md);
background: var(--accordion-panel-bg);
}
/* Animated height — Chrome 131+ */
@supports (interpolate-size: allow-keywords) {
.accordion { interpolate-size: allow-keywords; }
.accordion details::details-content {
overflow: hidden;
transition: block-size var(--duration-base) var(--ease-out);
}
.accordion details:not([open])::details-content { block-size: 0; }
} Tabs coming soon
Horizontal tabs for switching between views within a single screen. CSS-only active state
via :checked radio inputs; ARIA role="tablist" JS enhancement
documented separately.
Pagination coming soon
Previous/next and numbered page controls. --pagination-* tokens for
active background, active text, and border; .pagination, .page-item,
and .page-link classes with active, hover, focus, and disabled states.