Say you are opening a fresh Figma file because a small product needs a consistent set of buttons, inputs, and cards, and you want that consistency enforced by the tool rather than by memory. You have heard the term “design system” thrown around, and you suspect it means more than a page of rectangles labeled “Components.” This walkthrough covers the minimum viable structure that earns the name, in the order you should build it, with the specific Figma features that make each layer work.
The order matters more than any single technique. Beginners commonly start with components, discover that changing a brand color means touching forty of them, and rebuild from scratch. Starting with tokens instead means the color change is a single edit.
Beginner Path vs. Advanced Path: What Changes as You Scale
Before walking through the steps, it helps to see where the beginner path ends and the advanced path begins. The two are not different tools — they are the same tools used at different levels of rigor.
| Concern | Beginner Approach | Advanced Approach |
|---|---|---|
| Color and type values | Local variables in one collection | Multiple variable collections with modes (light/dark, brand A/B) |
| Component reuse | Components and instances | Components plus component properties and variants |
| Naming | Button/Primary/Default | Same, but enforced by a documented taxonomy and a linting plugin |
| Documentation | A “Read Me” page in the file | A published library with descriptions, changelog, and deprecation notes |
| Handoff | Dev Mode inspection | Code Connect mappings and token export to code |
| Governance | One designer owns the file | Contribution process, review, and versioned releases |
If you are a solo designer or on a small team shipping one product, the beginner column is sufficient and the advanced column adds overhead you will not recover. If multiple products consume the same library, or if your tokens need to reach code, the advanced column becomes the cheaper option over time. The rest of this post builds the beginner kit step by step, then explains which advanced steps to add and when.
Step 1: Create the File and Its Page Structure
A starter kit file needs pages, and the page structure should match the layers of the system. A workable default:
📄 Cover
📄 Foundations (variables, styles)
📄 Components
📄 Patterns (composed components)
📄 Playground (scratch space for testing)
The Cover page holds the file name, version, and a one-line description of who should use it. Foundations holds your tokens. Components holds atomic pieces. Patterns holds anything built from two or more components. Playground is where you assemble and stress-test without polluting the library pages.
This separation prevents a common beginner error: mixing token definitions into the Components page, which makes it hard to find anything once the file grows past a few dozen items.
Step 2: Define Your Token Layer with Variables
Variables are Figma’s mechanism for storing reusable values — colors, numbers, strings, and booleans. For a starter kit, you need at minimum a color collection and a number collection.
Create a color collection with two tiers. The first tier holds raw palette values, and the second tier holds semantic aliases that reference the first tier. The indirection is the whole point: components bind to the semantic tier, and rebranding means editing only the raw tier.
Collection: Primitives
color/blue/500 #2563EB
color/blue/600 #1D4ED8
color/gray/100 #F3F4F6
color/gray/900 #111827
Collection: Semantic
surface/default -> Primitives/color/gray/100
surface/inverse -> Primitives/color/gray/900
action/primary -> Primitives/color/blue/500
action/primary-hover -> Primitives/color/blue/600
Bind components to Semantic/action/primary, never to Primitives/color/blue/500 directly. When the brand shifts from blue to green, you change the alias in one place and every button updates.
The same pattern applies to a number collection for spacing and radius:
Collection: Scale
spacing/1 4
spacing/2 8
spacing/3 12
spacing/4 16
radius/sm 4
radius/md 8
A trade-off worth naming: variables add a layer of abstraction that new contributors must learn. On a one-page marketing design, that abstraction costs more than it saves. Once more than one product consumes the same values, it pays back quickly.
Step 3: Set Up Text and Effect Styles
Figma has two parallel systems for typography and effects: the older Styles and the newer Variables. Text styles and effect styles remain the practical choice for type and shadow because Figma’s variable support for font properties is limited compared to what text styles offer.
Define a type scale with named styles:
Display/Large 32/40, semibold
Heading/1 24/32, semibold
Body/Default 16/24, regular
Body/Small 14/20, regular
Label/Default 14/20, medium
Name them by role, not by size. Heading/1 survives a scale change; Text/24 does not. Define effect styles for elevation in the same pass:
Elevation/1 0 1 2 rgba(0,0,0,0.08)
Elevation/2 0 4 8 rgba(0,0,0,0.10)
Elevation/3 0 12 24 rgba(0,0,0,0.12)
Components bind to these styles, so a global shadow adjustment is a single edit per style rather than a per-component change.
Step 4: Build Your First Component Correctly
Start with something you will reuse constantly, like a button. The mistake beginners make is drawing a button and calling it a component immediately. The better sequence is: draw the button, decide which properties should vary, build those as variants, then convert to a component set.
For a button, the variable axes are usually:
- Variant: Primary, Secondary, Ghost
- Size: Small, Medium, Large
- State: Default, Hover, Focus, Disabled
That is a matrix, and if you build all combinations manually you create 36 variants. Build the ones you need on day one — typically Primary/Medium/Default plus a couple of states — and add the rest as the product requires them. Attempting the full matrix upfront is a classic beginner trap that produces a large, unused component set.
Bind the button’s fill to Semantic/action/primary and its padding to Scale/spacing/2 and Scale/spacing/4. Set the label style to Label/Default. When the token values change, the button follows.
Step 5: Add Component Properties and Nested Reuse
Once the button exists, component properties reduce the number of variants you need. A boolean property for an icon slot, a text property for the label, and an instance-swap property for the icon itself mean you can express “button with icon” without creating a variant for every icon.
Two properties cover most button needs:
Label(text property) — replaces the need for separate variants per label stringHas Icon(boolean) — toggles the nested icon instance
The result is a smaller component set that handles more cases. The trade-off: properties are a newer feature, and contributors unfamiliar with them may look for variants that no longer exist. Document the property names on the component’s description field so they are discoverable in the assets panel.
Step 6: Compose Patterns from Components
Patterns are components built from other components. A form field that combines a label, an input, and an error message is a pattern. The rule that keeps patterns maintainable: any element with its own identity should be its own component, and the pattern should nest instances of it rather than re-drawing it.
If your form field draws its own input rather than instancing the input component, a change to the input component will not propagate, and you will be editing the pattern separately. Over a handful of patterns this multiplies into a maintenance problem.
Step 7: Automate the Tedious Steps with Plugins
Several steps in this process are mechanical, and plugins handle them more consistently than manual work. The specific tasks worth automating:
Bulk variable creation. Manually adding a twelve-step color ramp means creating twelve variables and typing twelve hex values. A variable-bulk-creation plugin reads a JSON or diagram input and generates the collection in one pass. Here is the shape of the data these plugins typically accept:
{
"Primitives": {
"color/blue/50": "#EFF6FF",
"color/blue/500": "#2563EB",
"color/blue/900": "#1E3A8A"
},
"Scale": {
"spacing/1": 4,
"spacing/2": 8,
"spacing/4": 16
}
}
Producing that JSON from a design token spec (a W3C draft format many teams already maintain) means your Figma variables can be regenerated whenever the source of truth changes, instead of being hand-edited and drifting.
Naming and linting. A naming-convention plugin scans the file and flags components that violate your taxonomy — a btn-primary next to Button/Primary will surface as an inconsistency. Run it before publishing a library version.
Accessibility checks. A contrast-checking plugin evaluates your foreground and background token pairs and reports failures against WCAG thresholds. Running it on the token layer catches problems before they are baked into components.
A caution: plugin-generated variables and components are still your responsibility to review. A bulk importer that creates a variable named Color with no alias structure gives you a large collection that is harder to maintain than the one you started with. Use plugins to reduce typing, not to skip decisions.
Verify the Setup Works: A Concrete Test
Before calling the kit done, run this check end to end.
- Create a new frame on the Playground page.
- Place a button instance and a form-field pattern.
- Change
Primitives/color/blue/500to a different hue in your primitive collection. - Confirm the button’s fill and the form field’s focus ring both updated.
- Change
Scale/spacing/2from 8 to 10 and confirm padding across both components shifted.
If step 4 or 5 fails, a component is bound to a raw value rather than a token, and you have a rebinding job ahead. This test takes two minutes and catches the binding errors that make a design system feel unreliable. Run it every time you add a component.
Common Failure Modes to Avoid
Binding components to primitive values directly. This defeats the semantic layer and means a rebrand touches every component. Bind to semantic aliases; keep primitives as an implementation detail of the Foundations page.
Building the full variant matrix upfront. You will create variants nobody uses and slow the file. Add variants when a real design requires them.
Skipping documentation. A component with no description field and no usage note is a component every new contributor will use incorrectly. Add a one-line description and, for patterns, a short usage page.
Publishing too early. A library version published with structural problems forces consumers to migrate. Get the token layer and a handful of components stable before publishing v1.
Confusing styles and variables by duplicating values. If you maintain both a text style and a variable for the same font size, they will diverge. Pick one system per concern and stick to it.
When to Move Past the Beginner Kit
The beginner kit stops being sufficient when one of three signals appears: a second product starts consuming the library, your tokens need to reach code without manual re-entry, or your team grows past three contributors.
At that point the advanced steps become cost-effective. Multiple variable collections with modes let you support light and dark themes or multiple brands from one component set. Code Connect maps Figma components to code components so Dev Mode shows the real implementation rather than a generic snippet. A documented contribution process with review and versioned releases keeps the library from accumulating one-off components.
Each of these adds process. Adopting them before the signals appear means maintaining infrastructure no one is using. Adopting them after gives you a library that already has consumers, which makes the migration a coordinated effort rather than a personal one.
A Starter Checklist
- File created with Cover, Foundations, Components, Patterns, Playground pages
- Primitive color and scale variables defined
- Semantic aliases bound to primitives
- Text and effect styles created and named by role
- First component built, bound to semantic tokens, with at least one property
- One pattern composed from nested component instances
- Naming and contrast plugins run, issues resolved
- The two-minute rebind test passes
- Component descriptions written
- Decision made on whether to publish a library version
Which of these steps is currently missing from your file? Start there, because the layers above it depend on it being correct — a component bound to a raw color will not benefit from a semantic alias added later without a rebind.