Design System Governance: Process for Team Contributions
Design system governance doesn't have to be bureaucratic. Here's how to set up contribution workflows that keep your component library consistent without slowing teams down.
Why Most Design System Governance Falls Apart
Honestly, the number one reason design systems die is not technical debt — it's that nobody agrees on who gets to change what. You ship a solid component library, three months in someone on the marketing team adds a one-off button variant directly to production, and suddenly you have 14 button styles across 6 files and no coherent story.
Governance sounds like a corporate word. It conjures images of approval committees and ticket queues that take two weeks to process. That's not what we're talking about here. What you actually need is a lightweight set of rules that answer three questions: who can propose changes, how do those changes get reviewed, and what does 'done' look like for a component.
The teams that get this right aren't the ones with the strictest processes. They're the ones with the clearest processes. Ambiguity is the real killer. If contributors don't know whether they should open a GitHub issue or a PR or just ping someone in Slack, half of them will do nothing at all.
Defining Component Ownership and Contribution Tiers
Start by categorizing your components into tiers. Tier 1 covers foundational primitives — buttons, inputs, typography scales, spacing tokens. These need strict review because everything else builds on them. Tier 2 is composed patterns like cards, modals, and navigation bars. Tier 3 is page-level or product-specific stuff that teams own mostly themselves.
Each tier should have a named owner or a small owning team, not just 'the design system team' as a collective noun. Vague ownership means no accountability. When a Tier 1 component breaks, you want one person's name on the Slack handle. That person's job isn't to gatekeep — it's to review with context and speed.
For open-source libraries like Empire UI, this maps well to GitHub CODEOWNERS. You define a .github/CODEOWNERS file, list which paths map to which reviewers, and GitHub automatically requests reviews. It takes about ten minutes to set up and saves hundreds of hours of 'hey did anyone review this yet' messages.
Writing a Contribution Guide That Developers Actually Read
Nobody reads a 4,000-word CONTRIBUTING.md on first glance. Write for someone who's in a hurry and mildly annoyed. Lead with the 30-second version: fork, branch naming convention, run pnpm test, open a PR against main. Put all the nuance below the fold.
Your guide needs to answer: what does a minimal acceptable component look like? That means specifying concrete requirements. In Empire UI's case that's something like: the component must accept a className prop, support dark mode via the dark: Tailwind prefix, include at least one story in Storybook (see our Storybook component library guide for the setup), and export TypeScript types. No exceptions, no negotiation.
Also define what you won't accept. This is just as important. 'We don't merge components that introduce new third-party dependencies without a discussion issue first' is a sentence that prevents three days of back-and-forth on a PR. Explicit rejections feel harsh to write but they're kind to contributors because they save time.
PR Templates and Automated Checks for Consistency
A well-designed PR template is the cheapest governance tool you have. Ours at Empire UI asks five questions: What does this component do? Which existing patterns does it extend or replace? Does it work at mobile widths? Did you check color contrast against our WCAG accessibility guide? And does it respect the 8px spacing grid?
Pair that with automated checks so humans aren't manually verifying mechanical things. ESLint with eslint-plugin-react catches obvious prop issues. A16z-style visual regression tests in Playwright catch layout drift. TypeScript strict mode catches the type gaps. The goal is for reviewers to spend zero time on things a script can catch.
Here's a minimal GitHub Actions workflow that runs these checks on every PR:
``yaml
name: Component CI
on:
pull_request:
branches: [main]
jobs:
lint-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v3
with:
version: 9
- run: pnpm install --frozen-lockfile
- run: pnpm lint
- run: pnpm type-check
- run: pnpm test --run
- run: pnpm build
``
Nothing exotic. If this passes, reviewers can focus on whether the component is actually well-designed, not on whether it compiles.
Token Governance: Protecting the Color and Spacing System
The riskiest contributions are never new components — they're changes to tokens. Someone adds rgba(255,255,255,0.15) as a one-off background in a component file instead of using the surface-overlay token from your color system, and six months later that value is hardcoded in 40 places and impossible to theme.
The rule should be: no raw color values in component files, ever. Only token references. Enforce this at the linter level. You can write a custom ESLint rule, or use something like Stylelint with a declaration-property-value-disallowed-list config. In Tailwind v4.0.2, the CSS custom property approach makes this even cleaner — your tokens live in a single @theme block and arbitrary values throw a lint warning.
Same logic applies to spacing. If your spacing system is built on multiples of 4px, a gap-[13px] in a contribution should fail review instantly. Document the grid, enforce it in CI, and you'll never have a '13px gap' conversation in a PR review again. Because that conversation always takes longer than it should.
The Review Process: Keeping It Fast Without Losing Quality
A review SLA is something most teams skip and then complain about when PRs sit for two weeks. Set a target — Empire UI aims for first-response within 48 hours on community PRs, same-day on core team contributions. Put this in your CONTRIBUTING.md. If you can't meet it, scale your reviewer pool first before stretching the SLA.
Reviews should separate two concerns: 'is this the right thing to build' and 'is this built correctly'. The first is a design question that ideally gets answered in the proposal issue before any code is written. The second is what the PR review actually covers. If you find yourself arguing in a PR about whether a component should exist at all, that's a process failure that happened two steps earlier.
What does a good component review actually check? API surface (are the prop names consistent with existing components?), accessibility (keyboard navigation, ARIA, focus management — use Tab and a screen reader before approving), visual correctness in light and dark mode, and bundle size impact. That's it. Four things. If you're checking more than that on every PR, you're probably over-indexing on perfection and under-indexing on shipping.
Deprecation and Breaking Change Policy
Here's the thing: most governance docs cover adding components in detail and say almost nothing about removing or changing them. That's backwards. Breaking changes are where teams actually get hurt. You need a policy before you need it, not after.
The minimal version: any breaking change to a Tier 1 component requires a deprecation notice for at least one minor version before removal. Deprecation means the old API still works but logs a console warning in development pointing to the new API. If you're using Figma-to-React workflows to sync design tokens, make sure the deprecation shows up in both places — a token renamed only in code but not in the Figma library creates a disconnect that's painful to untangle later.
For semver: patch for bug fixes that don't change the API, minor for new components or additive props, major for anything that removes or renames existing exports. Tag your releases. Write changelogs that a developer who skipped two versions can actually use to upgrade. And if you're breaking something in a minor because it's 'clearly a bug fix' — it's not. Ship it as a major anyway. Your users will thank you.
Scaling Governance as the Team and Library Grow
When you're three people, governance is a conversation. When you're thirty contributors across six time zones, it's a system. The question isn't whether to formalize — it's when. The answer is usually earlier than you think and simpler than you fear.
Consider a Design System Committee only if you're past 50 active contributors and shipping more than ten new components per quarter. Below that threshold, a committee adds more overhead than it removes. Two or three named maintainers with explicit authority and a bias toward approving things rather than blocking them will outperform any committee.
The best signal that your governance is working? Contribution velocity. Are external contributors actually getting PRs merged within a week? Are they coming back to contribute again? If yes, you're doing it right. If PRs sit for three weeks and first-time contributors don't return, the process is too heavy regardless of how logical it looks on paper. Track it. Fix what the data tells you, not what feels right in a planning meeting.
FAQ
Reject them early and kindly. The best place to catch this is in a proposal issue before any code gets written. If someone's proposing a new card variant that uses drop shadows when your system uses border-based elevation, that's a design direction conversation — not a code review conversation. Ask them to open a discussion issue first, show the mockup, and get a thumbs-up from a maintainer before writing a single line of JSX.
Both. Branch protection on main requiring at least one approving review from a CODEOWNER is non-negotiable for any library with more than two contributors. Relying on reviewer diligence alone breaks down the moment someone is in a hurry. Set required_status_checks to include your lint and test jobs so the CI must pass before merge. That combination catches most problems without slowing anyone down.
With Tailwind v4.0.2 the CSS-first config approach helps — your design tokens live in a @theme {} block and you can explicitly disallow arbitrary values with Stylelint rules. For the class-naming side, a tailwind-merge call in your component utilities file and a Prettier plugin that sorts classes consistently both reduce the cognitive load on reviewers. Document which utilities are allowed and which are not in your CONTRIBUTING.md.
A TypeScript component file, a Storybook story with at least the default state and one variant, a brief description in the PR template explaining the use case, and a passing CI run. We also ask contributors to drop a screenshot of the component in both light and dark mode in the PR description. That screenshot requirement alone catches about 40% of dark-mode issues before a human reviewer sees the code.
Separate the factual from the preferential. 'This button's border-radius doesn't match the 4px spec in our token file' is factual — it fails review. 'I think this should be 6px instead of 4px' is preferential — that conversation belongs in a design token RFC, not in a PR blocking merge. When you have the spec documented and accessible, most disagreements resolve themselves because there's a source of truth to point at.
After it's been in production in at least two real projects, has no open accessibility issues, the API has been stable for at least one minor release cycle with no prop renames, and it has test coverage above 80%. The 'used in two projects' requirement is the one most teams skip, but it's the most valuable — hypothetical components that nobody has actually integrated reveal all kinds of API awkwardness the moment someone uses them for real.