The Beachcomber's Guide to Type-Safe Design Systems — Kathleen McMahon — React Alicante 2025

What is Beachcombing?

Treasure hunting on the beach

Kathleen wearing a teal rain jacket on a rainy day at the beach.

Sandy path to the beach on a sunny morning.

Random beach drift artwork at sunrise on the beach.

Holding up a clam shell filled with various other shells at sunset by the shoreline.

A trio of clam shells filled with a variety of other shells on the wet sand at sunset.

A tiny sand dollar on the beach, with my foot on the sand to indicate size (shell smaller than my big toe).

A large clam shell half buried in the wet sand at low tide, waves slowly approaching.

A clam shell filled with a group of sand dollars, resting on a wet seaweed-encrusted series of rocks.

Holding up the first sand dollar of the season, sadly broken, with my fingers. Thanks, greenhead fly, for biting me.

A group on sand dollars nestled in a large clam shell on a sand dune in the afternoon.

Did I mention how excited I was to find the sand dollar? And how sad I was that I broke it?

Design Systems = (perfectly) Fragile treasures

Bienvenidos!

The Beachcomber's Guide to Type-Safe Design Systems — Kathleen McMahon — React Alicante 2025

@resource11 Bluesky | Mastodon | Twitter | Instagram | GitHub

Kathleen McMahon — Engineer • Designer • Speaker

My cats, Otis and Thor, sitting on the windowsill next to some moon shells on a warm spring afternoon.

Design Systems are ALWAYS the hotness

A person wearing an inflatable T-rex costume joyfully does a forward flip off a dock into an inflatable raft floating in a pond.

Design systems are like herding kittens

A person attempting to line up a group of 10 wiggly kittens in a line. She was not successful.

O’Reilly — LaunchDarkly — Northwestern Mutual — Electronic Arts

Yes, I’m still sad about that broken sand dollar. Yet it reminds me of the fragility of design systems.

Drift & Fragile Sand Dollars

Drift & Fragile Sand Dollars

Hard-coded export type ThemeValues = | “katMeow” | “katPurr” | “katSqueak”;

—

Dynamically-generated

// This file is auto-generated // Do not edit manually

export const THEME_VALUES = [ “katMeow”, “katPurr”, “katSqueak”, “katHiss” ] as const;

export type Theme = typeof THEME_VALUES[number];

Tokens are Design Decisions

Typed Tokens Catch Bugs Early

Typed Tokens Catch Bugs Early

Untyped margin: var(—space-lgge);

—

Typed margin: ${theme.space.lg};

When to Invest in Typed Tokens

✅ Multi-app / multi-brand systems

✅ Accessibility / mission-critical

❌ MVPs / chaotic tokens (start clean first)

Type Safety depends on Tooling

Type Safety depends on Tooling

Tailwind → Typed config

PostCSS, SCSS → Style Dictionary pipeline

Vanila CSS → .d.ts exports

Styled Components → Strong TypeScript support

From Fragile to Typed

From Fragile to Typed

Before color: #0044cc; margin: 12px;

—

After color: ${theme.color.blue.primary}; margin: ${theme.space.md};

—

No drift → compiler enforces alignment

Tokens workflow — One source of truth, many outputs

Tokens workflow — One source of truth, many outputs

Designers → Figma → Tokens Studio

JSON → Style Dictionary → CSS + TypeScript

Styling + typing aligned

Typed Tokens Catch

Misspelled keys

Invalid overrides

Low-contrast colors

Typed Tokens Catch

Misspelled keys Without Types Silent Fail

Misspelled keys With Types Compile

Invalid overrides Without Types Wrong render

Invalid overrides With Types Blocked

Low-contrast colors Without Types Missed in QA

Low-contrast colors With Types Flagged

Interfaces vs. Types

Interfaces → shapes (extendable, generic)

Types → Options (exclusive, variant-driven)

Card & Alert in practice

Card & Alert in practice

Card: predictable shape → Interface

interface CardProps { title: string; children: React.ReactNode }

—

Alert: variant logic → Type

type AlertProps = { variant: “error” |… }

Why some teams ban interface

📏 Linting Rules: Type Only

• type is more general • Avoids declaration merging • Reduces decision fatigue

⚖ Intent matters Interfaces for shape, Types for options

A tiny sand dollar on the beach, with my foot on the sand to indicate size (shell smaller than my big toe).

Blending Interface + Types: MediaCard

• Shared props: title, description (interface) • Variant props: svg or photo (type union) • Combined: BaseMediaCardProps & VariantMediaCardProps

Drift Appears When APIs get fuzzy

• Dev adds both svg + photoUrl • Dev adds aria-label to a photo • Docs skipped → misuse slips in

Guardrails Using never to Forbid Props

type VariantMediaCardProps = | { mediaType:”svg”; svg:ReactNode; “aria-label”:string; photoUrl?:never; alt?:never } | { mediaType:”photo”; photoUrl:string; alt:string; svg?:never; “aria-label”?:never }

Guardrails Using never to Forbid Props

• Forbids invalid combinations • Enforces correct accessibility attributes

Anti-Pattern

🚫 Good grief… No!

<Button as=”a”>Link</Button>

<Link variant=”button”>About</Link>

A fish and fruit jello mold resting on a plate. Gross.

🚫 Good grief… No!

<Button as=”a”>Link</Button>

<Link variant=”button”>About</Link>

Disguises intent

Breaks material honesty

Successfully makes a jello-fish reference in presentation.

Being a developer is not stressing at all: Kathleen — every day

An older man smiles at the camera. The caption indicates the man is actually 26 years old

⚠ Polymorphic components Flexibility or fragility?

<Button as=”a”>Link</Button>

NO! No, no, no…

❌<Button as=”a”>Link</Button>

❌ <Link variant=”button”>About</Link>

Do this. Please.

✅ <Button>Click me</Button>

✅ <Link StyledAsButton />

✅ <a className=“btn”>Learn more</a>

A button should be a button

A link should be a link

A button should be a button

(say it with me)

A link should be a link

🟦 Compile time beats runtime

• Runtime: QA → Jira → patch → deploy • Compile-time: red squiggle → fixed pre-commit • Guardrails stop drift earlier

Sharing types is scalable

• Publish once, every app consumes • No local drift • Everyone aligned with latest design system

🐚 Wrap-up

• Tokens prevent drift • Props prevent misuse • Compile-time confidence scales

A clam shell filled with a group of sand dollars, resting on a wet seaweed-encrusted series of rocks.

🐚 Beachcomb your design system.

Thank you.

Gracias.

@resource11 Twitter | Instagram | GitHub

Still. Sad about that broken sand dollar.

Otis, the sleek black cat of wonder, playfully interrupts the sand dollar arrangement on my table.