Idaho Golden Retriever Rescue logo
    Idaho Golden Retriever RescueServing all of Idaho
    HomeRescue DogsSurrenderSupport UsAbout Us
    Idaho mountains

    IDGRR Design System

    The colors, type, components, and voice that shape every page of IDGRR.

    On this page

    Get started

    • Overview

    Foundations

    • Color
    • Typography
    • Spacing & Layout
    • Shape & Elevation
    • Responsive
    • Iconography
    • Imagery
    • Motion

    Components

    • Components
    • Navigation
    • Component Examples

    Patterns

    • Page Header
    • Status Tabs
    • Data Toolbar & Layout
    • Form Dialog & Unsaved Changes
    • View Detail Dialog
    • Toasts
    • Empty States
    • Friendly Errors

    Content & voice

    • Mission & Non-negotiables
    • Voice
    • Write like this / Avoid

    Accessibility

    • Accessibility

    Resources

    • For Developers
    • Export Guide

    Overview

    A live mirror of the system that ships in the IDGRR site. Every swatch, button, and component reads from the same tokens used everywhere else — switch themes and the whole page repaints.

    The five rules

    If you only remember five things from this guide.

    1. Use semantic tokens (bg-card, text-amber) — never bg-white or hex.
    2. Amber is the only brand accent. No new colors without adding a token.
    3. Stack vertically by default. Reach for grids only when content is tabular.
    4. Every form field is Label → Control → Hint → Message. No exceptions.
    5. Mobile first, AA contrast, keyboard reachable, motion-safe.

    Jump to a section

    Foundations

    Color, type, spacing, motion — the building blocks.

    Components

    Buttons, inputs, cards, navigation, and dialogs.

    Patterns

    Page headers, status tabs, form dialogs, toasts, and more.

    Content & voice

    Mission, voice, and write-like-this examples.

    Accessibility

    AA contrast, keyboard, motion-safe defaults.

    Resources

    Developer docs and how to export this guide.

    Live, not theoretical

    Every swatch and component on this page reads from the same tokens used everywhere else on the site. Switch themes — it all repaints.

    Who it's for

    Designers, contributors, board members reviewing copy, and developers wiring up a new admin page.

    How to use it

    Use the sidebar, the search above, or the jump cards. Pick a token or component, copy the class names, and ship. Don't fork — extend.
    01

    Foundations

    The atomic building blocks: color, typography, spacing, shape, responsive rules, icons, imagery, and motion.

    ↑ Back to top

    Color

    All color is expressed as HSL CSS variables. Eight themes repaint these tokens — components never reference raw colors.

    Palette at a glance

    Six token families compose every screen. Pick the family first, then reach for a specific token below.

    Brand · Amber

    Primary action, links, focus rings, accent dividers.

    Use for: CTAs, active nav, icon foregrounds.

    Surface

    Page, card, and overlay backgrounds.

    Use for: Layouts, cards, dialogs.

    Ink (Foreground)

    Body copy, secondary text, hairlines.

    Use for: Headings, paragraphs, dividers.

    Feedback

    Errors, warnings, info, success cues.

    Use for: Alerts, toasts, form messages.

    Footer

    Dedicated dark surface for the global footer.

    Use for: Footer only — never on body content.

    Sidebar

    Admin and member portal nav surface.

    Use for: Sidebar shell, active item, hover state.

    Surface & UI tokens

    Background
    --backgroundhsl(—)
    Page canvas
    Foreground
    --foregroundhsl(—)
    Primary body text
    Card
    --cardhsl(—)
    Card surface
    Card Foreground
    --card-foregroundhsl(—)
    Text on cards
    Popover
    --popoverhsl(—)
    Menus, dropdowns
    Primary
    --primaryhsl(—)
    High-contrast UI anchors
    Primary FG
    --primary-foregroundhsl(—)
    Text on primary
    Secondary
    --secondaryhsl(—)
    Soft alternating sections
    Muted
    --mutedhsl(—)
    Low-emphasis fills, inputs
    Muted FG
    --muted-foregroundhsl(—)
    Helper / support text
    Accent
    --accenthsl(—)
    Hover & accent fills
    Border
    --borderhsl(—)
    Hairline dividers
    Input
    --inputhsl(—)
    Form input border
    Ring
    --ringhsl(—)
    Focus ring color
    Destructive
    --destructivehsl(—)
    Errors, destructive actions

    Amber accent system

    Amber
    --amberhsl(—)
    Brand accent, links, active nav
    Amber Hover
    --amber-hoverhsl(—)
    Hover state
    Amber Active
    --amber-activehsl(—)
    Pressed state
    Amber Tint
    --amber-tinthsl(—)
    Soft highlight backgrounds
    Amber Pale
    --amber-palehsl(—)
    Section backgrounds
    Icon BG
    --icon-bghsl(—)
    Pale circle behind feature icons
    CTA BG
    --cta-bghsl(—)
    Donate / amber CTA backgrounds
    CTA FG
    --cta-fghsl(—)
    Text on CTA backgrounds

    Footer tokens

    Footer BG
    --footer-bghsl(—)
    Dark footer surface
    Footer FG
    --footer-fghsl(—)
    Footer body text
    Footer Link
    --footer-linkhsl(—)
    Amber footer link / hover
    Idaho Golden Retriever Rescue

    A foster-based nonprofit serving Idaho's goldens.

    AdoptFosterDonateVolunteerStories

    Sidebar tokens

    Dedicated palette for the admin and member portal sidebars. Mirrors the main token set but tuned for a darker, denser nav surface.

    Sidebar BG
    --sidebar-backgroundhsl(—)
    Admin/member sidebar surface
    Sidebar FG
    --sidebar-foregroundhsl(—)
    Sidebar body text
    Sidebar Primary
    --sidebar-primaryhsl(—)
    Active nav item fill
    Sidebar Primary FG
    --sidebar-primary-foregroundhsl(—)
    Text on active item
    Sidebar Accent
    --sidebar-accenthsl(—)
    Hover row fill
    Sidebar Accent FG
    --sidebar-accent-foregroundhsl(—)
    Text on hover row
    Sidebar Border
    --sidebar-borderhsl(—)
    Sidebar dividers
    Sidebar Ring
    --sidebar-ringhsl(—)
    Sidebar focus ring

    Icon-circle pattern

    Amber icon foreground on a soft --icon-bg circle is the signature feature-icon treatment. The same shape scales from tiny bullets to large hero tiles.

    Bullet dot · 6px
    h-1.5 w-1.5 bg-amber
    About list bullets
    1
    2
    3
    Numbered step · 28px
    w-7 h-7 bg-icon-bg text-amber
    Surrender process steps
    Small · 32px
    w-8 h-8 bg-icon-bg
    About bullet markers
    Medium · 40px
    w-10 h-10 bg-icon-bg
    About info rows, Support tiles
    Standard · 48px
    w-12 h-12 bg-icon-bg
    Adopt, Foster, Newsletter, Donate, FeatureCard
    Large · 56px
    w-14 h-14 bg-icon-bg
    FeatureCard at sm+
    Extra large · 64px
    w-16 h-16 bg-icon-bg
    Contact main card
    Amber tint · bg-amber/10
    w-16 h-16 bg-amber/10
    ApplicationCTA
    Amber tint · bg-amber/15
    w-12 h-12 bg-amber/15
    Membership, Newsletter feature cards

    Always pair amber icon foreground with a soft --icon-bg or amber tint background — never amber-on-white or amber-on-amber.

    Try a theme — swatches above repaint live

    Typography

    System sans-serif stack for performance and familiarity. Bold headings, comfortable body, no display fonts.

    Font stack
    -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif
    Hero XLtext-4xl md:text-6xl font-bold leading-tight

    Give goldens a second chance

    Section XLtext-3xl md:text-4xl font-bold

    Every dog has a story

    Card Titletext-2xl font-bold

    Foster a Golden

    Heading Mtext-xl font-bold

    How adoption works

    Heading Stext-lg font-semibold

    Application status

    Body Largetext-lg font-normal leading-relaxed

    Open your home temporarily — we cover the costs while you provide the love.

    Bodytext-base font-normal leading-relaxed

    Idaho Golden Retriever Rescue (IDGRR) is a 501(c)(3) nonprofit dedicated to the rescue, rehabilitation and placement of surrendered Golden Retrievers.

    Small / Labeltext-sm font-medium

    Login required

    Fine printtext-xs font-normal text-muted-foreground

    © 2026 IDGRR. A 501(c)(3) nonprofit.

    Weights

    Regular 400Medium 500Semibold 600Bold 700

    Inline link

    Body copy can include an inline amber link that underlines on hover. Use sparingly — most navigation belongs in buttons or nav.

    Spacing & Layout

    Tailwind 4-px scale. Generous spacing over dense layouts. Section rhythm anchors the page.

    Spacing scale

    space-14px
    space-28px
    space-416px
    space-624px
    space-832px
    space-1248px
    space-1664px

    Container & gutters

    Content sits inside max-w-6xl with px-4 sm:px-6 lg:px-8 gutters.

    Reading width

    Long-form text caps at max-w-3xl (~768px) so lines stay between 60 and 80 characters. This keeps reading comfortable on every screen — including narrow tablets and split-screen desktop.

    Responsive grid (1 → 2 → 3 → 4)

    Item 1
    Item 2
    Item 3
    Item 4
    Item 5
    Item 6
    Item 7
    Item 8

    grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4

    Padding rhythm

    Canonical inner-padding values used across the site. These are the defaults — only deviate when a specific composition demands it.

    RegionTailwindUsed for
    Card bodyp-6 (sm: p-4 mobile)Default Card content padding
    Card headerpx-6 pt-6 pb-2Title + description region
    Card footerpx-6 pb-6 pt-2Action row at the bottom
    Section verticalpy-12 sm:py-16 lg:py-20Between hero / content blocks
    Label → controlspace-y-2Form field internal stack
    Group → groupspace-y-6Between form sections
    Two-column formgap-5 lg:gap-x-6Field grid gutter
    px-6 pt-6 pb-2
    Card header
    p-6
    Card body
    px-6 pb-6 pt-2
    Card footer

    Card padding regions

    py-12
    Hero block
    sm:py-16
    Content block
    lg:py-20
    Closing CTA

    Section vertical rhythm

    space-y-2
    space-y-2
    ↑ space-y-6 between groups

    Form-field rhythm

    Grid layouts

    Named recipes used across the site. The classes below are the exact strings used in production — copy them straight into a new view.

    Dog / Listing grid
    grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-6
    KPI grid (Admin Dashboard)
    Dogs
    —
    Apps
    —
    Members
    —
    $ MTD
    —
    grid-cols-2 lg:grid-cols-4 gap-4
    Two-column form
    grid-cols-1 lg:grid-cols-2 gap-5 lg:gap-x-6
    Sidebar shell (admin / member)
    Sidebar (16rem)
    Content (1fr)
    grid-cols-[16rem_1fr] · single column < lg
    Footer columns
    About
    Adopt
    Support
    Contact
    grid-cols-2 md:grid-cols-4 gap-8
    Hero + supporting card
    Supporting card

    Sits beside the hero on lg+, stacks below on mobile.

    grid-cols-1 lg:grid-cols-[2fr_1fr] gap-6

    Shape, Border & Elevation

    Soft, friendly rounding. Borders before shadows. Shadows reserved for true elevation.

    Radius

    rounded-sm
    rounded-md
    rounded-lg
    rounded-xl
    rounded-2xl
    rounded-full

    Elevation

    none
    shadow-none
    Default flat surface
    sm
    shadow-xs
    Cards, list rows
    md
    shadow-md
    Hover lift, popovers
    lg
    shadow-lg
    Dropdowns, sheets
    xl
    shadow-xl
    Dialogs, navbar
    2xl
    shadow-2xl
    Auth cards, notifications

    Borders before shadows. Reach for elevation only when a surface needs to read as lifted off the page (overlays, hover states, modals).

    Border

    The default border border-border is a 1px hairline in hsl(var(--border)). Width, direction, and color combine to express emphasis — always preferred over shadows for separation.

    Widths

    1px (default)
    border border-border
    2px (emphasis)
    border-2 border-border
    4px (hero accent)
    border-4 border-border

    Directional

    Top
    border-t
    Right
    border-r
    Bottom
    border-b
    Left
    border-l

    Used widely for sticky-bar bottoms, image strip splitters, and the row-divider pattern border-b border-border last:border-b-0 on form sections, tables, and TOC groups.

    Tinted & state

    Amber/40
    Brand-tinted cards (Non-negotiables)
    Destructive/40
    Avoid / warning cards
    Primary
    High-emphasis selection
    Sidebar Border
    Admin/member nav dividers

    Divider pattern

    First row
    Second row
    Third row
    Last row

    border-b border-border last:border-b-0

    Focus rings

    Rings live in the same stroke family as borders but render outside the element on keyboard focus. The full ring system (--ring, ring-amber, ring-offset-*) is documented in Accessibility.

    Responsive Behavior

    Mobile-first. Layouts collapse stepwise. Touch targets stay generous everywhere.

    BreakpointMin widthIntent
    base0pxSingle column. Hamburger nav. Stacked CTAs.
    sm640pxTwo-column grids appear. Inline padding grows.
    md768pxSidebars and richer multi-column layouts.
    lg1024pxFull desktop nav. 3–4 column card grids.
    xl1280pxContainer hits max width. Wide composition.

    Iconography

    Lucide outline icons. Functional, never decorative overload. Amber on icon-bg circles for feature cards.

    Sizes

    16px
    20px
    24px
    32px

    Common icons

    Heart
    PawPrint
    HandHeart
    House
    Star
    Mail
    Bell
    Shield

    Imagery

    Scenic Idaho paired with golden retrievers. Natural light, hopeful, never glossy or studio-lit.

    Mountain Hero pattern

    Idaho mountains

    Page Hero

    White text over dark overlay

    Full-bleed image · 40–55% black overlay · centered white headline + CTAs.

    Common aspect ratios

    16:9 — hero / video
    4:3 — listing card
    1:1 — avatar / square
    3:4 — portrait

    Treatment

    • Hero images: full-bleed with dark overlay.
    • Feature images: rounded-2xl with light shadow.
    • Dog cards: object-cover at the top of the card.
    • Avatars: circular (rounded-full).

    Motion

    Subtle and helpful. Color ~200ms, transform ~300ms. No parallax, no bounce.

    I just faded in

    Standard entrance for sections and cards.

    Hover lift

    Hover me

    Cards lift gently on hover.

    Duration & easing

    Use caseDurationEasingTailwind / lib
    Color / opacity change150–200msease-outtransition-colors duration-200
    Hover lift / transform250–300msease-outtransition-all duration-300
    Section entrance (fade-in-up)500msease-outframer-motion duration: 0.5
    Dialog / Sheet open200msease-outRadix default
    Skeleton pulse2sease-in-out, ∞animate-pulse

    Do & Don't

    Do
    • Confirm an action (toast, button press).
    • Draw the eye to a state change (badge update, save tick).
    • Soften an entrance (fade-in-up on a section).
    Don't
    • Bounce or overshoot — feels noisy and slow.
    • Parallax or looping decorative animation.
    • Use transitions slower than 500ms on interactive UI.

    Reduced motion

    Honor prefers-reduced-motion: reduce. Framer Motion respects it automatically; for plain CSS transitions, rely on Tailwind's motion-safe: / motion-reduce: modifiers.

    motion-safe:transition-all motion-reduce:transition-none
    02

    Components

    Reusable interface pieces — buttons, inputs, cards, tables, navigation, dialogs — that compose every screen.

    ↑ Back to top

    Components

    Live, interactive examples. Hover, focus, and click — these are the same components used everywhere on the site.

    Buttons — variants

    Buttons — sizes

    BoneDonateButton

    DonateDonateDonate

    Card

    Standard card

    Header, content, and footer regions.

    Cards anchor most content blocks across the site — dog listings, support options, mission cards, profiles.

    Icon feature card

    Amber circle, bold heading, short body, optional text link.

    Form anatomy

    Every form field on the site stacks the same four parts: Label → Control → Description (hint) → Message (error). Admin and member forms wire these to the Form + FormField + FormItem + FormControl + FormMessage primitives from react-hook-form.

    We'll only use this for adoption updates.

    Required for the application signature.

    Enter a 5-digit ZIP code.

    Looks good — we'll text you about meet-and-greets.

    Disabled fields are demonstrated in Component Examples — they remove the field from Tab order and keep the label readable.

    Form controls

    Badges

    Default
    Secondary
    Outline
    Destructive

    Pop-ups & callouts

    Six surfaces for revealing extra context: Tooltip for icon hints, Popover for compact menus, Dialog for blocking tasks, AlertDialog for destructive confirmation, Toast for transient feedback, and Alert for inline status banners.

    Toast guidelines

    import { toast } from "sonner"
    toast.success

    Confirms a completed write the user initiated. Past tense, no punctuation needed.

    • • Saving a form, profile, or settings change
    • • Sending an email, invite, or application
    • • Archiving, restoring, or publishing a record
    • • Copying a link or token to the clipboard

    Example copy

    • "Profile updated"
    • "Application submitted"
    • "Link copied to clipboard"
    toast.error

    Reports a recoverable failure. State what failed, then suggest a next step.

    • • Network or server errors on save / fetch
    • • Permission denied on a non-blocking action
    • • Upload rejected (size, type, quota)
    • • Validation surfaced after submit, not inline

    Example copy

    • "Could not save changes. Try again."
    • "Upload failed — file must be under 5 MB."
    • "Network error. Check your connection."

    Dismissal behavior

    • • Auto-dismiss at ~4s for success and info; ~6s for errors so users have time to read.
    • • Manual dismiss via swipe or the close affordance is always available.
    • • Use duration: Infinity only when paired with an action button (e.g. "Undo").
    • • Never use a toast for a blocking decision (use AlertDialog) or persistent status (use Alert).

    Inline alerts

    Heads up
    Default informational alert. Use for non-urgent notes.
    Action recommended
    Warning variant. Tinted with the brand amber for soft urgency.
    Something went wrong
    Destructive variant. Use sparingly for hard failures.

    Brand callout

    Eligibility tip

    We adopt within Idaho and the immediately surrounding region — applications from farther afield are reviewed case-by-case.

    When to use: highlighting a non-blocking tip or eligibility note inside a longer page.

    Focus ring

    Press Tab to land on this button. Every interactive element shows a visible ring in hsl(var(--ring)).

    Forms & pop-ups accessibility checklist

    Verify forms, dialogs, popovers, and toasts behave correctly without a mouse and with a screen reader running.

    Forms — labels, errors, and aria-live

    • Every input has a programmatic <Label htmlFor> — placeholders are never the only label.
    • Required fields use aria-required="true" and a visible *; the asterisk is decorative (aria-hidden).
    • Hint text uses aria-describedby; error messages add aria-invalid="true" on the input.
    • Inline error messages render inside FormMessage (which is wired to role="alert" / aria-live="polite") so they announce on submit.
    • On submit failure, focus moves to the first invalid field — not just a banner at the top.
    • Async status (saving, success, error) is announced via Sonner toasts (role="status") — never silent.

    Pop-ups — focus traps, escape, return focus

    • Dialog / AlertDialog / Sheet: opening moves focus to the first focusable element inside; Tab cycles inside only (focus is trapped).
    • Esc closes the overlay and returns focus to the triggering element.
    • Background content is inert: it cannot be reached with Tab and is hidden from screen readers (aria-hidden on the rest of the page).
    • Every Dialog/Sheet has a DialogTitle (visible or VisuallyHidden) — Radix throws a console warning otherwise.
    • Popover / Tooltip: dismiss on Esc and on outside click; focus returns to the trigger.
    • Toasts (Sonner): rendered in an aria-live="polite" region; errors use role="alert" so they interrupt politely. Auto-dismiss never strands a focused close button.
    • Inline Alert blocks announce immediately when mounted — verify with a screen reader after a state change, not just on initial load.

    Reduced motion

    • Enable OS-level Reduce motion (macOS: System Settings → Accessibility → Display; Windows: Settings → Accessibility → Visual effects) and reload.
    • Section entrances (fade-in-up) are reduced to a near-instant fade — no upward translate, no parallax.
    • Hover-lift transforms are disabled; opacity / color transitions remain since they don't move pixels.
    • Skeleton pulses still animate (decorative, low amplitude) but no auto-rotating carousels or looping decorative motion play.
    • Use Tailwind's motion-safe: / motion-reduce: modifiers, or a useReducedMotion() guard with framer-motion, for any custom animation.

    Navigation

    Patterns for orienting and moving people through the site — the navbar, sidebars, sub-nav, breadcrumbs, pagination, and footer.

    Top navbar

    IDGRR
    AdoptFosterStoriesSupport

    When to use: every public page. Active link uses amber underline. Sticky at top-0.

    Mobile slide-out

    IDGRR
    ≤ md
    Menu
    • Adopt
    • Foster
    • Stories
    • Support
    • About

    When to use: triggered ≤ md. Built on the Sheet primitive; active row uses bg-amber/10.

    Admin / Member sidebar

    People
    • Members
    • Volunteers
    • Adopters
    Sign out

    When to use: admin and member portals. Uses --sidebar-* tokens; active item fills with bg-sidebar-primary.

    Sub-nav strip

    Adopt
    Foster

    When to use: pairing two closely related pages (Adopt/Foster, Dogs/Memorials). Sticks under the navbar.

    Breadcrumb

    1. Home
    2. Dogs
    3. Buddy

    When to use: deep detail pages where the path back matters. Last item is unlinked and bold.

    Pagination

    When to use: any list view longer than ~30 rows. Current page uses solid amber.

    Footer

    Idaho Golden Retriever Rescue

    A foster-based nonprofit serving Idaho's goldens.

    AdoptFosterDonateVolunteerStories

    When to use: bottom of every public page. Uses the dark --footer-* token set with amber links.

    Navigation accessibility checklist

    Verify every navigation surface — top nav, mobile sheet, sidebar, sub-nav, breadcrumb, pagination, and footer — works keyboard-only and announces correctly.

    Focus order & landmarks

    • Tab order matches visual order: logo → primary links → utilities (theme, auth) → main content → footer.
    • The first focusable element on every page is a "Skip to main content" link that becomes visible on focus and jumps to #main.
    • Each nav region uses the right landmark: <header>, <nav aria-label="Primary">, <aside> for sidebars, <footer>.
    • The current page link uses aria-current="page"; sub-nav active items use aria-current="true". Visual amber underline alone is not enough.
    • Breadcrumbs render inside <nav aria-label="Breadcrumb"> with chevrons marked aria-hidden.
    • Pagination uses <nav aria-label="Pagination">; disabled prev/next are skipped by Tab.

    Keyboard traps — mobile sheet & menus

    • The mobile slide-out (Sheet) traps focus while open: Tab cycles only inside the drawer; the page behind cannot be reached.
    • Esc closes the sheet and returns focus to the hamburger trigger.
    • Body scroll is locked while the sheet is open; closing it restores scroll position.
    • Dropdown menus (account, theme switcher) close on Esc and outside click; arrow keys move between items inside.
    • Collapsible sidebar groups: Enter / Space toggles, and focus stays on the trigger so it can be expanded again without re-Tabbing.
    • No element ever traps focus when it is not the active overlay — verify by tabbing past the navbar to reach page content.

    Route changes & aria-live

    • On client-side route change, focus moves to the new page's <h1> (or main landmark) so screen readers re-announce context.
    • The page <title> is server-rendered per route (each route's head()) so screen-reader title announcements stay accurate.
    • Loading states inside nav regions (e.g., async menu items) sit inside aria-busy="true" containers, not silent skeletons.

    Component Examples

    Real-world combinations of border, elevation, and focus — showing how the primitives compose into the patterns you'll actually build.

    Press Tab through the examples to see focus rings in action, hover to see elevation lift, and try clicking the disabled controls to confirm interaction is blocked.

    HHover — pointer over the element. Shifts color or lifts elevation.
    FFocus — Tab to the element. Shows the ring (keyboard only).
    DDisabled — 50% opacity, no pointer events, skipped by Tab.

    Buttons

    All six variants — flat by design, no elevation. Hover changes background; focus uses the app standard ring-2 ring-ring ring-offset-2.

    Sizes: sm, default, lg, icon.

    State previews — the first row is interactive (hover / Tab to it); the second row is a static preview that always shows the state.

    Hover

    hover:bg-primary/90

    Focus

    focus-visible:ring-2 ring-ring

    Disabled

    disabled:opacity-50

    Disabled button behavior

    • • Tab: skips the disabled button entirely — focus jumps to the next enabled control.
    • • Enter / Space: no-op. The browser blocks click, submit, and key activation.
    • • Screen reader: announces the label plus "dimmed" / "unavailable" via the native disabled attribute.

    Expected announcement

    • • VoiceOver: "Disabled outline, dimmed, button"
    • • NVDA / JAWS: "Disabled outline, button, unavailable"
    • • TalkBack: "Disabled outline, button, disabled"
    BoneDonateButton — brand CTA. Hover to lift; Tab to see ring-amber:
    Donate

    Inputs

    border-input · focus-visible:ring-2 ring-ring

    ring-2 ring-ring ring-offset-2

    disabled:opacity-50 · disabled:cursor-not-allowed

    Same border + ring tokens as Input.

    Disabled input behavior

    • • Tab: skips the disabled field — caret never lands inside it.
    • • Enter / Space: no typing, no form submission triggered from this field.
    • • Screen reader: reads the associated <Label> followed by "dimmed" / "unavailable"; required state is suppressed while disabled.

    Expected announcement

    • • VoiceOver: "Disabled, try clicking, dimmed, edit text"
    • • NVDA / JAWS: "Disabled, try clicking, edit, unavailable, blank"
    • • TalkBack: "Disabled, try clicking, edit box, disabled"

    Cards

    Cards aren't focusable by default. Each example below is wrapped in a focusable region so you can hover for the lift transition and Tab to see the focus ring on the surface itself.

    Default card

    Border + shadow-xs → hover:shadow-md

    The everyday card — used for dog listings, profiles, mission tiles.

    Elevated card

    shadow-lg → hover:shadow-xl

    Use for floating panels, hover-lifted listings, or featured content.

    Brand CTA card

    border-amber/40 → hover:border-amber/70

    The pattern used for amber CTA cards on Adopt and Foster — soft amber border draws the eye without shouting.

    Dialogs

    Both dialogs use border + shadow-lg with a translucent overlay and built-in focus trap. Footer uses the standard grid grid-cols-2 gap-2 equal-width pattern.

    Accessibility Checklist

    Use this checklist to verify the gallery above behaves correctly with assistive technology. No mouse required.

    Keyboard (Tab / Shift+Tab / Enter / Space)

    • Tab cycles through every interactive element in DOM order — no skipped buttons, inputs, or focusable cards.
    • Each focused element shows a visible ring (≥2px, ≥3:1 contrast against its surface).
    • Disabled buttons and inputs are skipped by Tab (not just visually dimmed).
    • Enter activates buttons and links; Space activates buttons and toggles checkboxes.
    • Esc closes the open Dialog and AlertDialog and returns focus to the trigger.

    Hover & pointer states

    • Hover state is never the only signal — every hover change has a matching focus-visible state.
    • Cursor changes to pointer on real interactive elements only (not on decorative wrappers).
    • Hover lift / shadow on cards is mirrored by the focus ring on the same card when reached via Tab.

    Screen reader (VoiceOver / NVDA / TalkBack)

    • Each button announces its accessible name (visible label or aria-label), role, and disabled state when applicable.
    • Inputs announce their associated <Label> text, type, and disabled/required state.
    • Opening a Dialog announces the dialog title and description; focus moves into the dialog.
    • Closing a Dialog returns focus to the triggering element and announces nothing stale.
    • Decorative icons (e.g., the bone in BoneDonateButton) are not announced separately — only the button label.

    How to test

    • macOS VoiceOver: Cmd + F5 to toggle. Use Ctrl + Option + ←/→ to navigate.
    • Windows NVDA: Ctrl + Alt + N to start. Use arrow keys + Tab to navigate.
    • Chrome DevTools: Open the Accessibility panel under Elements to inspect computed name, role, and state for any node.
    03

    Patterns

    How components combine into recognizable, reusable layouts — page headers, status tabs, form dialogs, toasts, and more.

    ↑ Back to top

    Page Header

    Every admin page opens with the same title block — title, optional subtitle, and one right-aligned action.

    Members

    Adopters, fosters, volunteers, and donors — everyone with an account.

    Below the header sits the page's content (table, list, dashboard, form…).

    When to use

    Top of every admin and member portal page.

    One primary action only — secondary actions belong in row menus or toolbars.

    Where it lives

    src/components/shared/PageHeader.tsx

    Used across /admin/* management pages and member-portal screens.

    Status Tabs

    A pill-shaped tab bar with counts. Used to slice a list by lifecycle status — applications, dogs, surrenders, donors.

    Active: pending. Zero-count tabs hide automatically (the active tab always stays visible).

    When to use

    A list with 3–7 mutually-exclusive lifecycle states.

    Counts come from a single query so they stay in sync with the list below.

    Where it lives

    src/components/shared/StatusTabs.tsx

    Adoption Applications, Surrenders, Dogs, Donors, Members, Stories, Memorials, and most other admin lists.

    Data Toolbar & Layout

    The standard search + filters + view-switcher toolbar paired with a view-aware list. Row 1 is search; row 2 is split into three equal thirds: Filter By, Sort By, and Layout By. Card view on mobile is forced; desktop respects the user's last choice.

    Cooper
    Available
    Daisy
    In Foster
    Bear
    Pending

    When to use

    Any admin list where a user might search, sort, filter, or change layout.

    Pair with StatusTabs above the toolbar when there are lifecycle states.

    Where it lives

    src/components/shared/DataToolbarV2.tsx + InlineFilterPanel.tsx + DataLayout.tsx

    Filter open/closed state and view choice persist inlocalStorage per page.

    Form Dialog & Unsaved Changes

    The standardized footer (full-width buttons, primary on the right) plus the discard-changes guard that protects every dirty admin form.

    Type into the field, then try to close — the discard guard intercepts.

    Footer rules

    Two columns, gap-2, both buttons full-width.

    Primary action on the right (Save, Submit, Confirm).

    Secondary on the left (Cancel, Back, Close).

    Destructive confirmations use AlertDialog, notDialog.

    Unsaved-changes guard

    Wire useUnsavedChangesGuard into every form that mutates data. It intercepts in-app navigation and the browser beforeunload event.

    For dialog close, render DiscardChangesDialog when the form is dirty (as shown above).

    src/hooks/useUnsavedChangesGuard.ts ·src/components/shared/DiscardChangesDialog.tsx

    View Detail Dialog

    A read-only modal for inspecting a single record before deciding whether to edit. Optional cover image, label-value rows, and an Edit handoff.

    When to use

    From a list row, when a user wants to scan details without losing list context.

    For full editing, route to a dedicated detail page or open a form dialog.

    Where it lives

    src/components/shared/ViewDetailDialog.tsx exports bothViewDetailDialog and DetailRow.

    Used across admin Applications, Surrenders, Donations, and other single-record inspection flows.

    Toasts

    Sonner toasts are how the app reports the outcome of user-initiated writes. Always paired with the action that triggered them.

    When to use which

    toast.success — past-tense confirmation of a write the user initiated.

    toast.error — recoverable failure; state what failed and the next step.

    toast() — passive background info (autosave, sync).

    toast.promise — long-running operation with loading → result.

    Don't use a toast for…

    A blocking decision — use AlertDialog.

    Persistent status that needs to stay on screen — use Alert.

    Inline form validation — use FormMessage next to the field.

    Empty States & Friendly Errors

    When a page has nothing to show, we celebrate the reason or guide the user forward — never a blank screen.

    404 — Page Not Found

    Happy Golden Retriever

    Ruh-Roh!

    Page Not Found

    This page may have wandered off… but don't worry, there are plenty of golden moments waiting for you back home.

    Shown when a user hits a route that doesn't exist (src/pages/NotFound.tsx).

    Public Dogs — All Adopted

    All Our Dogs Found Homes! 🎉

    This is actually wonderful news — every single pup has found their forever family!

    New dogs become available regularly, so check back soon. Or better yet, join as a member to get notified the moment a new golden is looking for a home.

    Happy golden retriever illustration

    Shown on /dogs when no available dogs match the public list.

    Member Dashboard / Member Dogs — All Adopted

    All Our Dogs Found Homes! 🎉

    Every pup has found their forever family — wonderful news!

    New dogs become available regularly. We'll notify you when a new golden is looking for a home.

    Happy golden retriever illustration

    Shown to logged-in members on /member/dashboard and /member/dogs when no dogs are available.

    Filter Empty

    No dogs match your filters. Try adjusting your search or filters.

    Shown on /dogs and /member/dogs when search or filter criteria are active but return zero results.

    Friendly Errors

    Hard-failure states use a destructive-tinted variant of the empty-state pattern, with a clear retry CTA. Never blame the user, never show a raw stack trace.

    We couldn't load this list.

    Something went wrong on our end. Please try again — if it keeps happening, let us know.

    Use this for query / fetch failures on list pages and dashboards. For inline form errors, use FormMessage; for transient failures the user can ignore, use toast.error.

    Always include

    An icon (XCircle or AlertTriangle) on the destructive tint.

    A headline that names what failed in plain language.

    One sentence of context — never a stack trace.

    A primary "Retry" CTA and a secondary escape hatch.

    Never do

    Blame the user ("You haven't…").

    Show "Error 500" or a raw exception message.

    Let the page render blank with no recovery path.

    Use only a toast for a hard failure that blocks the whole page.

    04

    Content & voice

    Mission, voice, and write-like-this examples — the words that make IDGRR sound like IDGRR.

    ↑ Back to top

    Mission & Non-negotiables

    Who IDGRR is, and the brand rules that protect it. Read this before writing any new copy or designing any new screen.

    Mission

    Rescue, care for, and place abandoned or unwanted Golden Retrievers across Idaho — through a network of volunteer foster families.

    Audience

    Idaho families considering adoption or fostering, current donors and volunteers, and people surrendering a dog they can no longer keep. Every page should make sense to all three at once.

    Non-negotiables

    • Bright, open, friendly. Never trendy startup, luxury, or rustic-western.
    • Amber is the only brand accent. Don't introduce blue, green, or purple.
    • Scenic Idaho photography paired with golden retriever imagery.
    • Cards, soft borders, rounded corners, restrained shadows.
    • CTAs stay practical: adopt, foster, volunteer, donate, remember.
    • No cartoon dogs, handwritten fonts, or decorative illustration.

    Voice

    Warm and direct. Compassionate but never melodramatic. Clear, welcoming, informational first — inspirational second. Always grounded in Idaho.

    We sound like…

    A neighbor who knows what they're talking about.

    A volunteer board describing real work, in plain language.

    An adopter sharing the practical side of the process.

    We do not sound like…

    A startup pitch deck or a luxury pet brand.

    A guilt-trip fundraising letter.

    A breeder website or a generic "pet care" blog.

    Write like this / Avoid

    Concrete examples lifted from public pages and CTAs. When in doubt, copy the cadence on the left.

    Write like this

    "Give golden retrievers a second chance at life."

    "Every contribution helps us save more dogs."

    "Open your home temporarily — we cover the costs while you provide the love."

    "Join our community through donations, volunteering, or attending events."

    Avoid

    "Disrupting pet rescue through innovation."

    "A premium canine lifestyle experience."

    "Bold, edgy, rebellious brand language."

    Excessive emotional manipulation or fundraising clichés.

    05

    Accessibility

    AA contrast, keyboard reachable, motion-safe — the non-negotiable baseline for every screen.

    ↑ Back to top

    Accessibility

    WCAG 2.1 Level AA across all eight themes — verified by automated CI.

    Contrast: Body text ≥ 4.5:1, large/UI text ≥ 3.0:1 on every theme.

    Focus: Always visible. Uses hsl(var(--ring)) — never removed.

    Color independence: States never depend on color alone — icons, labels, and text reinforce.

    Touch targets: Minimum 40×40px on mobile.

    CI: .github/workflows/a11y-contrast.yml blocks merges that drop a tracked pair below threshold.

    Ring & Focus Variants

    Press Tab to focus the buttons below — focus-visible only triggers on keyboard focus, not mouse clicks.

    Ring widths

    focus-visible:ring-{1|2|4}

    Ring colors

    focus-visible:ring-{ring|amber|destructive}

    Ring offsets

    ring-offset-{2|4} ring-offset-{background|card}

    App standard: focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 — applied to every shadcn button via src/components/ui/button.tsx.

    Brand-tinted rings (ring-amber) are reserved for amber CTAs like BoneDonateButton. Use ring-destructive for danger actions; everything else uses the default ring-ring token.

    Tracked contrast pairs

    PairThreshold
    Body text on background4.5:1
    Body text on card4.5:1
    Body text on popover4.5:1
    Body text on secondary4.5:1
    Body text on muted4.5:1
    Muted text on background4.5:1
    Muted text on card4.5:1
    Muted text on popover4.5:1
    Muted text on secondary4.5:1
    Muted text on accent4.5:1
    Ghost hover text on accent4.5:1
    Default button label3.0:1
    Destructive button label3.0:1
    CTA button label3.0:1
    Default hover on background3.0:1
    Default hover on card3.0:1
    Destructive hover on background3.0:1
    Destructive hover on card3.0:1
    Secondary hover on background4.5:1
    Secondary hover on card4.5:1
    Primary text link on background4.5:1
    Primary text link on card4.5:1
    Amber link on background4.5:1
    Amber link on card4.5:1
    Focus ring on background3.0:1
    Focus ring on card3.0:1
    Focus ring on sidebar3.0:1
    Sidebar text on sidebar bg4.5:1
    Sidebar hover row (accent)4.5:1
    Sidebar active item label3.0:1
    Footer text on footer bg4.5:1
    Footer link on footer bg4.5:1

    Verified across 10 themes: Light · English Cream · Golden Retriever · Warm Sunset · Cream & Ink · Night & Sand · Chocolate Lab · Twilight · Dark · Midnight Gold.

    06

    Resources

    Developer-facing references and how to export this guide as a PDF or Markdown file.

    ↑ Back to top

    For Developers

    The single sources of truth — and a few gotchas worth remembering.

    Where things live

    src/index.css — every theme block & CSS variables.

    tailwind.config.ts — Tailwind tokens & color shortcuts.

    src/hooks/useTheme.tsx — theme switching & persistence.

    src/lib/theme-tokens.ts — contrast pair manifest for CI.

    src/components/ui/* — shadcn primitives.

    Rules to remember

    No raw colors. Always use semantic tokens (bg-card, text-amber).

    HSL only. All CSS variables are HSL triples — wrap with hsl(var(--token)).

    Dialog footers use grid grid-cols-2 gap-2 with full-width buttons.

    Radix Select needs undefined, not "", for empty values.

    ChartContainer from shadcn requires explicit config prop.

    This page is a live mirror of the system — every swatch and component reads from the same tokens used everywhere else. To change a value, edit src/index.css and this page will reflect it automatically.

    Export Guide

    Take this guide with you. PDF for review, Markdown for source-of-truth diffs.

    Download PDF

    Uses your browser's "Save as PDF". Print styles hide the sidebar, search, and TOC; brand color swatches stay rendered.

    Download Markdown

    Canonical narrative copy from src/content/design-system.md. No live demos or swatches — diff-friendly text.

    ← Back to Site Map
    Idaho Golden Retriever Rescue logo

    Idaho Golden Retriever Rescue

    A foster-based rescue dedicated to finding loving and stable homes for Golden Retrievers in Idaho

    Donateor give via Venmo

    Quick Links

    HomeAvailable DogsAdopt a GoldenFoster a GoldenSurrender a DogAbout UsOur TeamFAQ

    Get Involved

    Support UsDonateStay in the LoopJoin Our PackSupport our Partners

    Contact

    Boise and Hailey, Idaho

    info@idahogoldenretrieverrescue.org

    Contact Us

    © 2026 Idaho Golden Retriever Rescue. All rights reserved.

    Finding loving and stable homes for all of Idaho’s awesome Golden Retrievers.

    Privacy Policy·Terms of Service·Animal Welfare·Accessibility·Site Map·