🏖 The Beachcomber’s Guide to Type-Safe Design Systems

A presentation at React Alicante 2025 in October 2025 in Alicante, Spain by Kathleen McMahon

Slide 1

Slide 1

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

Slide 2

Slide 2

What is Beachcombing?

Slide 3

Slide 3

Treasure hunting on the beach

Slide 4

Slide 4

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

Slide 5

Slide 5

Sandy path to the beach on a sunny morning.

Slide 6

Slide 6

Random beach drift artwork at sunrise on the beach.

Slide 7

Slide 7

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

Slide 8

Slide 8

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

Slide 9

Slide 9

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

Slide 10

Slide 10

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

Slide 11

Slide 11

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

Slide 12

Slide 12

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

Slide 13

Slide 13

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

Slide 14

Slide 14

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

Slide 15

Slide 15

Design Systems = (perfectly) Fragile treasures

Slide 16

Slide 16

Bienvenidos!

Slide 17

Slide 17

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

Slide 18

Slide 18

@resource11 Bluesky | Mastodon | Twitter | Instagram | GitHub

Slide 19

Slide 19

Kathleen McMahon — Engineer • Designer • Speaker

Slide 20

Slide 20

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

Slide 21

Slide 21

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.

Slide 22

Slide 22

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.

Slide 23

Slide 23

O’Reilly — LaunchDarkly — Northwestern Mutual — Electronic Arts

Slide 24

Slide 24

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

Slide 25

Slide 25

Drift & Fragile Sand Dollars

Slide 26

Slide 26

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];

Slide 27

Slide 27

Tokens are Design Decisions

Slide 28

Slide 28

Typed Tokens Catch Bugs Early

Slide 29

Slide 29

Typed Tokens Catch Bugs Early

Untyped margin: var(—space-lgge);

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

Slide 30

Slide 30

When to Invest in Typed Tokens

Slide 31

Slide 31

✅ Multi-app / multi-brand systems

✅ Accessibility / mission-critical

❌ MVPs / chaotic tokens (start clean first)

Slide 32

Slide 32

Type Safety depends on Tooling

Slide 33

Slide 33

Type Safety depends on Tooling

Tailwind → Typed config

PostCSS, SCSS → Style Dictionary pipeline

Vanila CSS → .d.ts exports

Styled Components → Strong TypeScript support

Slide 34

Slide 34

From Fragile to Typed

Slide 35

Slide 35

From Fragile to Typed

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

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

No drift → compiler enforces alignment

Slide 36

Slide 36

Tokens workflow — One source of truth, many outputs

Slide 37

Slide 37

Tokens workflow — One source of truth, many outputs

Designers → Figma → Tokens Studio

JSON → Style Dictionary → CSS + TypeScript

Styling + typing aligned

Slide 38

Slide 38

Typed Tokens Catch

Misspelled keys

Invalid overrides

Low-contrast colors

Slide 39

Slide 39

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

Slide 40

Slide 40

Interfaces vs. Types

Slide 41

Slide 41

Interfaces → shapes (extendable, generic)

Slide 42

Slide 42

Types → Options (exclusive, variant-driven)

Slide 43

Slide 43

Card & Alert in practice

Slide 44

Slide 44

Card & Alert in practice

Card: predictable shape → Interface

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

Alert: variant logic → Type

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

Slide 45

Slide 45

Why some teams ban interface

Slide 46

Slide 46

📏 Linting Rules: Type Only

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

Slide 47

Slide 47

⚖ Intent matters Interfaces for shape, Types for options

Slide 48

Slide 48

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

Slide 49

Slide 49

Blending Interface + Types: MediaCard

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

Slide 50

Slide 50

Drift Appears When APIs get fuzzy

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

Slide 51

Slide 51

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 }

Slide 52

Slide 52

Guardrails Using never to Forbid Props

• Forbids invalid combinations • Enforces correct accessibility attributes

Slide 53

Slide 53

Anti-Pattern

Slide 54

Slide 54

🚫 Good grief… No!

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

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

Slide 55

Slide 55

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

Slide 56

Slide 56

🚫 Good grief… No!

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

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

Slide 57

Slide 57

Disguises intent

Slide 58

Slide 58

Breaks material honesty

Slide 59

Slide 59

Successfully makes a jello-fish reference in presentation.

Slide 60

Slide 60

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

Slide 61

Slide 61

⚠ Polymorphic components Flexibility or fragility?

Slide 62

Slide 62

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

Slide 63

Slide 63

NO! No, no, no…

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

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

Slide 64

Slide 64

Do this. Please.

✅ <Button>Click me</Button>

✅ <Link StyledAsButton />

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

Slide 65

Slide 65

A button should be a button

Slide 66

Slide 66

A link should be a link

Slide 67

Slide 67

A button should be a button

(say it with me)

Slide 68

Slide 68

A link should be a link

Slide 69

Slide 69

🟦 Compile time beats runtime

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

Slide 70

Slide 70

Sharing types is scalable

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

Slide 71

Slide 71

🐚 Wrap-up

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

Slide 72

Slide 72

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

Slide 73

Slide 73

🐚 Beachcomb your design system.

Slide 74

Slide 74

Thank you.

Slide 75

Slide 75

Gracias.

Slide 76

Slide 76

@resource11 Twitter | Instagram | GitHub

Slide 77

Slide 77

Still. Sad about that broken sand dollar.

Slide 78

Slide 78

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