Bahasa Indonesia

Design System Contribution Without Breaking the Existing One

The Tuesday standup is where these things die. You've spent three days in Figma building what you thought was a clean little date-range picker variant. The DS lead glances at the share link, leans back, and says: "We already have that component." And you do the math in your head. Either your three days are dead, or the version that ships is the one-off you built in a hurry, nobody owns it, and in eight months a new designer will rebuild it and call it a fourth time.

Both outcomes are bad. The good news is the gap between them is a process problem, not a talent problem. I've watched IC designers ship into mature systems in two weeks and I've watched senior folks burn six months trying to push a button variant past a gatekeeper. The difference was rarely the work. It was almost always the contribution model.

This is the working version of that model.

Why This Matters Now

Fragmented systems cost more than slow ones. There are two failure modes I see on every team that struggles with this, and they look opposite but they produce the same result.

The first is the gatekept system. The DS team owns everything. Contributions need three approvals and a quarterly roadmap slot. ICs stop trying after the second rejection. The library calcifies. New patterns get built outside it because it's faster, and now you have a design system that's actually a museum.

The second is the open free-for-all. Anyone can publish anything. By month six, every product squad has its own dropdown component, three "primary" buttons in slightly different blues, and a Figma page called "TEMP — DO NOT USE" that everyone uses. Welcome to your fragmentation tax.

A typical product team I audit has 3–7 versions of "the same" button by year two. Not because anyone wanted that. Because the contribution flow was either too tight or too loose, and the path of least resistance was building locally and never pushing it back.

The model below is the middle path. It assumes contribution is a craft skill, not a political one, and the IC who ships clean DS work compounds influence faster than the one who builds a private component library.

When to Use the DS vs. Build a New Component

Most "we need a new component" conversations end the second someone runs the 80/20 test. It's two questions:

  1. Can I solve 80% of this with an existing component plus props or a variant?
  2. If I add this, will at least 20% of teams reach for it within six months?

If the answer to #1 is yes, you don't need a new component. You need a variant or a prop, which is a smaller, faster, cheaper contribution. If the answer to #1 is no but #2 is also no, this is a one-off. Keep it local, don't poison the library.

A new component justifies itself only when both answers swing the right way: the existing kit can't cover the case, and more than one team will reuse it.

The trap I see most often is what I call the almost-fits trap. A designer finds a component that's 70% there. They detach it, tweak the padding, swap a token, and ship it as "based on" the existing one. Six months later it's diverged in seven invisible ways and the design system owner has no record any of it happened. Almost-fits is worse than starts-from-scratch because it pretends to be reuse.

A simpler decision tree, in order:

  • Existing component, no changes → use it.
  • Existing component, new prop or state → propose a variant on the existing one.
  • Existing component but the underlying behavior is wrong → propose a refactor, not a fork.
  • Genuinely new pattern, multiple teams need it → propose a new component.
  • Genuinely new pattern, only your team needs it → build it locally, mark it as local, revisit in 90 days.

The last one is fine. Local components aren't a sin. Pretending a local component is a system component is.

The Contribution Flow: RFC → Review → Ship → Document

Once you've decided it's worth adding, the flow is four stages, each time-boxed. The whole thing should take under two weeks for a normal contribution. Anything longer, something's wrong with the process, not the component.

Stage 1, RFC (1–2 days). One page. Problem, proposed solution, alternatives considered, accessibility notes, and one sketch. Not six mockups. The mistake I made for years was over-investing in fidelity before the conversation. A single sketch lets the DS owner push back on the direction without you feeling like you have to defend three days of pixel work.

The RFC is also where you flag dependencies: new tokens, new icons, behavior that touches existing components. If you discover you need a new color token to make this work, that's now part of the RFC, not a surprise on day eight.

Stage 2, Review with the DS owner (1–3 days). Async-first, sync if it's stuck. The review is not approval theater. It's a co-design session. The DS owner is not your blocker. They're the person who will end up maintaining what you ship, so their input on naming, props, and edge cases is structural, not stylistic.

Walk in with the RFC, ask three things: does this belong in the system, does the proposed shape match the existing patterns, and what am I missing on the engineering side. Walk out with a yes/no/needs-work and a single owner from the DS side who'll review the final PR.

Stage 3, Ship behind a flag (3–5 days). Build it in Figma, build it in Storybook, ship the React/Vue/whatever component to a feature flag or a beta channel in the published library. Behind-a-flag matters because it lets you and one or two early adopters pressure-test the API before it's in the production kit. Most API mistakes show up in the first real use, not in the RFC.

Stage 4, Document and promote (1 day). Story published, usage doc written, deprecation notes if it replaces something, announcement in the DS channel with a one-paragraph "when to reach for this." Then promote it from beta to GA in the next library release.

If any single stage stretches past its time-box, that's a flag. Stage 1 stretching means the problem isn't clear. Stage 2 stretching means the DS owner has structural concerns and needs a real conversation, not another revision. Stage 3 stretching usually means the API is wrong and you're patching around it. Stage 4 doesn't stretch. If you're skipping it, you're skipping the part that makes it real.

Naming Conventions That Survive

Naming is the boring half of contribution and it's where most contributions quietly fail. A component called BigBlueBtn will get used by exactly one team, ironically, and then never again. A component called Button/Primary/Large gets adopted because the name tells you what it is, where it sits, and how it relates to the rest.

The naming hierarchy that holds up over time is token → component → variant → state. So:

  • Token: color/primary/600
  • Component: Button
  • Variant: Primary, Secondary, Ghost
  • Size: Small, Medium, Large
  • State: Default, Hover, Disabled, Loading

Read top to bottom: Button/Primary/Large/Hover is unambiguous, sortable, and matches how it should appear in Figma's component panel and in the Storybook navigation.

Three rules I'd commit to muscle memory:

  1. No proper nouns. No BobsButton, no Q3Modal, no MarketingHero. The name describes the thing, not the requester.
  2. No abbreviations under 5 characters. Btn saves nothing and costs searchability. Notif is worse than Notification.
  3. No size adjectives that aren't on the scale. "Big" is not a size. Large is. If your scale is S/M/L, don't introduce XLG without adding it to the scale system-wide first.

When names collide (and they will, especially in larger systems), the rule is: the more general name belongs to the more general use case. If marketing wants Card for a stylized hero card and the system already has Card for the generic content container, marketing's component becomes Card/Hero or HeroCard. The base name stays with the base behavior.

Accessibility Minimums: WCAG AA as the Floor

Components ship with the accessibility test or they don't ship. This is not a stretch goal. WCAG AA is the floor, not the ceiling, and it's the floor because below it you're shipping components that legally and ethically exclude users.

The minimums every component needs to clear before it gets merged:

Check Threshold Where it's tested
Body text contrast 4.5:1 against background Storybook a11y addon, Figma contrast plugin
Large text (18pt+ or 14pt bold) contrast 3:1 Same
UI element / icon contrast 3:1 Same
Keyboard navigation Tab in, tab out, no traps Manual + Storybook play function
Focus indicator Visible ring, 2px minimum, 3:1 contrast Visual + automated
Screen reader label Every interactive element has an accessible name axe-core, manual VoiceOver/NVDA pass
Color independence No info conveyed by color alone Manual review, RFC checklist
Touch target 44×44px minimum on mobile Spec + mobile QA

Every one of these has automated tooling now. axe-core in CI catches the structural ones. The Storybook a11y addon catches the obvious contrast and ARIA misses. What it won't catch (and what an IC has to actually do) is the keyboard trap test: tab through every state, including modal-open, dropdown-expanded, error-state. Keyboard traps are the failure mode I see most often and they're the easiest to test.

If you're not sure whether your component passes, ship it through the test before you ship it through review. A DS owner who has to flag a contrast failure in stage 2 has just found out you didn't do the work. A contribution that arrives a11y-clean is a contribution that gets approved fast.

Figma Library Hygiene

Figma is where contributions go to silently rot if you're not paying attention. The two things that matter:

Published vs. local. Published components live in the team library and propagate to every file that pulls from it. Local components live in the file you're working in and propagate nowhere. The mistake is using a local component for prototype work, then forgetting to either delete it or promote it. Six months later the file gets opened and someone copies the local one because it has the right shape.

Rule: a local component either gets promoted to the published library within two weeks, or gets deleted. There is no third state.

Detached instances are a smell. When a designer detaches an instance, they're saying: "I needed this component but slightly different and I didn't want to deal with the system." That's a signal. Sometimes the right answer is to add a variant. Sometimes it's to fix the underlying component. Sometimes the designer was just in a hurry. But every detached instance is data, and the monthly sweep should track them.

The sweep itself is mechanical: once a month, run the Figma instance audit (or a plugin like Instance Finder), list detached instances, and walk through them with the responsible designer. Either re-attach, propose a variant, or delete. Thirty minutes of work that prevents three months of fragmentation.

Code-Design Parity (Storybook)

Every Figma component has a Storybook story or it's not real. This is the rule I'd put on the wall.

Storybook is the only place where the design and the code coexist as the same artifact. Figma shows you what the component should look like. Storybook shows you what it actually is. When they drift (and they always drift), Storybook is the source of truth, because it's what the user sees.

A real Storybook story for a contributed component has:

  • All variants rendered (Primary, Secondary, Ghost, etc.)
  • All states rendered (Default, Hover, Focus, Disabled, Loading, Error)
  • A controls panel that lets a reviewer toggle props live
  • A play function that exercises keyboard interaction
  • An a11y addon report with no violations
  • Visual regression baselines committed (Chromatic, Percy, or homegrown)

Visual regression in CI catches the drift before the PM does. When someone updates the Button component and a padding token changes by two pixels across forty stories, the diff shows up in the PR. The DS owner approves or rejects. No surprise breakage in production.

Without it, you find out about the drift when a customer screenshots a misaligned form on Twitter.

Deprecation Cadence

Things you ship will stop being the right answer eventually. A deprecation discipline is how you avoid the museum-system problem.

The cadence I'd run:

  • Quarterly review. Every quarter, the DS team plus 2–3 IC designers walk through the library and flag components that are: low-usage (<5 instances across the codebase), superseded by a newer variant, or no longer matching a11y/visual standards.
  • Two-release deprecation window. Once a component is flagged, mark it @deprecated in Storybook, add a deprecation banner in Figma, and ship a replacement (or a migration path). Give it two release cycles (typically two months) before removal.
  • Provide the codemod. This is the part teams skip. If you're deprecating a component used in 200 places, "please migrate" is not a plan. Ship the codemod (a small script that auto-rewrites <OldButton> to <Button variant="primary">) so the migration takes minutes, not weeks. Without the codemod, deprecation gets ignored and the old component lives forever.

Common Pitfalls

The four ways contributions go wrong, in rough order of frequency:

  1. Designing in isolation, then pitching the finished thing. You spent three days in Figma. The DS owner has thirty seconds of context and now you want them to bless six mockups. They won't. Bring sketches early, finished work late.
  2. Copy-pasting the closest existing component and "just tweaking it." The almost-fits trap. Either commit to a real variant or build something new. Never ship a tweaked detach as if it's a system component.
  3. Skipping the Storybook story because "the dev will do it." They won't, or they'll do it badly because they don't know the design intent. The story is your spec, the same way the Figma component is. If you don't write it, the dev's interpretation becomes the truth.
  4. Treating the DS owner as a blocker instead of a co-author. This is the biggest mindset error. The DS owner has more context than you do on what's coming, what's deprecated, what teams are about to need. Bring them in early and they accelerate your contribution. Hide from them and they slow it down because they have to reverse-engineer your reasoning.

Templates and Tools

Three artifacts I'd keep within reach:

One-page RFC template. Problem (3 sentences max), proposed solution (1 sketch + 3 bullets), alternatives considered (2–3, with why-not), accessibility notes, dependencies, who's affected. If you can't fit it on one page, the contribution isn't scoped tight enough.

Component checklist. Tokens used, variants defined, all states designed, a11y test passed, Storybook story published with controls + play function, usage docs written, visual regression baseline committed, deprecation notice if replacing something. Tape it next to your monitor.

Deprecation notice template. What's being deprecated, what replaces it, link to migration guide, codemod command, removal date. Posted in the DS channel, added to the Storybook page, banner in the Figma component.

Measuring Success

You'll know the model is working when:

  • Your contribution lands in the published library within two weeks of the RFC. Longer than that, something in the flow is broken.
  • Zero detached instances of your component after thirty days. If they're showing up, the API isn't covering the use cases.
  • The Storybook story exists, has play functions, and has zero a11y violations.
  • One other team adopts the component without asking you. This is the real signal: when reuse happens organically, you've actually contributed to the system, not just added to it.

The IC who ships clean DS work compounds influence faster than the one who builds private component libraries, because every clean contribution makes the next one faster, for you and for everyone else.

Learn More