LUCENTCOMMERCEGET A FREE STORE AUDITFREE AUDIT

THEME · TECHNICAL · SECTIONS · 16 JUNE 2026 · 8 MIN READ

Design tokens between Figma and a Shopify theme

A Shopify theme already has a token layer — theme settings, colour scheme groups and font settings. The work is deciding which tokens the merchant owns, not building a pipeline.

A Liquid template open beside the section it renders

A Shopify theme already has a design token layer, and it is theme settings. Global settings live in config/settings_schema.json and are read in Liquid as {{ settings.<id> }}; colours are grouped with the color_scheme_group setting type, which holds a definition array of individual colours and a role object mapping them to semantic slots like text, background and primary button; fonts come from font_picker and are rendered with the font_face and font_modify filters. The job is not to invent a token system on top of that. It is to decide which values a merchant may change, emit those as CSS custom properties once, and hardcode the rest.

IN SHORT

  • Global theme settings are defined in `config/settings_schema.json`, saved into `settings_data.json`, and read in Liquid as `{{ settings.<id> }}`.
  • Shopify’s `color_scheme_group` setting type is already a semantic token layer: a `definition` array of raw colours plus a `role` object that maps them to text, background, links, icons and button slots.
  • Referencing a colour scheme setting directly returns its ID string — Shopify’s own example outputs `background-2` — which is what makes the CSS-class-per-scheme pattern work.
  • Figma’s Variables REST API is not generally available: Shopify documents nothing about it, and Figma states you must have a Full seat in an Enterprise org to use it.
  • Type and spacing have no native scale token type, so a numeric scale belongs in CSS rather than in twenty `range` settings a merchant can break.
  • Every token you expose in the theme editor is a value someone can set to something illegible, so exposure is a support decision as much as a design one.

The theme already has a token layer

Design token conversations on Shopify projects usually start from the assumption that the theme has nothing, and that a build step has to invent one. It has something, and it is more structured than most people expect.

Shopify’s documentation splits it across three files. config/settings_schema.json "controls the organization and content of the Theme settings area of the theme editor" — it is an array of objects representing setting categories, each with a name and a settings array. Whatever the merchant then chooses is "saved in settings_data.json". In Liquid, a global setting is read as {{ settings.setting_id }}, a section setting as {{ section.settings.setting_id }} and a block setting as {{ block.settings.setting_id }}.

That is the whole mechanism: a schema that declares what exists, a data file that holds the current values, and an accessor in the template. It is a token system with a merchant-facing editor attached, which is more than most design systems ship with. The question worth arguing about is not how to build one, it is what belongs in it.

Colour is the part Shopify has already modelled well

Most token systems distinguish primitives — the raw values — from semantic tokens that name a use. color_scheme_group does exactly that, in one schema object.

The setting type "outputs a color scheme which is composed of the following input setting types: header, color, color_background". Its definition array holds the raw colours: in Shopify’s own documented example, background, background_gradient, text, button, button_label, secondary_button_label and shadow. Its role object then maps those definitions onto semantic slots — text, background (with separate solid and gradient keys), links, icons, primary_button, on_primary_button, primary_button_border, secondary_button, on_secondary_button and secondary_button_border. The role map is what the editor uses to draw the scheme preview a merchant picks from.

The practical detail that decides how you write the CSS is what happens when you reference the setting. The color_scheme object has two properties, id and settings, but referencing the setting directly returns the ID string — Shopify’s example shows {{ settings.card_color_scheme }} outputting background-2. That is why the established theme pattern is a class per scheme: emit one CSS block per scheme defining custom properties, then put color-{{ section.settings.color_scheme }} on the section wrapper and let the cascade do the rest. Sections stop knowing colours. They know which scheme they are in.

If you are mapping a Figma file onto this, map the Figma collection onto the definition array and the Figma semantic layer onto role. Where the two disagree, the disagreement is usually a real one worth settling with the designer — a Figma library with fourteen greys and a theme with one text role means somebody has been picking greys by eye.

Type and spacing have no equivalent, and that is fine

There is no scale token type. font_picker gives you a font object per role — a header font and a body font, conventionally — and the filters do the rest. font_face "generates a CSS @font_face declaration to load the provided font" and takes an optional font_display parameter, so {{ settings.type_header_font | font_face: font_display: 'swap' }} is the whole loading story. font_modify derives a variant: it takes a property and a value, accepts weights of 100900, normal, bold, relative adjustments from +100 to -900, and lighter or bolder, and it returns nil when the variant does not exist. That last behaviour matters — a bold variant that a merchant-selected font does not have will silently produce nothing unless you pair it with the default filter.

Sizes, spacing and radii have nothing comparable. The temptation is to fill the gap with range settings: a base font size, a scale ratio, a spacing unit, a border radius, section padding top and bottom. Resist most of it.

A type scale is a set of relationships, and a merchant who moves one end of it has no way to see what happened at the other. Put the scale in CSS as custom properties on :root, expose at most a base size and a heading weight, and let everything else derive. The same for spacing: one --space unit and a documented multiplier series beats eight exposed numbers, because eight exposed numbers will be set to eight unrelated values within a month of handover.

Section padding is the exception, and it is worth conceding. Merchants genuinely need to tighten or loosen a specific section when a campaign layout demands it, and denying that just produces a support ticket. Expose it per section, as a range with sane bounds, and keep the global rhythm in CSS.

The Figma-to-theme pipeline most teams should not build

The appealing version of this is automated: variables change in Figma, a job pulls them, a script writes settings_schema.json and a CSS file, a pull request appears. It is a good idea roughly as often as it is proposed.

The first obstacle is access. Figma’s documentation on the Variables REST API is unambiguous: "To use this API, you must have a Full seat in an Enterprise org; guests cannot use the API." GET endpoints need View access and the file_variables:read scope; POST endpoints need Edit access and file_variables:write. If you are not on Enterprise, the pipeline does not exist and the question is settled.

The second obstacle is that the pipeline solves a problem most stores do not have. Tokens change rarely. A brand refresh is an event, not a cadence. Automating a quarterly hand-edit of a JSON file costs more to maintain than the hand-edits cost to make, and the automation itself becomes a thing that breaks quietly — a schema written by a script is a schema nobody reads, and settings_schema.json is a file where a bad key silently disables a control in the editor.

What is worth building is smaller: a single written mapping, checked into the repo next to the theme, that says which Figma variable corresponds to which setting ID and which CSS custom property. It takes an hour, it survives the designer leaving, and it makes a brand refresh a review rather than an archaeology exercise. If tokens genuinely change weekly — a multi-brand estate, several themes sharing one library — then build the pipeline, and build it one direction only, from Figma to the repo, with a human approving the diff.

Deciding what the merchant owns

This is the decision the whole exercise is actually about, and it is not a technical one. Every token in the theme editor is a value someone can set badly, at four o'clock on a Friday, on a live store.

A rough rule that has held up: expose values with a business reason to change and a visible failure mode. Brand colours, the two fonts, section padding, a logo width. Do not expose values whose failure is subtle — a line height, an individual heading size, a shadow opacity, a grid gutter. Nobody notices a slightly wrong line height in the editor preview; everybody notices it on the product page six weeks later, and nobody can say when it changed.

The contrast problem deserves naming explicitly. A colour scheme where a merchant sets text and background two steps apart passes every technical check in the theme and fails every accessibility one. Shopify does not validate this for you. If the theme has a scheme picker, someone on the team needs to check the schemes after handover, and the schemes are the right place to check because there are six of them rather than sixty sections.

One more constraint worth knowing while you are designing the schema: most setting types can be conditionally shown with the visible_if attribute, but conditional settings "cannot access runtime context or resolved data source values". Conditions are about the shape of the schema, not about the page.

What we would actually do on a new build

Four steps, in order, and none of them involve a build tool.

  • Write the CSS custom properties first, on :root, as the complete scale — colour, type, spacing, radius, shadow. This is the source of truth and it lives in the theme.
  • Define a color_scheme_group with a definition array matching the brand palette and a role map that covers text, background, links, icons and both button pairs. Emit one class per scheme that sets the colour custom properties.
  • Expose the short list: the schemes, two font_picker settings, and per-section padding. Everything else stays in CSS.
  • Check in a one-page mapping from Figma variable names to setting IDs and custom property names, and review it whenever the brand changes.

The honest position

Design tokens on Shopify are mostly a naming and governance problem wearing an engineering costume. The platform supplies the mechanism; the hard part is agreeing that a colour called "Grey 400" in Figma is the thing called text in the theme, and that nobody adds a fifteenth grey without a conversation.

A theme whose colours all resolve to a scheme, whose type derives from one scale, and whose exposed settings fit on a single screen is easier to hand over, cheaper to maintain and far less likely to drift than one with a generated token pipeline and eighty settings. If you are choosing between building the pipeline and writing the mapping down, write the mapping down.

Questions this raises

Where do design tokens live in a Shopify theme?

In two places that should agree. CSS custom properties hold the full scale — colour, type, spacing, radius — and `config/settings_schema.json` declares the subset a merchant may change, with the chosen values stored in `settings_data.json` and read in Liquid as `{{ settings.<id> }}`. The theme’s CSS should read the settings, not duplicate them.

What is a color_scheme_group and why use it instead of individual colour settings?

It is a setting type that outputs a colour scheme composed of `header`, `color` and `color_background` settings. A `definition` array holds the raw colours and a required `role` object maps them to semantic slots — text, background, links, icons and the primary and secondary button pairs. It gives you a semantic token layer plus a scheme preview in the editor, which individual colour settings do not.

Can you sync Figma variables to a Shopify theme automatically?

Only with Enterprise access on the Figma side. Figma documents that using the Variables REST API requires a Full seat in an Enterprise org, with the `file_variables:read` and `file_variables:write` scopes. Below that tier there is no supported programmatic read, and for most stores a written mapping between Figma variable names and theme setting IDs is cheaper to maintain than a pipeline anyway.

How do you handle font tokens when merchants can pick the font?

Use `font_picker` for the roles you want to be changeable, load with the `font_face` filter — it generates the `@font_face` declaration and takes an optional `font_display` — and derive weights and styles with `font_modify`. Guard the derivations: `font_modify` returns `nil` when the requested variant does not exist for the selected font, so pair it with the `default` filter rather than assuming a bold exists.

Should every spacing value be a theme setting?

No. Spacing is a set of relationships, and exposing each step lets a merchant set values that have no relationship to each other. Keep the scale in CSS custom properties, expose section padding where campaign layouts genuinely need it, and keep the global rhythm out of the editor.

Do tokens make a theme accessible?

They make it auditable, which is not the same thing. A colour scheme can be perfectly tokenised and still fail contrast, and Shopify does not check the schemes a merchant creates. The advantage of schemes is that there are a handful of them to review rather than every section on the site.

NEXT STEP

Free store audit

A senior Shopify engineer reviews your storefront, theme performance and checkout, then sends a prioritised list of fixes.