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.

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.

SituationGuidance
Current page linkKeep as an <a> with aria-current="page". Avoid plain <span> — it breaks keyboard navigation consistency.
DepthShow the full path from root. Truncating intermediate crumbs hides context without saving meaningful space.
PlacementRender above the page <h1>, below the primary navigation bar.
Mobile.breadcrumb uses flex-wrap: wrap so long paths reflow naturally.

Accessibility

AttributeWhereWhy
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::afterGenerated 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

SituationGuidance
FAQs and help contentIdeal fit. Users scan summaries to find the answer they need, then expand only the relevant panel.
Settings panelsGroup related settings under a collapsible heading to reduce visual noise without burying options.
Progressive disclosureUse when secondary details are genuinely optional — not to shorten a page that is already the right length.
Multiple open at onceNative <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 contentDo 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

TokenDefaultControls
--accordion-bordervar(--color-border)Outer border and item dividers
--accordion-radiusvar(--radius-md)Outer corner radius
--accordion-summary-bgtransparentSummary row background (idle)
--accordion-summary-hover-bgvar(--color-bg-panel)Summary row background (hover)
--accordion-panel-bgtransparentPanel content background
--accordion-font-size0.9375remSummary 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.