FairGarden Design

FAQ

The FAQ module: 4–15 independent questions and answers in a ruled Base UI Accordion, under a chapter-stack header.

import {
  FAQ,
  FAQContact,
  FAQHeader,
  FAQIntro,
  FAQItem,
  FAQList,
  FAQTitle,
} from '@fairgarden-private/design/components/FAQ'

Questions are h3 item heads in the display serif; answers are text serif within the reading measure. The first rule is the strong top under the header, and every item closes with a rule. Each question trails the LTA disclosure glyph, which points inward when its answer is open. Several answers may be open at once, and every answer prints expanded. Don't use an FAQ for essential content or for steps, and never open items on hover.

Ruled and barred

Ruled and barred

kind="ruled" is the default. barred adds a --ds-stroke-3 bar at the start of each open item's row, a bolder line and never a fill. kind="keyed" leads each row with a block subject icon (FAQItem icon) and indents the answers; kind="split" puts the header in the left third of the page grid from 1024 px.

Visiting the preserves

Answers to the questions we hear most at the trailhead.

Yes, on a leash no longer than six feet. Please pack out what your dog leaves behind.

Still have a question? Write to the stewardship team.

A legal agreement that limits development on land while it stays in private hands.

FAQVariants.tsx
import {
  FAQ,
  FAQContact,
  FAQHeader,
  FAQIntro,
  FAQItem,
  FAQList,
  FAQTitle,
} from '@fairgarden-private/design/components/FAQ'
import { Link } from '@fairgarden-private/design/components/Link'
import styles from './variants.module.css'

/** The ruled FAQ (default) with header, intro and contact line, and the barred form. */
export function FAQVariants() {
  return (
    <div className={styles.stack}>
      <FAQ>
        <FAQHeader>
          <FAQTitle>Visiting the preserves</FAQTitle>
          <FAQIntro>Answers to the questions we hear most at the trailhead.</FAQIntro>
        </FAQHeader>
        <FAQList defaultValue={['dogs']}>
          <FAQItem value="dogs" question="Can I bring my dog?">
            Yes, on a leash no longer than six feet. Please pack out what your dog leaves behind.
          </FAQItem>
          <FAQItem value="fees" question="Is there an entry fee?">
            No. Every FairGarden preserve is free and open from dawn to dusk.
          </FAQItem>
          <FAQItem value="bikes" question="Are bikes allowed on the trails?">
            Only on the gravel service roads, marked with a green post at each junction.
          </FAQItem>
          <FAQItem value="groups" question="Can our school group book a guided walk?">
            Yes. Walks run April to October for groups of up to 30.
          </FAQItem>
        </FAQList>
        <FAQContact>
          Still have a question? <Link href="#contact">Write to the stewardship team</Link>.
        </FAQContact>
      </FAQ>

      <FAQ barred>
        <FAQList defaultValue={['easement']}>
          <FAQItem value="easement" question="What is a conservation easement?">
            A legal agreement that limits development on land while it stays in private hands.
          </FAQItem>
          <FAQItem value="taxes" question="Does an easement change my property taxes?">
            It can lower them. Talk to your assessor before you sign.
          </FAQItem>
          <FAQItem value="sell" question="Can I still sell the land?">
            Yes. The easement travels with the deed to every future owner.
          </FAQItem>
        </FAQList>
      </FAQ>
    </div>
  )
}

Primary and secondary

Primary and secondary

primary recolors rules, questions, glyph and answers. secondary drives only the accent underlines: links inside answers and on the contact line, and a question's hover underline.

Membership

Each spring. Renew online any time after March 1.

FAQColor.tsx
import {
  FAQ,
  FAQContact,
  FAQHeader,
  FAQIntro,
  FAQItem,
  FAQList,
  FAQTitle,
} from '@fairgarden-private/design/components/FAQ'
import { Link } from '@fairgarden-private/design/components/Link'
import styles from './color.module.css'

/** `primary` drives rules, questions and glyph; `secondary` only the link underlines in answers. */
export function FAQColor() {
  return (
    <div className={styles.stack}>
      <FAQ primary="slate" secondary="indigo">
        <FAQHeader>
          <FAQTitle>Membership</FAQTitle>
        </FAQHeader>
        <FAQList defaultValue={['renew']}>
          <FAQItem value="renew" question="When does my membership renew?">
            Each spring. <Link href="#renew">Renew online</Link> any time after March 1.
          </FAQItem>
          <FAQItem value="gift" question="Can I give a membership as a gift?">
            Yes, with a printed card mailed to the recipient.
          </FAQItem>
        </FAQList>
      </FAQ>
    </div>
  )
}

On grounds

On paper and forest

On saturated grounds keep an FAQ to five items with answers of 40 words or fewer.

paper

Yes, except after ice storms.

forest

Yes, except after ice storms.

FAQGrounds.tsx
import {
  FAQ,
  FAQContact,
  FAQHeader,
  FAQIntro,
  FAQItem,
  FAQList,
  FAQTitle,
} from '@fairgarden-private/design/components/FAQ'
import { PresetGround } from '@/components/PresetGround'
import styles from './grounds.module.css'

const presets = ['paper', 'forest'] as const

/** Light and deep grounds both hold an FAQ; the rules and glyph re-resolve per ground. */
export function FAQGrounds() {
  return (
    <div className={styles.row}>
      {presets.map((preset) => (
        <PresetGround key={preset} preset={preset} className={styles.face}>
          <p className={styles.name}>{preset}</p>
          <FAQ>
            <FAQList defaultValue={['open']}>
              <FAQItem value="open" question="Are the trails open in winter?">
                Yes, except after ice storms.
              </FAQItem>
              <FAQItem value="restrooms" question="Are there restrooms?">
                At the north lot, May to October.
              </FAQItem>
            </FAQList>
          </FAQ>
        </PresetGround>
      ))}
    </div>
  )
}

API Reference

FAQ

The FAQ module: FAQHeader (with FAQTitle and an optional FAQIntro), FAQList holding FAQItems, and an optional closing FAQContact. Place it on light or deep grounds; on saturated grounds keep to five items with answers of 40 words or fewer. Every answer prints expanded.

PropTypeDescription
barred
boolean | null | undefined

Shows a --ds-stroke-3 --primary12 bar at the start of each open item’s row: a bolder line, never a fill. Default false.

kind
'ruled' | 'keyed' | 'split' | null | undefined

ruled (default): bare rows between --role-rule rules. keyed: a leading block icon per row (FAQItem icon), answers indented to the question column. split: from 1024 px of viewport, header and intro in the left third and the list in the right two thirds.

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: rules, questions, glyph and answers. 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: only the link underlines inside answers and on the contact link (--role-accent). Never defaulted.

FAQHeader, FAQTitle and FAQIntro

The module header: the chapter-stack title, then the optional intro (§3.5 Recipe A).

The section head: type-h2 in --role-heading. Renders <h2>.

An optional lede under the title, within --ds-measure-reading.

FAQList and FAQItem

The item list, a Base UI Accordion Root. Several items may be open at once (multiple, default true); closed answers stay in the DOM (hiddenUntilFound, default true) for find-in-page and print. Never open items by hover.

One question and its answer. The question is an h3 wrapping the trigger; the disclosure glyph trails it, top-aligned to its first line. The answer is type-body, within --ds-measure-reading. FAQ items are never disabled.

PropTypeDescription
icon
| 'menu'
| 'search'
| 'circle'
| 'arrow_forward'
| 'arrow_upward'
| 'expand_more'
| 'close'
| 'remove'
| 'add'
| 'check'
| 'chevron_right'
| 'chevron_left'
| 'more_horiz'
| 'play_arrow'
| 'pause'
| 'download'
| 'zoom_in'
| 'zoom_out'
| 'recenter'
| 'help'
| 'mail'
| undefined

The leading block icon of the keyed kind (§6.10 subject symbol, Material Symbols Rounded, block tier). Ignored by the other kinds.

panelProps
Omit<AccordionPanelProps, 'children'> | undefined

Props for the answer panel, such as keepMounted.

question
React.ReactNode | undefined

The question: type-itemhead in --primary12, inside an h3.

FAQContact

The optional closing contact line, e.g. “Still stuck? Write to us.” with a Link.

Additional types

faq
type faq = faq
FAQContactProps

Props for FAQContact: paragraph props and render.

type FAQContactProps = useRender.ComponentProps<'p'>
FAQHeaderProps

Props for FAQHeader: header props and render.

type FAQHeaderProps = useRender.ComponentProps<'header'>
FAQIntroProps

Props for FAQIntro: paragraph props and render.

type FAQIntroProps = useRender.ComponentProps<'p'>
FAQItemProps

Props for FAQItem: Base UI Accordion Item props (without disabled) plus the question.

type FAQItemProps = {
  /** The question: `type-itemhead` in `--primary12`, inside an `h3`. */
  question: React.ReactNode;
  /**
   * The leading block icon of the `keyed` kind (§6.10 subject symbol,
   * Material Symbols Rounded, block tier). Ignored by the other kinds.
   */
  icon?:
    | 'menu'
    | 'search'
    | 'circle'
    | 'arrow_forward'
    | 'arrow_upward'
    | 'expand_more'
    | 'close'
    | 'remove'
    | 'add'
    | 'check'
    | 'chevron_right'
    | 'chevron_left'
    | 'more_horiz'
    | 'play_arrow'
    | 'pause'
    | 'download'
    | 'zoom_in'
    | 'zoom_out'
    | 'recenter'
    | 'help'
    | 'mail';
  /** Props for the answer panel, such as `keepMounted`. */
  panelProps?: Omit<AccordionPanelProps, 'children'>;
}
FAQListProps

Props for FAQList: Base UI Accordion Root props.

type FAQListProps<Value = unknown> = AccordionRoot.Props<Value>
FAQProps

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

type FAQProps = React.ClassAttributes<HTMLElement> &
  React.HTMLAttributes<HTMLElement> &
  __type & {
    kind?: 'ruled' | 'keyed' | 'split' | null;
    barred?: boolean | null;
    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;
  }
FAQTitleProps

Props for FAQTitle: heading props and render (default <h2>).

type FAQTitleProps = useRender.ComponentProps<'h2'>

Specification: DESIGN-SYSTEM.md §12.5 (FAQ) and §10.13 (accordion).