FairGarden Design

Quote

Testimonials, pull quotes, blockquotes and epigraphs, composed as a figure with a blockquote and a figcaption.

import {
  Quote,
  QuoteAttribution,
  QuoteName,
  QuoteRole,
  QuoteText,
} from '@fairgarden-private/design/components/Quote'

Quotes set in type-quote, the display italic, and attributions in caps sans: the name over the role, never joined by a pipe. Every part is the scope's one ink. Use curly quotes, keep testimonials to 40 words, and never fabricate or anonymize an attribution. A quote is never a heading, and quotes never go in a carousel.

Kinds

Kinds

kind is required. band centers a testimonial that sits alone on its ground. framed draws a 2 px outline with a heavy open-quote glyph and takes an optional 40 px QuotePortrait. pull closes with a short rule and has no attribution; add duplicate when it repeats the article's text, which renders an aside hidden from assistive technology and drops it from print. block indents a quoted passage behind a 3 px rule; a linked QuoteSource holds a Link. epigraph is one or two lines of display bold italic with no marks.

“I came for the owls and stayed for the people who count them.”

Ana DíazVolunteer since 2019

The crew days are the best three hours of my month.

BOBen OkaforTrail crew lead

“A meadow is a slow argument with the forest.”

Burning in late winter keeps woody shrubs back and lets the prairie grasses take the light first.

Prairie Management Handbook

Every trail is a promise to come back.

QuoteKinds.tsx
import { AvatarFallback } from '@fairgarden-private/design/components/Avatar'
import { Link } from '@fairgarden-private/design/components/Link'
import {
  Quote,
  QuoteAttribution,
  QuoteName,
  QuotePortrait,
  QuoteRole,
  QuoteSource,
  QuoteText,
} from '@fairgarden-private/design/components/Quote'
import styles from './kinds.module.css'

/** Band and framed testimonials, a pull quote, a blockquote and an epigraph. */
export function QuoteKinds() {
  return (
    <div className={styles.stack}>
      <Quote kind="band">
        <QuoteText>
          <p>“I came for the owls and stayed for the people who count them.”</p>
        </QuoteText>
        <QuoteAttribution>
          <QuoteName>Ana Díaz</QuoteName>
          <QuoteRole>Volunteer since 2019</QuoteRole>
        </QuoteAttribution>
      </Quote>

      <Quote kind="framed">
        <QuoteText>
          <p>The crew days are the best three hours of my month.</p>
        </QuoteText>
        <QuoteAttribution>
          <QuotePortrait>
            <AvatarFallback>BO</AvatarFallback>
          </QuotePortrait>
          <QuoteName>Ben Okafor</QuoteName>
          <QuoteRole>Trail crew lead</QuoteRole>
        </QuoteAttribution>
      </Quote>

      <Quote kind="pull">
        <QuoteText>
          <p>“A meadow is a slow argument with the forest.”</p>
        </QuoteText>
      </Quote>

      <Quote kind="block">
        <QuoteText>
          <p>
            Burning in late winter keeps woody shrubs back and lets the prairie grasses take the
            light first.
          </p>
        </QuoteText>
        <QuoteAttribution>
          <QuoteSource>
            <Link href="#kinds">Prairie Management Handbook</Link>
          </QuoteSource>
        </QuoteAttribution>
      </Quote>

      <Quote kind="epigraph">
        <QuoteText>
          <p>Every trail is a promise to come back.</p>
        </QuoteText>
      </Quote>
    </div>
  )
}

On paper, forest and clay

On paper, forest and clay

Inside a deep field such as forest the frame and text turn light on their own. Saturated fields such as clay hold the band only, in their one ink.

paper

Worth every early start.

Mei LinMember

forest field

Worth every early start.

Mei LinMember

clay field

“Worth every early start.”

Mei Lin
QuoteGrounds.tsx
import { Ground } from '@fairgarden-private/design/components/Ground'
import {
  Quote,
  QuoteAttribution,
  QuoteName,
  QuoteRole,
  QuoteText,
} from '@fairgarden-private/design/components/Quote'
import styles from './grounds.module.css'

/** Framed on a page ground and a deep field; saturated fields hold the band only, in one ink. */
export function QuoteGrounds() {
  return (
    <div className={styles.row}>
      <Ground kind="face" preset="paper" className={styles.face}>
        <p className={styles.name}>paper</p>
        <FramedQuote />
      </Ground>
      <Ground kind="field" preset="forest" className={styles.face}>
        <p className={styles.name}>forest field</p>
        <FramedQuote />
      </Ground>
      <Ground kind="field" preset="clay" className={styles.face}>
        <p className={styles.name}>clay field</p>
        <Quote kind="band">
          <QuoteText>
            <p>“Worth every early start.”</p>
          </QuoteText>
          <QuoteAttribution>
            <QuoteName>Mei Lin</QuoteName>
          </QuoteAttribution>
        </Quote>
      </Ground>
    </div>
  )
}

function FramedQuote() {
  return (
    <Quote kind="framed">
      <QuoteText>
        <p>Worth every early start.</p>
      </QuoteText>
      <QuoteAttribution>
        <QuoteName>Mei Lin</QuoteName>
        <QuoteRole>Member</QuoteRole>
      </QuoteAttribution>
    </Quote>
  )
}

Placement and print

The hanging pull quote and 2-up framed testimonials are page-grid placements from 1024 px of viewport, set by the page template; the component has no container. In print the frame prints at 1.5 pt and the portrait in grayscale.

API Reference

Quote

The quote: a figure holding QuoteText (the blockquote) and, for testimonials, a QuoteAttribution (figcaption) with QuoteName, QuoteRole, an optional QuotePortrait and an optional QuoteSource. Keep band and framed testimonials to 40 words; never put quotes in a carousel.

PropTypeDescription
duplicate
boolean | undefined

The pull quote repeats text from the article: it renders as an aside hidden from assistive technology and is hidden in print [D174]. Default false: a pull quote with unique text is a figure and prints.

kind
'pull' | 'band' | 'framed' | 'block' | 'epigraph'

A pull quote: type-quote followed by a short rule, no attribution.

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: text, mark, rules, frame and attribution. 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 a linked source’s underline (--role-accent). Never defaulted.

QuoteText

The quoted words, a blockquote (pass cite for a source URL): type-quote for band, framed and pull; type-body behind the left rule for block; display bold italic for epigraph. Curly quotes only; pull quotes hang their punctuation. A pull quote closes with its short rule.

QuoteAttribution, QuoteName and QuoteRole

Who said it, --size-px-5 below the quote: QuotePortrait, then QuoteName over QuoteRole (a line break, never a pipe), then an optional QuoteSource.

The person’s name: type-label caps in --primary12, on its own line.

Role or affiliation under the name: type-caption in --primary12.

QuotePortrait and QuoteSource

The portrait: an Avatar at 40 px (md) with its --role-rule ring, taking the quote’s primary and secondary unless given its own. Compose AvatarImage (empty alt, since the name is adjacent) and AvatarFallback inside. Prints grayscale, at most 20 mm.

PropTypeDescription
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 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, accepted for the shared contract; no part uses it.

size
'xs' | 'sm' | 'md' | 'lg' | 'xl' | null | undefined

xs 24, sm 32, md 40 (default), lg 64, xl 128 px. Up to 40 px the ring is --border-size-1 --role-rule; from 64 px it is --border-size-2 --primary12. Sizes are fixed at every width.

The source title in a cite (type-caption, upright). Link it with a Link inside, which owns the --role-accent underline and its states.

Additional types

quote
type quote = quote
QuoteAttributionProps

Props for QuoteAttribution: figcaption props and render.

type QuoteAttributionProps = useRender.ComponentProps<'figcaption'>
QuoteKind

The five quote builds (§12.8).

type QuoteKind = 'band' | 'framed' | 'pull' | 'block' | 'epigraph'
QuoteNameProps

Props for QuoteName: span props and render.

type QuoteNameProps = useRender.ComponentProps<'span'>
QuotePortraitProps

Props for QuotePortrait: Avatar props; size defaults to md.

type QuotePortraitProps = AvatarProps
QuoteProps

Props for Quote: figure props, render, the kind (and duplicate) and the color axes.

type QuoteProps =
  | (React.ClassAttributes<HTMLElement> &
      React.HTMLAttributes<HTMLElement> &
      __type & { kind: 'pull'; duplicate?: boolean } & {
        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;
      })
  | (React.ClassAttributes<HTMLElement> &
      React.HTMLAttributes<HTMLElement> &
      __type & { kind: 'band' | 'framed' | 'block' | 'epigraph'; duplicate?: undefined } & {
        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;
      })
QuoteRoleProps

Props for QuoteRole: span props and render.

type QuoteRoleProps = useRender.ComponentProps<'span'>
QuoteSourceProps

Props for QuoteSource: cite props and render.

type QuoteSourceProps = useRender.ComponentProps<'cite'>
QuoteTextProps

Props for QuoteText: blockquote props and render.

type QuoteTextProps = useRender.ComponentProps<'blockquote'>

Specification: DESIGN-SYSTEM.md §12.8 (testimonial and quote).