The Documentation Gap
Our shared-ui library had 37 components, a cn() utility, error boundaries,
and a theme with 100+ design tokens defined in CSS custom properties. It also
had a README that said:
Shared React component library.
That's it. One line.
Developers consuming the library had to read the source code to discover
available tokens, understand component APIs, or learn the conventions.
Every question — "What's the text-tertiary color?" "Is there a card variant
for elevated surfaces?" — required grepping through theme.css.
Docs Are Part of the Design System
A component library without documentation is a codebase, not a design system.
The distinction matters because a design system encodes decisions, not just
code. When you choose text-text-secondary for body text, that's a decision.
When a developer guesses text-gray-500 because they don't know the token
exists, the decision is lost.
Documentation captures three things code alone cannot:
- Intent: Why
text-text-secondaryexists and when to use it - Constraints: What's available and what's off-limits
- Patterns: How tokens compose into common UI recipes
The Token Reference Structure
We organized the theme documentation around usage categories, not implementation details. Developers think "I need a muted text color," not "I need an oklch value":
Color Tokens
Surface colors:
--color-surface → Base background
--color-surface-secondary → Card/section backgrounds
--color-surface-elevated → Elevated cards, modals
--color-surface-tertiary → Subtle backgrounds (input fields, code blocks)
Text colors:
--color-text-primary → Headings, primary content
--color-text-secondary → Body text, descriptions
--color-text-tertiary → Metadata, timestamps, hints
--color-text-inverse → Text on dark backgrounds
Border colors:
--color-border → Default borders
--color-border-secondary → Emphasized borders (hover states)
Each token includes light and dark mode values. Developers don't need to remember oklch values — they pick the semantic token that matches their intent.
Typography Tokens
Rather than listing font sizes, we documented the variant components:
| Use case | Component | Output |
|----------|-----------|--------|
| Page hero | <Heading variant="hero"> | 36px → 48px responsive, bold |
| Card title | <Heading variant="cardTitle"> | 14px, semibold |
| Body text | <Text variant="body"> | 14px, secondary color |
| Metadata | <Text variant="meta"> | 12px, tertiary color |
This bridges the gap between "I need small gray text" and "use
<Text variant='meta'>."
Component Catalog
Each component gets a consistent entry:
### Alert
Status messages with contextual styling.
**Variants:** `info` | `success` | `warning` | `error`
**Props:** `title?`, `dismissible?`, `onDismiss?`
Usage:
<Alert variant="warning" title="Heads up">
Check your input values.
</Alert>Key decisions are called out explicitly: "Alert title uses text-sm font-semibold with margin reset. The body uses text-sm text-text-secondary."
Contribution Guidelines
The README includes rules for adding new components:
- No Next.js dependencies — shared-ui depends only on React + Tailwind
- Use
Heading/Textfor all typography — no rawtext-*+font-* - Accept
refas a regular prop (React 19 pattern, notforwardRef) - Accept
classNamefor style extensions viacn()merging - Export types alongside components from the barrel index
These rules prevent the library from drifting back toward the inconsistent state we started in.
The Impact
After shipping the comprehensive README:
- Onboarding friction dropped — new components follow documented patterns instead of cargo-culting from existing code
- Token adoption increased — developers use
text-text-tertiaryinstead of guessing gray values - PR reviews got faster — reviewers point to the docs instead of explaining conventions from memory
Documentation isn't a nice-to-have. It's the interface between your design decisions and the developers who implement them. Without it, every decision lives in one person's head — and that doesn't scale.