FairGarden Design

Feature Grid

Two to six parallel benefits or ways to take part, each a sentence or two, led by an icon, a numeral or a small drawing.

import {
  FeatureGrid,
  FeatureGridBody,
  FeatureGridCell,
  FeatureGridHeading,
  FeatureGridIcon,
  FeatureGridList,
} from '@fairgarden-private/design/components/FeatureGrid'

Use one leader type per grid, never mixed. Icons are Material Symbols Rounded at the block tier and fill in the accent; never put a colored tile behind them. A FeatureGridLink inside the heading links the whole cell, and the cell draws the focus ring. Don't use a feature grid for measurable attributes (use a spec sheet), and split it into two modules rather than exceed six cells.

Kinds

Kinds

icon is the default. numbered draws zero-padded numerals in outline circles from the list order and separates cells with inset rules, which turn vertical once the cells sit in a row. rule-topped opens each cell with a hairline. framed sets the cells 2 × 2 inside one faced frame, which opens a light face on every ground. The grid stacks below 768 px of its container, runs 3-up from 768 and up to 4-up from 1024. header renders the §11.8 section header above the cells: pass SectionHeader props (the grid is then labelled by its heading) or any node; FeatureGridHeader still takes children.

Visit

Plan your walk

  • Find a trail

    Search 40 preserves by distance, terrain and dogs allowed.
  • Take the map

    Download printable maps that work without a signal.
  • Ask a steward

    Our stewards answer questions within two days.
  1. Pick a date

    Crew days run every second Saturday.
  2. Bring gloves

    We supply tools, water and snacks.
  3. Meet at the kiosk

    We start at nine and finish by noon.
  • Pick a date

    Crew days run every second Saturday.
  • Bring gloves

    We supply tools, water and snacks.
  • Meet at the kiosk

    We start at nine and finish by noon.
  • Quiet

    One sentence on what it means for your visit.
  • Shaded

    One sentence on what it means for your visit.
  • Level

    One sentence on what it means for your visit.
  • Open daily

    One sentence on what it means for your visit.
FeatureGridKinds.tsx
import {
  FeatureGrid,
  FeatureGridBody,
  FeatureGridCell,
  FeatureGridHeading,
  FeatureGridIcon,
  FeatureGridLink,
  FeatureGridList,
} from '@fairgarden-private/design/components/FeatureGrid'
import { Icon } from '@fairgarden-private/design/components/Icon'
import styles from './kinds.module.css'

const ways = [
  { icon: 'search', title: 'Find a trail', body: 'Search 40 preserves by distance, terrain and dogs allowed.' },
  { icon: 'download', title: 'Take the map', body: 'Download printable maps that work without a signal.' },
  { icon: 'help', title: 'Ask a steward', body: 'Our stewards answer questions within two days.' },
] as const

const steps = [
  { title: 'Pick a date', body: 'Crew days run every second Saturday.' },
  { title: 'Bring gloves', body: 'We supply tools, water and snacks.' },
  { title: 'Meet at the kiosk', body: 'We start at nine and finish by noon.' },
]

/** Icon (default), numbered, rule-topped and framed grids. */
export function FeatureGridKinds() {
  return (
    <div className={styles.stack}>
      <FeatureGrid header={{ eyebrow: 'Visit', heading: 'Plan your walk' }}>
        <FeatureGridList>
          {ways.map((way) => (
            <FeatureGridCell key={way.title}>
              <FeatureGridIcon>
                <Icon name={way.icon} size="block" />
              </FeatureGridIcon>
              <FeatureGridHeading>
                <FeatureGridLink href="#kinds">{way.title}</FeatureGridLink>
              </FeatureGridHeading>
              <FeatureGridBody>{way.body}</FeatureGridBody>
            </FeatureGridCell>
          ))}
        </FeatureGridList>
      </FeatureGrid>

      <FeatureGrid kind="numbered">
        <FeatureGridList>
          {steps.map((step) => (
            <FeatureGridCell key={step.title}>
              <FeatureGridHeading>{step.title}</FeatureGridHeading>
              <FeatureGridBody>{step.body}</FeatureGridBody>
            </FeatureGridCell>
          ))}
        </FeatureGridList>
      </FeatureGrid>

      <FeatureGrid kind="rule-topped">
        <FeatureGridList>
          {steps.map((step) => (
            <FeatureGridCell key={step.title}>
              <FeatureGridHeading>{step.title}</FeatureGridHeading>
              <FeatureGridBody>{step.body}</FeatureGridBody>
            </FeatureGridCell>
          ))}
        </FeatureGridList>
      </FeatureGrid>

      <FeatureGrid kind="framed">
        <FeatureGridList>
          {['Quiet', 'Shaded', 'Level', 'Open daily'].map((title) => (
            <FeatureGridCell key={title}>
              <FeatureGridHeading>{title}</FeatureGridHeading>
              <FeatureGridBody>One sentence on what it means for your visit.</FeatureGridBody>
            </FeatureGridCell>
          ))}
        </FeatureGridList>
      </FeatureGrid>
    </div>
  )
}

On paper, forest and leaf

On paper, forest and leaf

glossary pairs small geometric drawings with caps captions. Pure line and type, the grid works on every ground; in saturated fields such as leaf, icons, numerals and drawings take the one ink. Keep grids in saturated fields to four cells of 40 words or fewer.

paper

  • Above

    Higher than the trail.
  • Around

    A loop back to the lot.

forest field

  • Above

    Higher than the trail.
  • Around

    A loop back to the lot.

leaf field

  • Above

    Higher than the trail.
  • Around

    A loop back to the lot.
FeatureGridGrounds.tsx
import {
  FeatureGrid,
  FeatureGridBody,
  FeatureGridCell,
  FeatureGridDrawing,
  FeatureGridHeading,
  FeatureGridList,
} from '@fairgarden-private/design/components/FeatureGrid'
import { Ground } from '@fairgarden-private/design/components/Ground'
import styles from './grounds.module.css'

/** A glossary grid: drawings in the ground's one ink, captions in caps. */
export function FeatureGridGrounds() {
  return (
    <div className={styles.row}>
      <Ground kind="face" preset="paper" className={styles.face}>
        <p className={styles.name}>paper</p>
        <Glossary />
      </Ground>
      <Ground kind="field" preset="forest" className={styles.face}>
        <p className={styles.name}>forest field</p>
        <Glossary />
      </Ground>
      <Ground kind="field" preset="leaf" className={styles.face}>
        <p className={styles.name}>leaf field</p>
        <Glossary />
      </Ground>
    </div>
  )
}

function Glossary() {
  return (
    <FeatureGrid kind="glossary">
      <FeatureGridList>
        <FeatureGridCell>
          <FeatureGridDrawing>
            <svg viewBox="0 0 64 64" strokeWidth="2">
              <path d="M8 56 32 12l24 44z" />
            </svg>
          </FeatureGridDrawing>
          <FeatureGridHeading>Above</FeatureGridHeading>
          <FeatureGridBody>Higher than the trail.</FeatureGridBody>
        </FeatureGridCell>
        <FeatureGridCell>
          <FeatureGridDrawing>
            <svg viewBox="0 0 64 64" strokeWidth="2">
              <circle cx="32" cy="32" r="22" />
            </svg>
          </FeatureGridDrawing>
          <FeatureGridHeading>Around</FeatureGridHeading>
          <FeatureGridBody>A loop back to the lot.</FeatureGridBody>
        </FeatureGridCell>
      </FeatureGridList>
    </FeatureGrid>
  )
}

API Reference

FeatureGrid

The feature grid module: an optional header (header, or a composed FeatureGridHeader), then a FeatureGridList of 2–6 FeatureGridCells. Each cell holds one leader (FeatureGridIcon or FeatureGridDrawing; numbered cells draw their own numeral), a FeatureGridHeading (optionally wrapping a FeatureGridLink) and a FeatureGridBody. Use one leader type per grid, never mixed.

PropTypeDescription
header
React.ReactNode | SectionHeaderProps | undefined

The module header (§11.8), rendered in the header slot before the children: a SectionHeader props object (the section is then labelled by its heading), or any node, such as a SectionHeader element. Omit it to compose FeatureGridHeader yourself.

kind
FeatureGridKind | undefined

icon (default): block icon → serif heading → body, whitespace only. numbered: a zero-padded numeral in a 56 px outline circle, cells separated by inset rules (vertical rules once they sit in a row). rule-topped: each cell opens with a --role-hairline rule. framed: 2 × 2 inside one faced frame. glossary: small drawings with caps captions, no rules.

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: numerals, headings, text, rules and the frame. Never defaulted; omitted, it inherits the scope [D133].

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: the block icons (--role-accent) and the title links’ underline. Never defaulted.

FeatureGridHeader and FeatureGridList

The module header slot: holds the §11.8 SectionHeader (FeatureGrid’s header prop fills it for you); stays with the first cells in print.

The cells’ grid: a ul, or an ol for the numbered kind, whose order the numerals carry. In the framed kind it sits inside the faced frame.

FeatureGridCell

One benefit: leader → heading → body. Numbered cells draw their zero-padded numeral (“01″) first, from the list order. A cell with a FeatureGridLink is linked as a whole.

FeatureGridIcon and FeatureGridDrawing

The block-icon leader: put an <Icon size="block" /> (Material Symbols Rounded, FILL 0) inside. It fills in --role-accent, which resolves to --primary12 on saturated grounds. Never put a colored tile behind it.

The glossary leader: a small geometric drawing (inline SVG, spot size --size-px-9) in --primary12. Fixed size; it never scales.

The cell’s heading: type-itemhead in --primary12; in the glossary kind a type-label caps caption. Renders <h3>.

The cell’s one link, inside FeatureGridHeading: a §9.3 title Link whose hit area stretches over the cell. Hover, focus and press underline the heading; the cell draws the focus ring.

PropTypeDescription
external
boolean | null | undefined

Adds the arrow-open mark and “(external site)” for assistive technology. Default false.

index
boolean | null | undefined

Adds the screen-only visited ✓ used in long indexes, such as reference and archive lists. Default false [D174, D175].

list
boolean | null | undefined

With kind="nav": a list link (nav-panel, footer, drawer and breadcrumb lists), whose hover is --role-link-hover color only, plus the --ds-stroke-1-5 --role-accent underline where that ink is --primary12. Without it, nav is bare navigation text (bar and utility items, page numbers, toolbar links), whose hover is the --border-size-2 --role-accent underline (§9.3) [D181]. Default false.

muted
boolean | null | undefined

Rests in --role-muted instead of --primary12, as a breadcrumb’s ancestors. Hover takes --role-link-hover only (the underline is added where that ink is --primary12); with kind="nav" it replaces the bar item’s bare-text underline (§9.3, §9.8) [D181]. Default false.

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: the link text and focus ring. Never defaulted; omitted, it inherits the scope [D133].

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: the accent underline. Never defaulted; omitted, it inherits the scope.

className
string | undefined

Extra class names, added after the module’s own.

A sentence or two of type-body-ui in --primary12.

Additional types

featureGrid
FeatureGridBodyProps

Props for FeatureGridBody: div props and render.

type FeatureGridBodyProps = useRender.ComponentProps<'div'>
FeatureGridCellProps

Props for FeatureGridCell: list-item props and render.

type FeatureGridCellProps = useRender.ComponentProps<'li'>
FeatureGridDrawingProps

Props for FeatureGridDrawing: span props and render.

type FeatureGridDrawingProps = useRender.ComponentProps<'span'>
FeatureGridHeaderProps

Props for FeatureGridHeader: header props and render.

type FeatureGridHeaderProps = useRender.ComponentProps<'header'>
FeatureGridHeadingProps

Props for FeatureGridHeading: heading props and render (default <h3>).

type FeatureGridHeadingProps = useRender.ComponentProps<'h3'>
FeatureGridIconProps

Props for FeatureGridIcon: span props and render.

type FeatureGridIconProps = useRender.ComponentProps<'span'>
FeatureGridKind

The five cell constructions (§12.6).

type FeatureGridKind = 'icon' | 'numbered' | 'rule-topped' | 'framed' | 'glossary'
FeatureGridLinkProps

Props for FeatureGridLink: Link props except kind, which is always title.

type FeatureGridLinkProps = {
  /** Extra class names, added after the module's own. */
  className?: string;
  /**
   * Primary Radix scale: the link text and focus ring. Never defaulted;
   * omitted, it inherits the scope [D133].
   */
  primary?:
    | 'ruby'
    | 'olive'
    | 'sage'
    | 'slate'
    | 'sand'
    | 'gray'
    | 'mauve'
    | 'brown'
    | 'bronze'
    | 'gold'
    | 'red'
    | 'crimson'
    | 'tomato'
    | 'pink'
    | 'plum'
    | 'indigo'
    | 'iris'
    | 'violet'
    | 'purple'
    | null;
  /**
   * Secondary Radix scale: the accent underline. Never defaulted; omitted,
   * it inherits the scope.
   */
  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;
  /**
   * With `kind="nav"`: a list link (nav-panel, footer, drawer and breadcrumb
   * lists), whose hover is `--role-link-hover` color only, plus the
   * `--ds-stroke-1-5` `--role-accent` underline where that ink is
   * `--primary12`. Without it, `nav` is bare navigation text (bar and
   * utility items, page numbers, toolbar links), whose hover is the
   * `--border-size-2` `--role-accent` underline (§9.3) [D181]. Default
   * `false`.
   */
  list?: boolean | null;
  /**
   * Adds the screen-only visited ✓ used in long indexes, such as reference
   * and archive lists. Default `false` [D174, D175].
   */
  index?: boolean | null;
  /**
   * Adds the arrow-open mark and "(external site)" for assistive technology.
   * Default `false`.
   */
  external?: boolean | null;
  /**
   * Rests in `--role-muted` instead of `--primary12`, as a breadcrumb's
   * ancestors. Hover takes `--role-link-hover` only (the underline is added
   * where that ink is `--primary12`); with `kind="nav"` it replaces the bar
   * item's bare-text underline (§9.3, §9.8) [D181]. Default `false`.
   */
  muted?: boolean | null;
}
FeatureGridListProps

Props for FeatureGridList: list props and render.

type FeatureGridListProps = useRender.ComponentProps<'ul'>
FeatureGridProps

Props for FeatureGrid: section props, render, the kind and the color axes.

type FeatureGridProps = React.ClassAttributes<HTMLElement> &
  React.HTMLAttributes<HTMLElement> &
  __type & {
    kind?: FeatureGridKind;
    primary?:
      | 'ruby'
      | 'olive'
      | 'sage'
      | 'slate'
      | 'sand'
      | 'gray'
      | 'mauve'
      | 'brown'
      | 'bronze'
      | 'gold'
      | 'red'
      | 'crimson'
      | 'tomato'
      | 'pink'
      | 'plum'
      | 'indigo'
      | 'iris'
      | 'violet'
      | 'purple'
      | null;
    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;
    header?: React.ReactNode | SectionHeaderProps;
  }

Specification: DESIGN-SYSTEM.md §12.6 (feature grid) and §5.10.2 (module reflow).