· 10 min read

Building language for design systems

Architecture decisions will shape what you inherit, and what you author.

Building language for design systems
Cristian Morales

Cristian Morales

Product Designer

· 10 min read

Talking the same language

I heard it, you heard it. It's better when design and engineering share the same language. But what does that really mean? It's not designers learning to code, or developers learning design. It's sharing the same vocabulary and grammar. And this depends on the architecture decisions that will shape what you inherit, and what you author.

When you choose a framework you inherit language, its vocabulary and its grammar: the rules for how terms are formed, how components compose, how state flows, what's idiomatic and what fights the grain.

Naming conventions are the easiest place to see grammar at work. camelCase, kebab-case, PascalCase each carry meaning. A prop is onPress. A DOM attribute is on-press. A component is MyButton. A custom element is my-button. The casing tells you what kind of thing you're looking at. It's actual grammar, not decoration.

Composition is vocabulary too. Whether a thing is one component with a variant or two separate components. Whether children pass through a slot, a prop, or a render function. Whether a card "has" a header or "accepts" one. These are grammatical decisions about how parts relate, and they drift exactly like names do: one team nests, another flattens, both express the same intent, and the two halves of the system stop fitting together.

And above all of it sits the highest-stakes vocabulary of all, the categories themselves. What counts as a "variant" versus a "state" versus a "status." When one team files something under variant and another files the same thing under status, that's not a naming quibble. It's two different theories of what the component is, encoded in a single word, and it's the kind of drift that makes a system incoherent at the root.

So vocabulary is the nouns, the grammar, the composition rules, and the categories. The whole language. And a design system's defining question is where that language comes from.

This is also where the oldest pain in the field lives. The design handoff loses something in every interpretation, no matter how careful the spec, and the usual fixes, spec harder, sync more, never hold. The handoff wobbles because the two sides describe the same object in two different languages. Every fix that doesn't address the language just relocates the wobble.

Inherit or author

Every term in your vocabulary arrives one of two ways. You inherit it, or you author it.

When a team adopts frameworks and libraries, it inherits their vocabulary wholesale. React hands you controlled components, hooks, children-as-props, synthetic events, an entire grammar of how a React thing is built. You didn't choose those conventions one at a time. They came as a package with the stack, and they shape every component whether you think about them or not. Inheriting is cheap: you get a complete, coherent, battle-tested vocabulary for free, and it's enforced for you, because the framework's tooling, types, and idioms hold you to it.

But notice where that enforcement stops: at the code boundary. Nothing the framework gives you reaches the design surface. React's type system doesn't police a Figma property panel. A designer naming a property On Press with a capital and a space isn't held by anything, because the design tool was never inside the framework's enforcement perimeter. This is the quiet reason the handoff wobbles even in fully framework-native systems: inherited vocabulary is enforced within code and enforced nowhere across surfaces. The crossing is unguarded by default, no matter what you inherited.

Authoring is the opposite trade. When the vocabulary you need doesn't come from the stack, you write it yourself. Your semantic color names. Your variant taxonomy. Your composition conventions. Authoring gives you a vocabulary that fits your system exactly instead of fitting the framework's defaults. But it's expensive, and it carries a catch most teams never see coming: the moment you author a term, you inherit the enforcement burden the framework used to carry. Inherited vocabulary polices itself. Authored vocabulary drifts the instant you stop policing it, because nothing underneath it is holding it in place.

Almost no real system is purely one or the other; the ratio just moves through a range. They're hybrids: an inherited base from the stack, an authored extension on top. The coherence problem, the actual day-to-day work of a design system, is making the authored part sit cleanly on the inherited part without contradicting it, and enforcing the authored part as reliably as the framework enforces its own.

Tokens proved authored vocabulary can be enforced

The encouraging news is that the field already solved this once, for one slice of the vocabulary: tokens.

A design token is authored vocabulary at the value level. color.danger isn't a framework primitive; nobody inherited it. A team wrote it, decided it means one thing, spelled it one way, and pointed both design and code at a single source of truth. And the field went further than building tools around that idea: it standardized the vocabulary's format itself. The W3C Design Tokens Community Group specification, DTCG tokens, defines how tokens are named, typed, and structured, which is why the ecosystem around them interoperates at all. Token Studio, the token pipelines, the plugins that sync design variables to code variables: they can all be bridges because they converge on a shared format. Enforcement isn't hostage to one vendor's plugin. The format is the contract, and any tool that reads it can carry it.

There's a quiet lesson in that for the inherit-or-author spectrum: standardization is how the field converts authoring burden into inheritance. A team still authors its own tokens, but it now inherits the format they're written in. What every team used to author alone, badly, and differently, the spec turned into something everyone inherits once. That's the maturity path for authored vocabulary, and tokens are the slice that's furthest along it.

Tokens are also semantic, which is the discipline the rest of the vocabulary needs to inherit from them. danger, not red. primary, not blue. Naming by intent, by what the thing is for, not by what it looks like. That's the standard for authored vocabulary: name the meaning, enforce the spelling, audit the drift. Tokens meet it. Most other authored vocabulary doesn't, which is where the handoff breaks.

Standardizing props is different

A prop is the thing the designer sets in Figma's panel and the thing the developer passes in code. Same slot, both hands. And designers know props perfectly well, they compose component APIs with them every day. The gap isn't knowledge, it's enforcement. A designer authoring a variant property in Figma has no way to know whether their variant is the same variant the code uses, because nothing resolves the two against a shared source of truth the way a plugin resolves token values. Both sides author the vocabulary independently, and independently authored vocabulary diverges.

That divergence is measurable. Scan a real library, compare every prop and value against a canonical vocabulary, and you can count it. In one system I audited, eighty-eight components across two platforms produced over two hundred distinct props and ten hard drift flags: a type here and a variant there meaning the same thing, a sizeVariant that was just size under another word. And the values drift worse than the props, because there are more of them and each component's author picks their own: outline, outlined, and bordered for one value; sm, small, and s for one size. Most of the flags were at the value level. The vocabulary frays at the values first.

The fix is the same move tokens made, one layer up: a shared look-up table both design and code compose against, defining which props exist and which values each accepts, one term per meaning at both levels, named by intent. Written down, resolved against on both surfaces, and therefore auditable. That last word is the whole point. A vocabulary you can audit is a contract.

But there's a reason props can't ride the same maturity path tokens did, and it's worth being honest about it. Tokens could standardize because values are framework-independent by nature: a color is a color in React, Vue, or SwiftUI, so a shared format could bind everyone without breaking anyone. Props can't, because prop vocabulary is downstream of the language and framework abstraction it lives in. It gets worse: a team's real vocabulary isn't even one framework's. It's a composite of the framework, the component libraries on top of it, and whatever sits between. There's no single binding point for a standard to attach to.

The two sides of the boundary are not equally hard, though. The code side is deterministic: because code is formal language, a prop map can be generated and queried by parsing the codebase directly, every prop and value extracted exactly as it exists. The source of truth builds itself from the code. The design side has no formal layer to parse against. Props would need a bespoke bridge per component library.

This is where an agent changes the structure of the problem, not just its convenience. Props need an interpreter, because there is no standard to converge on: the bridge has to be able to read your vocabulary, whatever composite of framework and library grammar it turned out to be. An agent connected to the prop map on the design system repo is that interpreter. A designer working through a component's intent and anatomy can ask which props and values the vocabulary accepts and have the prop table generated into the design tool on demand, not because someone built a plugin for that specific system, but because the contract was written in a form a machine can read and carry. This is the concrete payoff of the previous article's claim that contracts must be machine-readable: machines are how you easily enforce vocabulary across surfaces. And for props, where standardization is structurally unavailable, an adaptive machine is the only bridge that doesn't have to be rebuilt for every system.

Vocabulary should describe intention

Authored vocabulary can feel like free invention, but it isn't, because it still has to describe what an interface actually does. And what an interface does reduces to three things: input, how a user communicates intent to the system; feedback, how the system communicates state back; and structure, how the relationships between them get organized. The user or the world changes something, the state changes, and the system shows that change.

Variants are where this surfaces in the component vocabulary. A variant is the visual form of a state, and state is the feedback half of that loop. It's worth being precise: not every state comes from a user's action. A user action can change state, but so can the system on its own, a sync resolves, a permission expires, data arrives. The feedback is always about state; the user is only one of the things that can change it. A loading variant is feedback that something is processing, whether the user triggered it or a background sync did. A disabled variant is feedback that an action is unavailable, whether because of what the user did or a permission they never controlled.

This is why naming variants by intent isn't bookkeeping. Whether you inherit those names or author them, you're naming the feedback states of an interface, and the names have to describe the state, not the look. danger is feedback about consequence. success is feedback about outcome. Authored or inherited, the vocabulary bottoms out in the same HCI substrate, which is what keeps even a fully authored vocabulary anchored to something real instead of floating free as preference.

The shared surface

This is why a design system team is not a sub-team of design or of engineering. It's the surface between them, holding decisions about language: what to inherit, what to author, and how to enforce the authored part so both sides speak the same one.

The opening asked what sharing a language between design and engineering really means. It means both sides resolve against the same vocabulary, enforced across the boundary either way. The designer's variant: danger is the developer's variant="danger" because both consult the same map. Nothing to translate, so nothing drifts in translation. The handoff stops being interpretation.

And that's a decision. Inherit everything and you get coherence for free, but you live in someone else's grammar, unenforced beyond the code boundary, and you migrate when they do. Author everything and the language is yours, but so are all the consequences. Most systems thread between, and the threading, deciding term by term where the language comes from and building the enforcement that makes the authored parts hold across every surface, is what a design system actually is underneath the components. Not a library. A language, and the decision of who writes it.

design systemsdesign tokensdesign engineeringcomponent apiagentic design systems