FairGarden Design

Footer

The page's heaviest band and closing summary, required on every page: action blocks, the sitemap, contact, social links, the legal row and the colophon. It absorbs everything other systems float: back to top, accessibility settings, chat and feedback are footer links.

import { Footer, FooterBlock } from '@fairgarden-private/design/components/Footer'

The footer is a band Ground, night by default (slate × green, amber action); never a field. Page ground always lies above it, so no field touches it except a straddle card crossing its seam (straddle). On a dark page Ground draws its top seam as a one-ink rule. sitemap takes the navigation data, one group per top-level section, so the footer and the navigation panels never drift apart; a section with an href gets a linked heading with a chevron.

Field guide footer

FooterBlocks stack at base and run 3-up from 944 px of their row. Sitemap links have no rest underline, stand 44 px apart and take the link hover ink only; the current page carries a 3 px start bar and bold. Contact links are underlined at rest. The legal row sits above a rule, stacked at base with © last and on one line from 768 px (© left, links right). From 1024 px the brand, contact and social take columns 1–4 and the sitemap 5–12, its groups 4-up.

FooterGuide.tsx
import { Button } from '@fairgarden-private/design/components/Button'
import { Footer, FooterBlock } from '@fairgarden-private/design/components/Footer'
import { sitemap } from './footerSitemap'
import styles from './guide.module.css'

/** A 20 px outline social mark, drawn custom in currentColor. */
function Mark() {
  return (
    <svg viewBox="0 0 20 20" aria-hidden="true" focusable="false" className={styles.mark}>
      <rect x="2.75" y="2.75" width="14.5" height="14.5" rx="4" />
      <circle cx="10" cy="10" r="3.5" />
    </svg>
  )
}

/** The field-guide footer on the night band: blocks, the sitemap from the navigation data, contact, legal and colophon. */
export function FooterGuide() {
  return (
    <div className={styles.frame}>
      <Footer
        brand={
          <a href="/" className={styles.logo}>
            FairGarden
          </a>
        }
        blocks={
          <>
            <FooterBlock
              icon="help"
              heading="Talk to a Land Trust"
              action={
                <Button variant="outline" render={<a href="/find" />} nativeButton={false}>
                  Find One Near You
                </Button>
              }
            >
              <p>Local experts can walk your land with you and explain your options.</p>
            </FooterBlock>
            <FooterBlock
              icon="download"
              heading="Get the Guide"
              action={
                <Button variant="outline" render={<a href="/guide" />} nativeButton={false}>
                  Download the Guide
                </Button>
              }
            >
              <p>A plain-language guide to easements, taxes and stewardship.</p>
            </FooterBlock>
            <FooterBlock
              icon="add"
              heading="Support the Work"
              action={
                <Button variant="solid" render={<a href="/donate" />} nativeButton={false}>
                  Donate
                </Button>
              }
            >
              <p>Every gift protects land that can never be made again.</p>
            </FooterBlock>
          </>
        }
        sitemap={sitemap}
        address={
          <>
            1250 H Street NW, Suite 600
            <br />
            Washington, DC 20005
          </>
        }
        contact={[
          { value: 'info@example.org', href: 'mailto:info@example.org' },
          { value: '202 555 0100', href: 'tel:+12025550100' },
        ]}
        social={[
          { label: 'Instagram', href: 'https://instagram.com/example', icon: <Mark /> },
          { label: 'Newsletter', href: '/newsletter' },
        ]}
        legal={[
          { label: 'Privacy', href: '/privacy' },
          { label: 'Accessibility', href: '/accessibility' },
          { label: 'Non-Discrimination', href: '/non-discrimination' },
        ]}
        copyright="© 2026 FairGarden"
        colophon="Set in Fraunces, Source Serif 4, Figtree and IBM Plex Mono. Last updated 22 Sept 2026."
        organization="FairGarden"
        backToTop={{ href: '#top' }}
      />
    </div>
  )
}

Newsletter column, ruled and minimal

Newsletter column, ruled and minimal

With newsletterColumn, from 1024 px the sitemap takes columns 1–7 (the brand above, groups 3-up) and the inline newsletter columns 8–12 behind a vertical rule, which turns horizontal when the column stacks. Above 24 links the groups collapse into an Accordion on phones; at 24 or fewer they stay open. signoff crops a large wordmark in the tint at the band's bottom edge. kind="ruled" sets a full-bleed grid of caps link cells and contact rows on a page ground; kind="minimal" a top rule, the logo, one link line, the legal row and the colophon.

FooterColumn.tsx
import { Footer } from '@fairgarden-private/design/components/Footer'
import { Newsletter } from '@fairgarden-private/design/components/Newsletter'
import { largeSitemap, sitemap } from '../guide/footerSitemap'
import styles from './column.module.css'

/**
 * The newsletter column (sitemap 1–7, newsletter 8–12 from 1024 px) with a
 * sitemap over 24 links, which collapses into an Accordion on phones; then
 * the ruled grid and the minimal footer on page grounds.
 */
export function FooterColumn() {
  return (
    <div className={styles.stack}>
      <Footer
        newsletterColumn
        brand={<span className={styles.logo}>FairGarden</span>}
        sitemap={largeSitemap}
        newsletter={
          <Newsletter
            heading="Get the Latest Conservation News"
            pitch="Stories from the land, once a month."
            printUrl="example.org/newsletter"
          />
        }
        newsletterUrl="example.org/newsletter"
        legal={[{ label: 'Privacy', href: '/privacy' }]}
        copyright="© 2026 FairGarden"
        signoff
        wordmark="FairGarden"
      />
      <Footer
        kind="ruled"
        sitemap={sitemap}
        contact={[
          { label: 'Email', value: 'hello@example.org', href: 'mailto:hello@example.org' },
          { label: 'Phone', value: '202 555 0100', href: 'tel:+12025550100' },
        ]}
        copyright="© 2026 FairGarden"
      />
      <Footer
        kind="minimal"
        preset="white"
        brand={<span className={styles.logo}>FairGarden</span>}
        sitemap={sitemap}
        legal={[
          { label: 'Privacy', href: '/privacy' },
          { label: 'Terms', href: '/terms' },
        ]}
        copyright="© 2026 FairGarden"
        colophon="Version 0.1.0."
      />
    </div>
  )
}

One navigation object

The footer mirrors the mega panels because both read the same data: a Sitemap (SitemapSections from @fairgarden-private/design/utils/navigation). The Navigation Menu builds its bar from it with NavigationMenuSections, the drawer with its sections prop, and the footer with sitemap. Each section is a bar item, a drawer group and a footer group headed by its label. A link with its own links is a third tier that only the panels show: a caps group in an overview panel, a category in an index panel. description and promo fill the overview panel and featured the index panel's featured bar. The menu and drawer mark current pages by path; the footer has no path, so it reads current from the data.

FooterNavigation.tsx
'use client'

import * as React from 'react'
import { Button } from '@fairgarden-private/design/components/Button'
import { Footer } from '@fairgarden-private/design/components/Footer'
import { NavDrawer, NavDrawerFooter, NavDrawerLink } from '@fairgarden-private/design/components/NavDrawer'
import { NavigationBar } from '@fairgarden-private/design/components/NavigationBar'
import { NavigationMenu, NavigationMenuSections } from '@fairgarden-private/design/components/NavigationMenu'
import { navigation } from './navigation'
import styles from './navigation.module.css'

/**
 * One navigation object feeds the header's menu (from 1024 px), the drawer
 * (below it; narrow the window) and the footer sitemap, so the footer
 * mirrors the mega panels [D187]. The page sits in What We Do › Our programs.
 */
export function FooterNavigation() {
  return (
    <div className={styles.page}>
      <NavigationBar
        logo={<span className={styles.logo}>FairGarden</span>}
        logoLabel="FairGarden home"
        currentPath="/what-we-do/our-programs"
        skipHref="#navigation-demo-content"
        action={
          <Button variant="solid" size="sm" nativeButton={false} render={<a href="/donate" />}>
            Donate
          </Button>
        }
        drawer={
          <NavDrawer
            sections={navigation}
            footer={
              <NavDrawerFooter
                action={
                  <Button variant="solid" size="lg" nativeButton={false} render={<a href="/donate" />}>
                    Donate
                  </Button>
                }
              >
                <NavDrawerLink href="/about">About</NavDrawerLink>
                <NavDrawerLink href="/accessibility">Accessibility settings</NavDrawerLink>
              </NavDrawerFooter>
            }
          />
        }
      >
        <NavigationMenu>
          <NavigationMenuSections sections={navigation} />
        </NavigationMenu>
      </NavigationBar>
      <div id="navigation-demo-content" className={styles.content} tabIndex={-1}>
        Page content starts here.
      </div>
      <Footer
        brand={
          <a href="/" className={styles.footerLogo}>
            FairGarden
          </a>
        }
        sitemap={navigation}
        legal={[
          { label: 'Privacy', href: '/privacy' },
          { label: 'Accessibility', href: '/accessibility' },
        ]}
        copyright="© 2026 FairGarden"
        organization="FairGarden"
        backToTop={{ href: '#navigation-demo-content' }}
      />
    </div>
  )
}

In print

Only the imprint prints, above a 0.75 pt rule: organization, the address, the contact line written out, "Printed on" the date, ©, the colophon and, with the newsletter column, "Newsletter (newsletterUrl)". The sitemap, social links, buttons, forms, sign-off and back-to-top drop.

API Reference

The footer. In print only the imprint prints (organization, address, contact written out, the print date, ©, colophon at 8–8.5 pt); the sitemap, social links, buttons, forms, sign-off and back-to-top drop.

PropTypeDescription
address
React.ReactNode | undefined

The postal address: mixed case, unlabeled lines (one per line).

backToTop
FooterBackToTop | undefined

An in-flow “Back to top” link; never fixed.

blocks
React.ReactNode | undefined

The action blocks: FooterBlocks (icon → heading → body → action).

brand
React.ReactNode | undefined

The logo link or wordmark, at the head of the brand column.

colophon
React.ReactNode | undefined

The colophon, type-small (dates and versions in <data> or <time>): “Set in Fraunces, Source Serif 4, Figtree and IBM Plex Mono. Last updated 22 Sept 2026.”

contact
FooterContactItem[] | undefined

The contact line, middle-dot separated; links underlined at rest.

copyright
React.ReactNode | undefined

The © line, e.g. “© 2026 FairGarden”.

headingLevel
HeadingLevel | undefined

Heading level of the block and group headings. Default 2.

kind
'guide' | 'ruled' | 'minimal' | undefined

guide (default): the field-guide footer on the night band: action blocks → sitemap → contact → legal → colophon. ruled: a page ground opened by the chrome rule, a full-bleed cell grid of caps links and contact rows. minimal: a top rule, the logo, one link line, the legal row and the colophon.

legal
FooterLegalLink[] | undefined

The legal links, beside the ©.

locale
string | undefined

The locale of the printed “Printed on” date. Default en-GB.

newsletter
React.ReactNode | undefined

The inline Newsletter for the newsletter column.

newsletterColumn
boolean | undefined

The sitemap in columns 1–7 and the inline newsletter in 8–12 behind a vertical rule, from --lg-n-above [D187]. Default false.

newsletterUrl
string | undefined

The newsletter’s short URL, printed in the imprint: “Newsletter (example.org/newsletter)".

organization
React.ReactNode | undefined

The organization’s name, first in the printed imprint.

preset
BandPreset | undefined

The footer’s band: night (default for guide, slate × green, amber action), or a page ground (default paper for ruled and minimal). Never a field [D177].

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, rules, rings. Never defaulted; omitted, it inherits the Ground [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: icons, chevrons, contact underlines, link hover. Never defaulted.

signoff
boolean | undefined

A large wordmark in the tint, cropped by the band’s bottom edge. Default false.

sitemap
SitemapSection[] | undefined

The sitemap: the shared navigation data (utils/navigation), one group per top-level section, headed by its label and linked to its href [D187]. Each group lists the section’s links; the mega panels’ third tier (a link’s own links) stays in the panels.

sitemapLabel
string | undefined

The sitemap’s accessible name. Default “Site”.

social
FooterSocialLink[] | undefined

Social links, outline icons or text.

straddle
React.ReactNode | undefined

A straddle Newsletter (kind="straddle") across the footer’s top seam.

wordmark
string | undefined

The sign-off wordmark’s text (decorative; the brand is named elsewhere).

FooterBlock

An action block: icon → heading (--size-px-3) → body (--size-px-7) → action (--size-px-7).

PropTypeDescription
action
React.ReactNode | undefined

The action: a §9.2 Button, links or the §11.11 inline form.

heading
React.ReactNode | undefined

The heading, type-itemhead (an item head, not a section head).

headingLevel
HeadingLevel | undefined

The heading level. Default 2.

icon
| ReactElement
| '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 block-tier icon (36 px, FILL 0): an inventory name, or a subject symbol’s SVG element in currentColor. It takes --role-accent.

children
React.ReactNode | undefined

The body, type-body-ui.

Additional types

footer
FooterBackToTop

Links from the page to its in-flow “Back to top” target (the skip-link target).

type FooterBackToTop = {
  /** The target, e.g. "#main". Focus moves there. */
  href: string;
  /** Default "Back to top". */
  label?: string;
}
FooterBlockProps

Props for FooterBlock: section props, the icon, heading, body and action.

type FooterBlockProps = {
  /**
   * The block-tier icon (36 px, FILL 0): an inventory name, or a subject
   * symbol's SVG element in `currentColor`. It takes `--role-accent`.
   */
  icon?:
    | ReactElement
    | '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';
  /** The heading, `type-itemhead` (an item head, not a section head). */
  heading: React.ReactNode;
  /** The heading level. Default `2`. */
  headingLevel?: HeadingLevel;
  /** The body, `type-body-ui`. */
  children?: React.ReactNode;
  /** The action: a §9.2 Button, links or the §11.11 inline form. */
  action?: React.ReactNode;
}
FooterContactItem

One contact item: middle-dot separated in the contact line.

type FooterContactItem = {
  /** The caps label on the ruled grid's contact rows ("Email"). */
  label?: string;
  /** The text: an address, a number, an email. */
  value: string;
  /** A `mailto:`, `tel:` or page link, underlined at rest. */
  href?: string;
}
FooterLegalLink
FooterProps

Props for Footer: footer props, the kind and its options, the parts’ content and the color axes.

type FooterProps = FooterGuideProps | FooterPageGroundProps
FooterSocialLink
SitemapLink
SitemapSection

One top-level navigation section: a bar item and its panel, a drawer group and a footer sitemap group, whose heading is the section’s label [D187].

type SitemapSection = {
  /** The bar item, drawer row and footer group heading. */
  label: string;
  /**
   * The section's landing page: the `overview` panel's overview link (a
   * `dropdown` panel's first link), the drawer group's first child and the
   * footer group's linked heading. Its path marks the parent of current.
   */
  href?: string;
  /** One line under the `overview` panel's overview link, in `--role-muted`. */
  description?: string;
  /**
   * The section's pages: the `dropdown` list, the `overview` link groups
   * (each link with its own `links`) or the `index` categories; the drawer
   * group's child links; the footer group's links. A section with no links
   * and an `href` is a plain bar link and a direct drawer link.
   */
  links: SitemapLink[];
  /**
   * The desktop panel. Default: `overview` when the section has a
   * `description` or a `promo`, or a link carries its own `links`; else
   * `dropdown` (see `sitemapPanel`).
   */
  panel?: SitemapPanel;
  /** The `overview` panel's optional promo card (≤ 1 per panel). */
  promo?: SitemapPromo;
  /** The `index` panel's featured bar: one link across both panes, at the foot. */
  featured?: SitemapLink;
}

Specification: DESIGN-SYSTEM.md §11.13 (footer; the sitemap and newsletter column per D187), §5.10.2 (module reflow) and §7.12.3 (print).