Design System

Living reference of all design tokens powering dustinrea.com. Each section shows tokens in both light and dark themes, stacked vertically.

Colors

8 semantic color tokens defined in global.css. Light and dark values shown for each token.

Light

Background --color-bg #FFFFFF
Background Alt --color-bg-alt #F6F3F2
Surface --color-surface #F2EEED
Text --color-text #171717
Text Muted --color-text-muted #555555
Primary --color-primary #2B2E4A
Primary Hover --color-primary-hover #3D415F
Border --color-border #E0DDDC

Dark

Background --color-bg #171717
Background Alt --color-bg-alt #1E1E1E
Surface --color-surface #252525
Text --color-text #F6F3F2
Text Muted --color-text-muted #A0A0A0
Primary --color-primary #9CA0C4
Primary Hover --color-primary-hover #B0B4D4
Border --color-border #333333

Typography

Type Scale

Fluid type scale using clamp(), scaling smoothly between mobile and desktop viewports.

Light

Aa
--text-xs clamp(0.75rem, 0.7rem + 0.25vw, 0.875rem)
Aa
--text-sm clamp(0.875rem, 0.8rem + 0.35vw, 1rem)
Aa
--text-base clamp(1rem, 0.9rem + 0.5vw, 1.125rem)
Aa
--text-lg clamp(1.125rem, 1rem + 0.6vw, 1.25rem)
Aa
--text-xl clamp(1.25rem, 1.1rem + 0.75vw, 1.5rem)
Aa
--text-2xl clamp(1.5rem, 1.2rem + 1.5vw, 2rem)
Aa
--text-3xl clamp(1.875rem, 1.5rem + 1.875vw, 2.5rem)
Aa
--text-4xl clamp(2.25rem, 1.75rem + 2.5vw, 3.5rem)

Dark

Aa
--text-xs clamp(0.75rem, 0.7rem + 0.25vw, 0.875rem)
Aa
--text-sm clamp(0.875rem, 0.8rem + 0.35vw, 1rem)
Aa
--text-base clamp(1rem, 0.9rem + 0.5vw, 1.125rem)
Aa
--text-lg clamp(1.125rem, 1rem + 0.6vw, 1.25rem)
Aa
--text-xl clamp(1.25rem, 1.1rem + 0.75vw, 1.5rem)
Aa
--text-2xl clamp(1.5rem, 1.2rem + 1.5vw, 2rem)
Aa
--text-3xl clamp(1.875rem, 1.5rem + 1.875vw, 2.5rem)
Aa
--text-4xl clamp(2.25rem, 1.75rem + 2.5vw, 3.5rem)

Font Stacks

Sans (Primary) --font-sans

The quick brown fox jumps over the lazy dog.

'Gordita', system-ui, -apple-system, sans-serif
Mono --font-mono

The quick brown fox jumps over the lazy dog.

ui-monospace, 'Cascadia Code', 'Source Code Pro', Menlo, Consolas, monospace

Font Weights

Gordita Regular 400
Gordita Medium 500
Gordita Bold 700

Tracking (Letter Spacing)

4 letter-spacing tokens for controlling text density. Tight values compress display text; wide values open up uppercase labels.

Light

Section Heading
--tracking-tight -0.02em: Tight Display/hero text
Section Heading
--tracking-snug -0.01em: Snug Section headings (h1, h2)
Section Heading
--tracking-normal 0: Normal Body text
DESIGN TOKENS
--tracking-wide 0.06em: Wide Uppercase labels

Dark

Section Heading
--tracking-tight -0.02em: Tight Display/hero text
Section Heading
--tracking-snug -0.01em: Snug Section headings (h1, h2)
Section Heading
--tracking-normal 0: Normal Body text
DESIGN TOKENS
--tracking-wide 0.06em: Wide Uppercase labels

Spacing

8 spacing tokens from global.css. Visual bars show relative size with pixel equivalents.

Light

--space-xs 0.25rem (4px)
--space-sm 0.5rem (8px)
--space-md 1rem (16px)
--space-lg 1.5rem (24px)
--space-xl 2rem (32px)
--space-2xl 3rem (48px)
--space-3xl 4rem (64px)
--space-4xl 6rem (96px)

Dark

--space-xs 0.25rem (4px)
--space-sm 0.5rem (8px)
--space-md 1rem (16px)
--space-lg 1.5rem (24px)
--space-xl 2rem (32px)
--space-2xl 3rem (48px)
--space-3xl 4rem (64px)
--space-4xl 6rem (96px)

Borders & Radii

Border-radius tokens from global.css. Each box demonstrates the radius applied to a 1px border. Token names map to CSS custom properties.

Light

--radius-sm 6px Small
--radius-md 8px Medium
--radius-lg 12px Large
Direct value 50% Circle (special)
Direct value 999px Pill (special)

Dark

--radius-sm 6px Small
--radius-md 8px Medium
--radius-lg 12px Large
Direct value 50% Circle (special)
Direct value 999px Pill (special)

Shadows

6 shadow patterns used for elevation, focus, and interactive feedback. Each card demonstrates the shadow at rest.

Light

Subtle Used for cards, elevated surfaces at rest. 0 1px 2px rgba(0,0,0,0.08), 0 2px 8px rgba(0,0,0,0.06)
Hover / Elevated Multi-layer hover shadow for interactive cards. 0 2px 4px rgba(0,0,0,0.1), 0 4px 16px rgba(0,0,0,0.08)
Soft Lift Comparison blocks and light hover feedback. 0 2px 4px rgba(0,0,0,0.04), 0 4px 16px rgba(0,0,0,0.06)
Dropdown Nav dropdowns, popovers. 0 4px 6px -1px rgba(0,0,0,0.08), 0 2px 4px -2px rgba(0,0,0,0.05)
Sheet (top edge) Cookie banner and bottom sheets. 0 -4px 12px rgba(0,0,0,0.08)
Focus ring Focus indicator for form inputs. 0 0 0 3px rgba(43,46,74,0.15)

Dark

Subtle Used for cards, elevated surfaces at rest. 0 1px 2px rgba(0,0,0,0.08), 0 2px 8px rgba(0,0,0,0.06)
Hover / Elevated Multi-layer hover shadow for interactive cards. 0 2px 4px rgba(0,0,0,0.1), 0 4px 16px rgba(0,0,0,0.08)
Soft Lift Comparison blocks and light hover feedback. 0 2px 4px rgba(0,0,0,0.04), 0 4px 16px rgba(0,0,0,0.06)
Dropdown Nav dropdowns, popovers. 0 4px 6px -1px rgba(0,0,0,0.08), 0 2px 4px -2px rgba(0,0,0,0.05)
Sheet (top edge) Cookie banner and bottom sheets. 0 -4px 12px rgba(0,0,0,0.08)
Focus ring Focus indicator for form inputs. 0 0 0 3px rgba(43,46,74,0.15)

Breakpoints

3 responsive breakpoints used in @media queries. All layouts use min-width (mobile-first).

Name Value Usage
sm 640px Small tablets, landscape phones. Single-column to two-column shifts.
md 768px Tablets. Dual-column layouts, theme grid side-by-side, font-stack grid.
lg 1024px Desktops. Sidebar TOC becomes sticky, nav drawer → inline links.
sm: 640px
md: 768px
lg: 1024px

Motion

Duration tokens and motion patterns used for transitions and animations.

Duration Tokens

3 duration tokens from global.css. Hover each demo box to see the transition speed in action.

Light

Hover me
--duration-fast 0.15s: Fast Micro-interactions: focus rings, hover feedback
Hover me
--duration-normal 0.2s: Normal Standard hover/click transitions
Hover me
--duration-slow 0.3s: Slow Theme changes, layout shifts, drawer slides

Dark

Hover me
--duration-fast 0.15s: Fast Micro-interactions: focus rings, hover feedback
Hover me
--duration-normal 0.2s: Normal Standard hover/click transitions
Hover me
--duration-slow 0.3s: Slow Theme changes, layout shifts, drawer slides

Motion Patterns

5 motion patterns used for transitions and animations. Hover or click each demo to see the curve in action.

Theme Transition --transition-theme background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease Applied globally for smooth light ↔ dark mode switches.
A
A
Hover Ease 0.15s ease Quick interactive feedback on buttons, links, and TOC items.
Hover me
Nav Drawer Slide 0.3s cubic-bezier(0.22, 1, 0.36, 1) Elastic slide-in for the mobile navigation drawer.
Cookie Banner Rise 0.3s cubic-bezier(0.22, 1, 0.36, 1) Bottom sheet rise with matched opacity fade.
ThemeToggle Icon 0.3s cubic-bezier(0.2, 0, 0, 1) Icon morph curve for sun ↔ moon toggle.

Components

All site components rendered with props tables and dual-theme previews. Components marked Static replica cannot be rendered live due to singleton constraints (transition:persist, canvas, or cookie-gated scripts).

Button Variants

Three canonical button styles: .btn-primary (solid background, prominent CTA), .btn-secondary (text-only with underline on hover), and .cta-link (inline arrow link). All use --color-primary / --color-primary-hover and share min-height 48px for touch targets.

Defined in: global.css (available site-wide)

Usage: CTAs on all pages. .btn-primary has box-shadow lift on hover, .btn-secondary shows border-bottom on hover, and .cta-link shows border-bottom on hover with arrow suffix. Styles defined globally in global.css.

Theme Toggle

Animated sun/moon icon toggle for light/dark mode with cross-fade transition.

Props: None

Light

Dark

Usage: Rendered inside Nav. Persists choice to localStorage. View-transition compatible via astro:page-load listener. The is:inline script binds a click handler to [data-theme-toggle].

Nav

Site-wide navigation with responsive hamburger/drawer, theme-aware logo, and dropdown menu.

NameTypeDefaultDescription
No props. Active-link highlighting is updated client-side via setupNav() on each astro:page-load event, reading window.location.pathname to set .active class and aria-current="page"

Light

Static replica
Book a Call

Dark

Static replica
Book a Call
Usage: Rendered in PageLayout with transition:persist="nav". Active-page highlighting is updated client-side on each navigation via updateActiveLinks() in the astro:page-load handler — the server-rendered .active class and aria-current are stale after the first page since the nav persists across view transitions. The dropdown trigger uses the same .nav-link styles as regular links (apart from its chevron caret). Cannot be rendered live here because transition:persist and data-nav selectors would conflict with the real Nav.

Nav Dropdown

Keyboard-accessible dropdown menu used for the "Resources" nav item. Supports Enter/Space to toggle, ArrowDown/ArrowUp to navigate items, and Escape to close.

Props: None (embedded in Nav component)

Light

Static replica
Resources
Resources
Better Every Cycle

Dark

Static replica
Resources
Resources
Better Every Cycle
Usage: Part of the desktop Nav. Trigger uses the same .nav-link styles as regular links (font-size, weight, color) plus a chevron caret. Uses aria-expanded and aria-haspopup. Menu items use role="menuitem" with roving tabindex. Active state (.active class + aria-current="page") updated client-side on navigation. Closed on click-outside, Escape, Tab, or view transition navigation.

Dot Field

Generative canvas dot field for the homepage hero with ambient dots, cursor interaction on desktop, and drift on mobile.

Props: None

Light

Static replica

Dark

Static replica
Usage: Homepage hero only. 3–4 dot size classes with organic spacing. Desktop: cursor parallax. Mobile: ambient drift. Respects prefers-reduced-motion (static single draw). Theme-adaptive via MutationObserver on data-theme. Canvas-based, so a second instance cannot be rendered without selector conflicts.

Calendly Embed

Inline Calendly scheduling widget with view-transition support and script dedup.

NameTypeDefaultDescription
url string 'https://calendly.com/dustinrea/strategy-call' Calendly scheduling URL to embed

Light

Static replica

Loading scheduling widget…

Book directly on Calendly →

Dark

Static replica

Loading scheduling widget…

Book directly on Calendly →

Usage: Placed on scheduling pages. Loads Calendly's widget.js from CDN with dedup checks. Uses Calendly.initInlineWidget() (not data-url auto-load) for view-transition compatibility. Container has id="calendly" for CTA scroll targets. Cannot be rendered live here because it would load external scripts and create a real scheduling widget.

Text Input

Standard form text input with focus ring, placeholder styling, and error state. Used across all form components.

ClassPurpose
.form-inputBase input styles — border, padding, min-height 44px, focus ring
.input-errorRed border for validation errors

Light

Name is required.

Dark

Name is required.
Usage: All forms (Contact, Discovery, Feedback). Base class .form-input provides consistent styling. Focus ring uses box-shadow: 0 0 0 3px with theme-adaptive opacity. Error state via .input-error turns border red. Min-height 44px for WCAG touch target compliance.

Select Dropdown

Native <select> with custom chevron icon, appearance: none styling, and theme-aware SVG arrow. Used for engagement type, team size, timeline, and budget fields.

ClassPurpose
.form-input.form-selectCombines base input styles with custom select appearance

Light

Dark

Usage: Discovery and Feedback forms. Uses appearance: none with a background SVG chevron that swaps between light/dark variants via [data-theme]. Padding-right reserves space for the custom arrow. Options list uses native browser rendering.

Rating Pills

1–5 radio button group styled as clickable pill buttons. Uses visually-hidden <input type="radio"> with styled <span> labels.

Props: None (embedded in Feedback Form)

Light

Rating

Dark

Rating
Usage: Feedback form. Radio inputs are visually hidden with clip: rect(0,0,0,0). Pill labels (.rating-pill-label) use border-radius: 999px for pill shape. Checked state fills with --color-primary. Focus-visible shows a 2px outline. Supports keyboard navigation via native radio group behavior.

Patterns

Reusable layout and interaction patterns used across the site. Each pattern shows the canonical structure with live or code examples in both themes.

Page Layout

Every page follows BaseLayout → PageLayout → page content. PageLayout adds Nav, Footer, CookieConsent, and ClientRouter (view transitions). Content is structured with semantic <section> blocks inside <main id="main-content">.

Light

Nav
Section 1
Section 2
Section 3

Dark

Nav
Section 1
Section 2
Section 3
Usage: Every page. PageLayout wraps BaseLayout and provides Nav, Footer, CookieConsent, and ClientRouter for view transitions.

Section Pattern

Content sections alternate between default and --color-bg-alt backgrounds. Each section uses section-inner (max-width 64rem) or section-inner--narrow (48rem, centered) containers, with aria-labelledby linking to the section heading.

Light

Section (default bg)

Content inside .section-inner

Section (alt bg)

Alternating --color-bg-alt

Dark

Section (default bg)

Content inside .section-inner

Section (alt bg)

Alternating --color-bg-alt

Usage: All content pages. Alternating backgrounds create visual rhythm between sections.

Form Pattern

All forms share: honeypot spam protection (name="_gotcha", hidden via CSS), client-side validation, a server error banner (initially hidden), a success state transition, and fetch-based submission. The form replaces itself with a success message on completion.

Interactive demo — fill out and submit to see the success state, or toggle "Trigger Errors" to see validation and server error states. No data is sent anywhere.

Light

Dark

Usage: Contact, Discovery, and Feedback forms. All three share identical error handling, honeypot protection, and success-state architecture. Toggle "Trigger Errors" to simulate a server error after validation passes.