Skip to content

Updated   11 min read

How one Figma token becomes SwiftUI, Compose, Flutter and CSS

One Figma variable, brand/base, followed into CSS, SwiftUI, Compose and Flutter: where each code name comes from, what a mode becomes, what cannot translate.

The Flutter and SwiftUI app icons side by side on a grey textured ground, with a cursor labeled Dev pointing at SwiftUI
On this page
  1. Design tokens
  2. The W3C format
  3. The token file
  4. Code names
  5. Four outputs
  6. Light and Dark
  7. Type and motion
  8. What does not translate
  9. Descriptions for agents
  10. Questions
  11. Sources

One Figma variable, brand/base, becomes var(--ap-brand-base) in CSS, Color.apBrandBase in SwiftUI, AppetiteTheme.colors.brandBase in Jetpack Compose and apBrandBase in Flutter. All four come from one JSON file in the W3C Design Tokens format, compiled by Style Dictionary v5, and three of the four names are stored on the variable in Figma, so the name a designer picks is the name a developer types.

Appetite UI is a Figma UI kit for iOS and Android apps, built like a design system. One token source drives Apple and Material components in light and dark mode, and exports to SwiftUI, Jetpack Compose, Flutter and CSS. This article follows one of its tokens through that export, with the values the code output docs print.

A token is a name the designer and the developer both read.

A design token is a named value that lives in one place and is read everywhere else: a variable in Figma, and a custom property, a static constant or a theme field in code. #155DFC in a handoff note says nothing about where it belongs. brand/base says what the value is for, and when the value changes, nothing that reads the name has to change.

In Appetite UI, brand/base is a semantic token that points at a primitive, colors/blue/500. Components bind to semantic tokens only, so a component never names a color (introduction).

One token, its source and its four generated names
WhereName
Figma, Color Tokensbrand/base
CSSvar(--ap-brand-base)
SwiftUIColor.apBrandBase
Jetpack ComposeAppetiteTheme.colors.brandBase
FlutterapBrandBase

The file format has been a W3C community standard since October 2025.

The token file is JSON in the format of the W3C Design Tokens Community Group. Its first stable version, 2025.10, was published as a Final Community Group Report on October 28, 2025, and the announcement lists Figma, Style Dictionary, Tokens Studio and Penpot among the tools that support it.

The specification is split into modules. The Format Module defines a token as an object with a $value, a $type unless its group already sets one, an optional $description, and an optional $extensions object where tools may keep data of their own. A reference to another token is its path in curly braces, {colors.blue.500}. Themes such as light and dark live in a second document, the Resolver Module, which describes them as modifiers with named contexts.

Style Dictionary, the build tool the export runs on, has had first-class support for the format since version 4. Its own page says full support for 2025.10 is still work in progress in v5. One difference shows in the sample below: the file writes a color as a hex string, the form Style Dictionary v5 compiles, while the 2025.10 report defines a color as an object with a color space, its components and an optional hex.

Primitives hold the value, semantic tokens hold the decision.

The docs print the smallest file that shows the whole idea: one primitive, one semantic token and one reference between them.

The token filetwo entries, one reference
{
  "colors": {
    "blue": {
      "500": { "$type": "color", "$value": "#155DFC" }
    }
  },
  "brand": {
    "base": { "$type": "color", "$value": "{colors.blue.500}" }
  }
}

colors.blue.500 is the only place #155DFC is written. brand.base holds a reference to it. Figma writes the same path with slashes, brand/base, and the JSON writes it with dots: the same token in two notations, and the export is where they meet.

The full source carries the file's 6 collections, each with its own modes. A mode is a second value on the same token, which is why Light and Dark, Brand and System, and the three device modes all fit in one source:

The token source by collection, with its variables and modes
CollectionVariablesModes
Foundations196Default
Color Tokens161Light, Dark
Typography28Default
Motion31Default
Platform16ios-md, ios-lg, android
Platform Bridge112Brand, System

Three of the four names are typed into Figma, one is derived.

Figma lets a variable carry code syntax: one name per platform, Web, Android and iOS, entered in the Edit variable dialog. Dev Mode prints those names in its code snippets, which Figma supports for variables in CSS, SwiftUI and Compose.

Every variable in Appetite UI stores its own code name for all three platforms, and the export reads the names off the variable. AppetiteTheme.colors.brandBase reads like Compose because it was written as Compose, once, on the variable. Since release 2.4 the Platform Bridge roles, the Apple and Material slots, carry SwiftUI, Compose and CSS syntax too (changelog).

Figma's Edit variable dialog for brand/base: blue/500 in Light and blue/400 in Dark, and the code syntax var(--ap-brand-base) for Web, Color.apBrandBase for iOS and AppetiteTheme.colors.brandBase for Android
The code names are part of the variable, so Dev Mode, the export and an agent quote the same string.

Figma has no slot for Dart, so the Flutter name, apBrandBase, is the one name in the set that the export derives instead of reading it off the variable.

I keep the names on the variables so there is one place to change them. When a name changes, the next export carries it, Dev Mode shows it the same day, and nobody maintains a mapping table between the design file and the codebase.

One source compiles into four languages and four units.

Style Dictionary reads the JSON, resolves every reference to its final value and writes one output per platform. The code output docs and the tokens page cut the output to three tokens so the four languages sit side by side: a color, a spacing step and a duration.

CSS custom propertiesweb
:root {
  --ap-brand-base: #155DFC;
  --ap-space-4: 16px;
  --ap-duration-fast: 120ms;
}
SwiftUIiOS
extension Color {
  static let apBrandBase = Color(
    red: 0.082, green: 0.365, blue: 0.988)
}
enum AppetiteSpacing { static let space4: CGFloat = 16 }
enum AppetiteMotion { static let durationFast = 0.12 }
Jetpack ComposeAndroid
internal val BrandBase = Color(0xFF155DFC)
internal val Space4 = 16.dp
internal val DurationFast = 120

// read as AppetiteTheme.colors.brandBase
FlutterDart
const apBrandBase = Color(0xFF155DFC);
const apSpace4 = 16.0;
const apDurationFast = Duration(milliseconds: 120);

The rows below are brand/base, spacing/4 and motion/duration/fast: the same three values, each in the platform's own unit.

The same three tokens in four languages, value and unit
TokenCSSSwiftUIComposeFlutter
Brand color#155DFCred 0.082, green 0.365, blue 0.9880xFF155DFC0xFF155DFC
Spacing step16px16 points, a CGFloat16.dp16.0 logical pixels
Fast duration120ms0.12 seconds120 milliseconds120 milliseconds, a Duration

SwiftUI writes a color as three components between 0 and 1: 0x15 divided by 255 is 0.082, 0x5D is 0.365 and 0xFC is 0.988. Compose and Flutter write the same hex with the alpha first, FF for fully opaque. The spacing step stays 16 everywhere because each platform counts in its own density-independent unit: CSS pixels, points on iOS, dp on Android and logical pixels in Flutter. The duration changes its number, because SwiftUI animations take seconds and Compose takes milliseconds.

Light and Dark compile into one name with two values.

The samples above show one value per token, the Light one. With both modes, the CSS output keeps one custom property and writes the Dark value into a prefers-color-scheme block:

What the CSS export does with two modesone name, two values
:root {
  --ap-brand-base: #155DFC;
}

@media (prefers-color-scheme: dark) {
  :root { --ap-brand-base: #2B7FFF; }
}

brand/base points at colors/blue/500 in Light and at colors/blue/400 in Dark, and the export writes both hexes under the one property. Nothing that reads --ap-brand-base knows it has two values: the browser picks one from the system setting, which MDN describes as the user's request for "light or dark color themes".

Each platform keeps a second value in a place of its own, and a color with two modes has to arrive there to follow the system setting:

Where each platform keeps a light and a dark value, and what picks one
PlatformWhere both values liveWhat picks one
CSS:root and a prefers-color-scheme blockthe system setting, read by the browser
SwiftUI and UIKita Color Set with light and dark appearancesthe color object, each time it draws
Jetpack ComposelightColorScheme and darkColorSchemeisSystemInDarkTheme()
Fluttertheme and darkTheme on MaterialAppthemeMode

Apple puts the SwiftUI and UIKit row in one sentence: a color object created from a color asset "loads the color variant that matches the current environment settings". How a mode is designed in Figma, and why the Dark value of brand/base is a different blue, is in Dark mode with Figma variables, not duplicate frames.

Type and motion arrive in each platform's own model.

Each platform already has a model for type and motion, and the output writes into it. The SwiftUI output sets type on Dynamic Type text styles, and the Compose output includes a ready Material 3 typography object (FAQ). Apple defines a text style as "a dynamic text style to use for fonts": its size follows the reader's text size setting at runtime, which a Figma file cannot show. The typography docs read every size as the default size class for that reason.

In the file, the Platform Bridge points iOS body at fontSize/lg, 18, and Material bodyLarge at fontSize/base, 16. It carries the size only: line height, weight and tracking stay on the Appetite UI side (Platform Bridge).

Easing curves need the least translation. motion/easing/standard holds cubic-bezier(0.2, 0, 0, 1): CSS reads it as written, and the same four numbers are the arguments of SwiftUI's timingCurve, Compose's CubicBezierEasing and Flutter's Cubic. Durations change their number, as the table above shows, and springs are the part the web cannot take (motion docs).

Some values do not survive the trip, and the file says which.

A token set that exports to four platforms holds values one of them cannot express. Appetite UI marks four kinds where a developer will look:

Values that do not translate, and what the file does with them
TokensIn FigmaIn code
Liquid Glass, liquidGlass/surface, liquidGlass/control and their layersa Glass effect with a refraction of 0.7, 0.8 on the keyboardno web equivalent, because CSS has no refraction; the docs mark every glass layer that way
Backdrop blur, backdropBlur/sm to xlblur radius 8, 16, 24 and 40approximated as blur(8px) to blur(40px); the file keeps them as Figma values, because the two radii were never measured against each other
Material state opacity, M3/sys/state/*hover 8, focus 10, pressed 10, dragged 16, disabled 38 and 12, scrim 32the same bare numbers in both Platform Bridge modes, for the state layer Material draws
Springs, motion/spring/subtle, default and bouncyresponse 0.30, 0.40 and 0.50, damping 0.90, 0.80 and 0.65no CSS equivalent; they stay in the native outputs, and the web takes the easing curves

The effects docs give the instruction for the first row in one line: "Use liquidGlass in the file, reach for the platform's own glass material in code, and treat the token as a Figma instruction rather than a web spec." The state opacities are numbers on purpose. They describe a layer Material draws over a component, and iOS has no such layer to give them to.

Descriptions travel with the values, so an agent reads them too.

A variable in the file carries a written description of its job next to its value. brand/base says "Primary buttons, active states, links. Maps to iOS tint and M3 primary." The file you buy ships a context pack, tokens.md and tokens.json, and the Appetite UI Core Skill, included with it, tells Claude, Cursor or any agent that reads project instructions which token to reach for (AI).

A generated screen and a compiled build then read the same names from the same source. The longer version, with the Core Skill and the Figma MCP, is in How Appetite UI works with AI agents: Claude, Cursor and the Core Skill.

Questions

What is the W3C Design Tokens format?

The W3C Design Tokens format is a JSON format for design tokens from the W3C Design Tokens Community Group, stable since version 2025.10 of October 28, 2025. A token is an object with a $value, a reference is a path in curly braces, and light and dark themes belong to the separate Resolver Module.

Can Figma export variables to DTCG JSON without a plugin?

Figma's native variable export was still reaching accounts in December 2025, when Figma's community support wrote on the Figma forum that it was "still being rolled out". Figma is also on the W3C list of tools that support the format, so check the file menu of your own account. Appetite UI does not depend on it, because the token source and its four outputs ship in the file you buy.

Does Style Dictionary output SwiftUI and Jetpack Compose?

Yes. Style Dictionary's predefined formats include ios-swift/class.swift, ios-swift/enum.swift and ios-swift/any.swift for Swift, which import UIKit by default, with a color/ColorSwiftUI transform for SwiftUI colors; compose/object for Compose and flutter/class.dart for Flutter sit next to css/variables. A team that wants another shape, such as a SwiftUI Color extension, writes a custom format.

How do design tokens handle dark mode in SwiftUI and Compose?

Design tokens reach dark mode through each platform's own theme switch: a Color Set with two appearances on Apple platforms, and a theme function that picks lightColorScheme or darkColorScheme from isSystemInDarkTheme() in Compose. In the token file both values sit on one token as two modes.

Do the token outputs come with Appetite UI?

Yes. The DTCG token source and its CSS, SwiftUI, Jetpack Compose and Flutter outputs ship in the file you buy, with the context pack an agent reads. The code output docs show what each output contains.

Can React Native use the same tokens?

Yes, with one more build. Appetite UI ships CSS, SwiftUI, Compose and Flutter outputs, and no React Native one. The source is DTCG JSON, so a Style Dictionary build with one of its JavaScript formats, such as javascript/es6, can compile it for a React Native project.

Sources

  1. W3C Design Tokens Community Group: Design Tokens specification reaches first stable version, read September 26, 2026
  2. Design Tokens Format Module 2025.10, read September 26, 2026
  3. Design Tokens Resolver Module 2025.10, read September 24, 2026
  4. Style Dictionary: DTCG, read September 26, 2026
  5. Style Dictionary: Predefined formats, read September 26, 2026
  6. Style Dictionary: Predefined transforms, read September 26, 2026
  7. Figma Learn: Create and manage variables and collections (code syntax), read September 26, 2026
  8. Figma Forum: Native variable export feature, read September 26, 2026
  9. MDN: prefers-color-scheme, read September 24, 2026
  10. Apple Developer: Supporting Dark Mode in your interface, read September 24, 2026
  11. Apple Developer: Font.TextStyle, read September 24, 2026
  12. Android Developers: Material Design 3 in Compose, read September 24, 2026
  13. Flutter API: MaterialApp.darkTheme, read September 24, 2026

Written by

Bob Poláček

Designer of Appetite UI. Builds the Figma file, the token architecture and the code output, and answers the support inbox.

Keep reading

  • Guide · Sep 24, 2026 · 9 min read

    Dark mode with Figma variables, not duplicate frames

    Dark mode in Figma without a second set of frames: semantic variables with a Light and a Dark mode, one frame per screen, and the checks to run before handoff.

  • AI · Sep 16, 2026 · 9 min read

    How Appetite UI works with AI agents: Claude, Cursor and the Core Skill

    The Appetite UI Core Skill, included in the file you buy, tells Claude, Cursor and other agents which token to use, so a generated screen reads your values.

  • Guide · Sep 24, 2026 · 11 min read

    How to design one app for iOS and Android in Figma

    Keep one token set, share the components that behave alike, draw bars and sheets once per platform, and switch touch targets with a Figma variable mode.

Your next rebrand is one edit.

One-time payment, Lifetime updates, Unlimited client projects