Figma Styles & Variables
By the end of this lesson you will be able to create colour and text styles, build a set of variables with light and dark modes, and switch a whole screen between themes or languages in one click.
Imagine your client says, "Can the purple be a little bluer?" If you typed the colour by hand on 80 layers, you now have 80 changes to make. If every layer uses a shared style or variable, you change one value and the whole file updates. That is the job of styles and variables.
Styles vs variables
Both store reusable design values. They differ in what they hold.
| Styles | Variables | |
|---|---|---|
| Holds | A full set of properties, such as a whole text setup or a shadow | One single value: a colour, number, string or boolean |
| Kinds | Colour (including gradients and images), text, effect (shadows, blur), layout guide | Colour, number, string, boolean |
| Modes | No | Yes: one variable can have different values per mode, such as Light and Dark |
| Best for | Typography sets, shadows, gradients | Design tokens, themes, spacing, radius, languages, prototype logic |
Many teams use both: variables for raw values and themes, and text and effect styles that combine several values.
Think of a restaurant menu. A style is a full combo meal: "Thali" means rice, dal, two sabzis, roti and sweet together. A variable is a single item with a price that can differ by branch: "Masala chai" costs one amount in Jaipur and another at the airport. Same name, different value per location. Those locations are like modes.
Step 1: create a text style
- Select a heading text layer, for example "Choose a doctor", set to 24px Bold with 32px line height.
- In the right sidebar, find the Typography section and click the style icon (four dots).
- Click + to create a style. Name it
Heading/H2. - Repeat for body text (
Body/Regular, 16px, line height 24px) and a caption (Body/Small, 13px).
What you should see: the text panel now shows the style name instead of raw numbers. The slash in the name creates folders, so styles stay organised.
Step 2: create an effect style
- Select a card and add a drop shadow in the Effects section.
- Click the style icon in that section, then +, and name it
Shadow/Card.
Now every card can share one shadow. Change the style later and all cards update.
Step 3: create variables
- Click an empty part of the canvas so nothing is selected.
- In the right sidebar, find Local variables and click to open the variables panel.
- Create a collection named
Primitives. A collection is a folder of related variables. - Add colour variables for raw colours:
purple/600=#5b21b6,purple/50=#f3ecff,grey/900=#1a1a2e,white=#ffffff. - Add number variables for spacing:
space/2= 8,space/3= 16,space/4= 24, andradius/md= 8.
What you should see: a table of variables with names on the left and values on the right.
Step 4: semantic variables and aliases
Raw names like purple/600 say what a colour is. Semantic names say what a colour is for: bg/surface, text/primary, action/primary. A semantic variable can point to a primitive. This pointing is called an alias.
- Create a new collection named
Theme. - Add
bg/surface,text/primaryandaction/primaryas colour variables. - For each value, instead of typing a colour, right-click (or open the value picker) and choose a primitive variable, such as
purple/600foraction/primary.
Now designs use the semantic names. If the brand changes from purple to teal, you only change which primitive action/primary points to.
Step 5: add modes
- In the
Themecollection, add a new mode column. Rename the first modeLightand the new oneDark. - Give each semantic variable a dark value:
bg/surfacebecomesgrey/900,text/primarybecomeswhite, and so on. - Apply the variables to your screen: select a frame's fill and choose
bg/surface; select text and set its colour totext/primary. - Select the whole "Home" frame. In the right sidebar's layer area, change its mode from
LighttoDark.
What you should see: the whole screen switches to the dark theme instantly, because every layer uses variables that have dark values.
The same idea works in CSS with custom properties. Run this example and switch modes.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Primitives, semantic tokens and modes</title>
<style>
:root {
/* Primitives */
--purple-600: #5b21b6; --purple-300: #c4a5ff; --purple-50: #f3ecff;
--grey-900: #1a1a2e; --grey-600: #4b5563; --grey-300: #b8bcc6; --white: #ffffff;
--space-2: 8px; --space-3: 16px; --radius-md: 8px;
}
/* Semantic tokens, Light mode */
.screen[data-mode="light"] { --bg-surface: var(--white); --text-primary: var(--grey-900); --text-muted: var(--grey-600); --action-bg: var(--purple-600); --action-text: var(--white); --card-bg: var(--purple-50); }
/* Semantic tokens, Dark mode */
.screen[data-mode="dark"] { --bg-surface: var(--grey-900); --text-primary: var(--white); --text-muted: var(--grey-300); --action-bg: var(--purple-300); --action-text: var(--grey-900); --card-bg: #2a2a44; }
body { font-family: system-ui, sans-serif; padding: 16px; }
.screen { background: var(--bg-surface); color: var(--text-primary); width: 300px; padding: var(--space-3); border-radius: 16px; border: 1px solid var(--grey-300); }
.card { background: var(--card-bg); padding: var(--space-3); border-radius: var(--radius-md); margin: var(--space-3) 0; }
.card p { margin: 4px 0 0; color: var(--text-muted); }
button.cta { width: 100%; min-height: 44px; border: 0; border-radius: var(--radius-md); background: var(--action-bg); color: var(--action-text); font-weight: 600; font-size: 16px; }
</style>
</head>
<body>
<p><button type="button" id="toggle">Switch mode</button></p>
<div class="screen" id="screen" data-mode="light">
<strong>Namaste, Sunita</strong>
<div class="card"><strong>Dr Mehta</strong><p>Thu 19 Nov · 9:00 am</p></div>
<button class="cta" type="button">Book a visit</button>
</div>
<script>
const screen = document.getElementById('screen');
document.getElementById('toggle').addEventListener('click', () => {
screen.dataset.mode = screen.dataset.mode === 'light' ? 'dark' : 'light';
});
</script>
</body>
</html>You should see a light screen. Click "Switch mode" and the same layout turns dark. No layout rule changed. Only the semantic tokens point to different primitives, exactly like Figma modes. Notice the dark mode uses a lighter purple for the button so it still contrasts with the dark background.
String variables for languages
String variables hold text. With modes named English and Hindi, a button label variable can show "Book a visit" in one mode and "अपॉइंटमेंट बुक करें" in the other. Switching the frame's mode previews the whole screen in the other language, which quickly shows where longer text breaks your layout.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>String modes</title>
<style>
body { font-family: system-ui, "Noto Sans Devanagari", "Nirmala UI", sans-serif; padding: 16px; }
.screen { width: 260px; border: 1px solid #b8bcc6; border-radius: 16px; padding: 16px; line-height: 1.6; }
.cta { width: 100%; min-height: 44px; border: 0; border-radius: 8px; background: #5b21b6; color: #fff; font-size: 16px; font-weight: 600; }
</style>
</head>
<body>
<label>Mode <select id="mode"><option value="en">English</option><option value="hi">Hindi</option></select></label>
<div class="screen" id="screen" lang="en">
<p id="greeting"></p>
<button class="cta" type="button" id="cta"></button>
</div>
<script>
const strings = {
en: { greeting: 'Your next visit is on Thursday at 9:00 am.', cta: 'Book a visit' },
hi: { greeting: 'आपकी अगली मुलाक़ात गुरुवार सुबह 9:00 बजे है।', cta: 'अपॉइंटमेंट बुक करें' }
};
function apply(mode) {
document.getElementById('screen').lang = mode;
document.getElementById('greeting').textContent = strings[mode].greeting;
document.getElementById('cta').textContent = strings[mode].cta;
}
document.getElementById('mode').addEventListener('change', e => apply(e.target.value));
apply('en');
</script>
</body>
</html>Switch the dropdown to Hindi. The text changes and the lang attribute updates so screen readers pronounce it correctly.
Common mistakes
- Typing raw colours on layers instead of using variables or styles.
- Only primitives, no semantic names. Then dark mode means editing every layer.
- Naming by looks in the semantic layer, like
purple-button. Name by purpose:action/primary. - Forgetting to check contrast in every mode. Dark mode needs its own contrast checks.
Key points
- Styles store a full set of properties; variables store single values with optional modes.
- Use text and effect styles for typography and shadows.
- Variables come in four types: colour, number, string and boolean, grouped in collections.
- Primitives hold raw values; semantic variables alias them and describe purpose.
- Modes let one variable hold different values for themes or languages, switched per frame.
Exercise
In the first example, add a third mode called contrast with a pure white background, black text and a very dark purple button, and make the button cycle through all three modes. In Figma, add a High contrast mode to your Theme collection with the same idea.
Show solution
.screen[data-mode="contrast"] { --bg-surface: #fff; --text-primary: #000; --text-muted: #000; --action-bg: #2e0a6b; --action-text: #fff; --card-bg: #fff; }const modes = ['light', 'dark', 'contrast'];
document.getElementById('toggle').addEventListener('click', () => {
const next = (modes.indexOf(screen.dataset.mode) + 1) % modes.length;
screen.dataset.mode = modes[next];
});The new mode only redefines the semantic tokens, so the layout rules stay untouched. The modulo (%) wraps the index back to 0 after the last mode.
Quick Quiz
1. Which can hold different values per mode, such as Light and Dark?
Variables in a collection can have one value per mode.
2. What is an alias in Figma variables?
Semantic variables often alias primitive variables, such as action/primary pointing to purple/600.
3. Which name is best for a semantic colour variable?
Semantic names describe purpose, so the underlying colour can change without renaming.
Learn UI/UX Design with a mentor
Free lessons get you started. Our UI/UX Design training adds real projects and code reviews from working developers.
- Project-based learning
- Mentor code reviews
- Free counselling call
