FairGarden Design

Breadcrumb

Where the page sits in the hierarchy: one line of caps crumbs, the LTA staircase in the section bar, or a single parent link.

import { Breadcrumb } from '@fairgarden-private/design/components/Breadcrumb'

Pass the ancestors root first as items and the page's own title as current. The current page comes last, unlinked, heavier and marked aria-current="page"; ancestors are --role-muted at weight 400, and thin chevrons separate them. Author labels in sentence case: type-label sets the caps. Ancestors are Link kind="nav" muted: on hover only their ink changes, to --role-link-hover (underlined where that ink is the text ink), and they share the Link's press and focus ring. A level with no href has no page and shows as plain text. renderLink builds each anchor, for example (href) => <NextLink href={href} />. Use a breadcrumb on pages two or more levels deep, never as a history trail.

Kinds

Inline, staircase and parent

inline (the default) is the in-page breadcrumb, and it reflows on its own width: below 768 px it is the parent link alone; from 768 px it is one line, collapsing the middle into a "…" menu past four levels; from 1024 px it shows up to six. It is an inline-size container, so give it a width in shrink-to-fit layouts. staircase is the section-bar form: below a 768 px viewport the toggle and truncated title open one indented row per level, and from there it is one line (four levels, six from 1024 px) whose crumbs truncate and whose current crumb, in bold, acts as the title. The section bar renders that line with withStaircase={false} and opens the staircase in its own panel. parent is always the parent link. Drag the frame's corner to watch the reflow.

inline

staircase

parent

BreadcrumbKinds.tsx
import { Breadcrumb, type BreadcrumbCrumb } from '@fairgarden-private/design/components/Breadcrumb'
import styles from './kinds.module.css'

/** Six ancestors, one of them a level without a page. */
const path: BreadcrumbCrumb[] = [
  { label: 'Home', href: '#kinds' },
  { label: 'Programs', href: '#kinds' },
  { label: 'Conservation', href: '#kinds' },
  { label: 'Regional' },
  { label: 'Land trusts', href: '#kinds' },
  { label: 'Stewardship', href: '#kinds' },
]

export function BreadcrumbKinds() {
  return (
    <div className={styles.stack}>
      <p className={styles.name}>inline</p>
      <div className={styles.frame}>
        <Breadcrumb items={path} current="Easement monitoring" />
      </div>
      <p className={styles.name}>staircase</p>
      <Breadcrumb kind="staircase" items={path.slice(0, 3)} current="Easement monitoring" />
      <p className={styles.name}>parent</p>
      <Breadcrumb kind="parent" items={path} current="Easement monitoring" />
    </div>
  )
}

On grounds

On page grounds and fields

On a page ground the ancestors and chevrons are muted and the current page takes the text ink. On a deep field the crumbs take the dark scale's paper-colored ink, and where the muted role resolves to the text ink itself (some deep fields and every saturated one), weight and last position tell the current page apart.

paper

tide

forest

leaf

BreadcrumbGrounds.tsx
'use client'

import * as React from 'react'
import { Breadcrumb, type BreadcrumbCrumb } from '@fairgarden-private/design/components/Breadcrumb'
import { Ground } from '@fairgarden-private/design/components/Ground'
import {
  isFieldPreset,
  isPageGroundPreset,
  presets,
  type GroundPreset,
} from '@fairgarden-private/design/utils/scope'
import styles from './grounds.module.css'

const path: BreadcrumbCrumb[] = [
  { label: 'Home', href: '#grounds' },
  { label: 'Guides', href: '#grounds' },
]

/** The first preset that passes `test`, read from the preset table, never named here. */
function firstPreset(test: (preset: GroundPreset) => boolean): GroundPreset | undefined {
  return (Object.keys(presets) as GroundPreset[]).find(test)
}

/** A sample surface: a field preset as an inset field, a page ground as a face. */
function Sample({
  preset,
  className,
  children,
}: {
  preset: GroundPreset
  className: string
  children: React.ReactNode
}) {
  if (isFieldPreset(preset)) {
    return (
      <Ground kind="field" preset={preset} className={className}>
        {children}
      </Ground>
    )
  }
  if (isPageGroundPreset(preset)) {
    return (
      <Ground kind="face" preset={preset} className={className}>
        {children}
      </Ground>
    )
  }
  return null
}

/** A light page ground, a pastel, a deep field and a saturated field. */
const grounds = [
  firstPreset((preset) => presets[preset].tone === 'light-base'),
  firstPreset((preset) => presets[preset].tone === 'tinted'),
  firstPreset((preset) => isFieldPreset(preset) && presets[preset].mode === 'always-dark'),
  firstPreset((preset) => isFieldPreset(preset) && presets[preset].mode === 'always-light'),
].filter((preset): preset is GroundPreset => preset != null)

export function BreadcrumbGrounds() {
  return (
    <div className={styles.stack}>
      {grounds.map((preset) => (
        <Sample key={preset} preset={preset} className={styles.face}>
          <p className={styles.name}>{preset}</p>
          <Breadcrumb kind="staircase" items={path} current="Warblers of the Northeast" />
        </Sample>
      ))}
    </div>
  )
}

In print the breadcrumb is replaced by one plain line of caps, ancestors joined by " / ", the current page omitted and no URLs. Pass printLine={false} where a page masthead prints the path instead.

API Reference

A nav named “Breadcrumb” holding the ancestors in order, then the current page, unlinked and marked aria-current="page". Crumbs are type-label caps; ancestors are --role-muted at --font-weight-4, the current page --primary12 at --font-weight-6, and › chevrons separate them. Crumbs never wrap: the middle collapses into a “…” menu instead. Use it on pages two or more levels deep, never as a history trail.

PropTypeDescription
current
React.ReactNode

The current page’s title: last, unlinked, aria-current="page".

domainGlyph
React.ReactNode | undefined

An optional solid domain glyph at --size-px-3, drawn before the root in --primary12.

items
BreadcrumbCrumb[]

The ancestors, root first. The current page is current, never an item.

kind
'inline' | 'staircase' | 'parent' | null | undefined

inline (default): the in-page breadcrumb, a module that reflows on its own width (parent link only, then one line). staircase: the section-bar breadcrumb, the LTA staircase below --md-n-above and one line from it, the current crumb at --font-weight-7 acting as the title [D184]. parent: always the single “‹ Parent” link.

overflowLabel
string | undefined

Accessible name and tooltip of the “…” overflow button. Default “More levels”.

primary
| 'ruby'
| 'olive'
| 'sage'
| 'slate'
| 'sand'
| 'gray'
| 'mauve'
| 'brown'
| 'bronze'
| 'gold'
| 'red'
| 'crimson'
| 'tomato'
| 'pink'
| 'plum'
| 'indigo'
| 'iris'
| 'violet'
| 'purple'
| null
| undefined

Primary Radix scale: every part’s ink and the focus ring. Never defaulted; omitted, it inherits the scope [D133].

printLine
boolean | undefined

Prints the path as one plain line of caps, ancestors joined by “ / “, current page omitted, no URLs (§7.8) [D80, D105]. Pass false when the page masthead prints the path instead. Default true.

renderCrumb
((href: string) => ReactElement) | undefined

Builds the one line’s ancestor links in place of renderLink; the “…” menu’s items, the staircase rows and the parent link keep renderLink. The §11.7 SectionBar passes a Toolbar link here, so its crumbs join the toolbar’s arrow-key roving.

renderLink
((href: string) => ReactElement) | undefined

Builds each anchor, e.g. (href) => <NextLink href={href} />. Default: a plain <a href>.

secondary
| 'ruby'
| 'olive'
| 'sage'
| 'slate'
| 'sand'
| 'gray'
| 'mauve'
| 'brown'
| 'bronze'
| 'gold'
| 'red'
| 'crimson'
| 'tomato'
| 'pink'
| 'plum'
| 'indigo'
| 'iris'
| 'violet'
| 'purple'
| 'amber'
| 'blue'
| 'cyan'
| 'grass'
| 'green'
| 'jade'
| 'lime'
| 'mint'
| 'orange'
| 'sky'
| 'teal'
| 'yellow'
| null
| undefined

Secondary Radix scale, accepted for the shared contract; the breadcrumb carries no accent.

toggleLabel
string | undefined

Accessible name of the staircase toggle. Default “Page path”.

withStaircase
boolean | undefined

kind="staircase" only: render the staircase’s own Collapsible (toggle, truncated title and rows) below --md-n-above. Pass false when a host reveals the staircase itself, as the §11.7 SectionBar does across its whole bar; the breadcrumb then renders only its one line. Default true.

breadcrumb
BreadcrumbCrumb
BreadcrumbKind
BreadcrumbProps
breadcrumbStairRow
BreadcrumbVariants

Specification: DESIGN-SYSTEM.md §9.8 (breadcrumb), §5.10.2 (reflow) and §7.8 (print), with D80 and D165.