Skip to content

Export & Import

Export formats

Theme Settings dialog lists eleven export formats side-by-side; each row has Preview and Download buttons. Preview opens a full-screen read-only overlay with syntax highlighting (highlight.js, common bundle — JSON/CSS/JS/SCSS/Swift/Kotlin/XML/Markdown), per-line gutter numbers, colour chips next to every recognised literal (hex, rgb[a](...), Swift/Compose Color(red:…, green:…, blue:…)), and Copy-to-clipboard. Download saves a single file or zips multi-file bundles client-side via jszip.

The preview header carries a search field with a live match counter and prev/next chevron buttons (Enter / Shift-Enter cycle, Escape clears). Matches are highlighted amber inline; the active one gets a thicker amber + outline. For multi-file bundles, the query persists across file switches and the file list on the left shows an amber dot next to every file that contains a hit — typing a query auto-jumps to the first matching file when the current one has no matches. The About panel (Help → About) reuses the same search header pattern across its sections (sidebar dots flag matched sections), and its own usage examples are rendered through the same highlight.js pipeline so light/dark mode tokens look identical to the export preview.

Every format reads the current preview mode (previewModeId) and emits that mode first — the plugin view and export stay aligned. Missing previewModeId falls back to the first enabled mode.

FormatFilenameWhat's inside
Systema JSON{name}.systema.jsonFull re-importable config (theme + version). Source of truth.
Design Tokens (W3C DTCG){name}.tokens.json (single mode) / {name}.dtcg.zip (multi-mode: light.tokens.json + dark.tokens.json + index.json)W3C DTCG format with $value / $type / $description leaves. Works with Tokens Studio, Style Dictionary 4+. Meta docs duplicated into each mode file via the default-bucket fallback so every file is self-contained.
Style Dictionary (legacy){name}.style-dictionary.json (single) / {name}.{mode}.style-dictionary.json zippedPre-DTCG Amazon Style Dictionary structure ({ value, comment }). For older SD pipelines that haven't migrated to DTCG.
CSS variables{name}.css:root baseline + [data-theme="<mode>"] override per additional mode + @media (prefers-color-scheme: dark) { :root:not([data-theme]) { … } } system fallback.
Tailwind v3 config{name}.tailwind.config.jsmodule.exports = { theme: { extend: { colors } } } with the baseline mode palette (color/ prefix stripped so consumers write bg-brand / bg-surfaces-0). Works with Tailwind v4 too via @config "./path".
Tailwind v4 CSS{name}.tailwindv4.cssNative v4 @theme { --color-*: ... } block. Import into main CSS; utilities like bg-color-brand appear automatically. [data-theme="dark"] blocks override the same variable names.
SCSS variables{name}.scss$kebab-case variable per token, using the baseline mode. Additional modes emit @mixin mode-{name} { ... !global } blocks the consumer wires up scope-by-scope.
SwiftUI{name}.swiftenum Theme { static let brandPrimary = Color(red: …, green: …, blue: …, opacity: …) } with Theme{PascalMode} companions per extra mode.
Jetpack Compose{name}.ktobject Theme { val brandPrimary = Color(red = …f, green = …f, blue = …f, alpha = …f) } + Theme{PascalMode} objects.
Android XMLvalues/{name}_colors.xml + values-night/… (dark) + values-<slug>/… (other modes) in a zipGradle-convention folder hints baked into the zip paths; drop-in to app/src/main/res/. ARGB hex format.
DESIGN.md{name}.DESIGN.mdYAML front-matter (colors: map) + markdown body (Overview, Colors, Agent Prompt Guide). Google Stitch / VoltAgent DESIGN.md format — drop into project root so AI coding agents (Claude Code, Cursor, v0) use it as the visual-identity source of truth.

The Systema JSON remains the round-trip shape for re-import via the Add Theme dialog. All other writers derive from generateAllTokensWithStats(theme) — single source of truth for value generation — so extending a format means reading the same token list differently.

Meta tokens for documentation

Every export format (except the binary zip-wrappers) carries a meta prefix of documentation variables so the target system sees the theme's shape at a glance:

  • theme-name, optional description
  • color/contrast-model, color/collection-structure
  • color/palette/count, color/modes/count, color/interactions/count, color/conditions/count
  • Per-mode details: color/modes/{name}/seed-color | direction | contrast-factor | chroma-factor
  • Per-entity structure: color/{entityName}/contrast-ratios | level-count | pattern | contrast-step | direction | colors | preview-shape | parents | calculate-on | interactions | conditions
  • color/meta/seed-color — multi-mode COLOR variable (only entry outside the single-mode meta collection)

Meta emits unconditionally into Figma variables — the meta and color/meta collections are always written on push, so a Figma reader can open any file and see what theme it holds. In export files (CSS, DTCG, Tailwind, Swift, Compose, etc.) meta is off by default to keep outputs terse; flip Settings → Helpers → Export → Export meta tokens to include it. Tokens Studio / Style Dictionary consumers who use meta to drive codegen should turn it on before downloading.

How to use each export

Each section below is written as "you downloaded the file — now what?". All examples assume the default theme.color prefix (color/brand-primary, color/surfaces-0/brand, …). When you toggle Export meta tokens on, additional --theme-name / --color-contrast-model / --color-{entity}-* variables appear alongside the palette.

Systema JSON

(.systema.json). Round-trip format — reopen in any Systema instance via Add Theme → Import (welcome screen) or Theme Settings → Import (replace current theme, keep name). This is the only format Systema itself re-imports.

Design Tokens / W3C DTCG

(.tokens.json or .dtcg.zip). Feeds W3C-compliant pipelines:

  • Tokens Studio (Figma plugin): Settings → JSON → Load → pick light.tokens.json (or each mode file one-by-one as sets).

  • Style Dictionary ≥ 4.0: npm i -D style-dictionary@4, drop files into tokens/, then:

    js
    // config.json
    { "source": ["tokens/*.json"],
      "platforms": { "css": { "transformGroup": "css", "files": [{ "destination": "vars.css", "format": "css/variables" }] } } }

    npx style-dictionary build.

  • Supernova / Specify / Zeroheight: import the zip as a token set; each mode becomes a collection.

Style Dictionary (legacy)

(.style-dictionary.json). Older SD 3.x pipelines that haven't migrated to DTCG. Same recipe as DTCG above but target Style Dictionary 3.x in package.json.

CSS variables

(.css). Drop-in custom properties. Step 1 — include the file and pick a mode:

html
<!-- index.html -->
<link rel="stylesheet" href="./theme.css" />
<html data-theme="dark"><!-- omit attribute to use prefers-color-scheme --></html>

Step 2 — name mapping. Every Systema token path becomes one CSS custom property under --color-* (slashes turn into hyphens):

text
color/brand                       → --color-brand
color/brand/hovered               → --color-brand-hovered
color/brand/pressed               → --color-brand-pressed
color/brand/focused               → --color-brand-focused
color/brand/disabled              → --color-brand-disabled
color/surfaces-0                  → --color-surfaces-0
color/surfaces-0/on-brand         → --color-surfaces-0-on-brand
color/containers/surfaces-0/card  → --color-containers-surfaces-0-card

Step 3 — a full card-on-surface example showing nesting + every interaction state. Each declaration answers "what does this thing draw against, and what content sits on top":

css
/* Page sits ON surface 0 */
body {
  background: var(--color-surfaces-0);
  color: var(--color-surfaces-0-content);
}

/* Card sits ON surface 0, so its tokens live under containers/surfaces-0/card */
.card {
  background: var(--color-containers-surfaces-0-card);
  color: var(--color-containers-surfaces-0-on-card);
  border: 1px solid var(--color-containers-surfaces-0-card-border);
  border-radius: 8px;
  padding: 16px;
}
.card .secondary {
  color: var(--color-containers-surfaces-0-on-card-secondary);
}

/* Brand button — every interaction state has its own token */
.button-brand {
  background: var(--color-brand);
  color: var(--color-surfaces-0-on-brand);
  padding: 8px 16px;
  border-radius: 6px;
}
.button-brand:hover    { background: var(--color-brand-hovered); }
.button-brand:active   { background: var(--color-brand-pressed); }
.button-brand:focus    { background: var(--color-brand-focused);
                         outline: 2px solid var(--color-brand-focus-ring); }
.button-brand:disabled { background: var(--color-brand-disabled);
                         color: var(--color-surfaces-0-on-brand-disabled);
                         cursor: not-allowed; }

Step 4 — flip mode at runtime from JS:

js
document.documentElement.setAttribute("data-theme", isDark ? "dark" : "light");

// Or, attach to a system-preference listener:
const mq = matchMedia("(prefers-color-scheme: dark)");
mq.addEventListener("change", (e) => {
  document.documentElement.setAttribute("data-theme", e.matches ? "dark" : "light");
});

The @media (prefers-color-scheme: dark) fallback inside the exported file kicks in automatically when data-theme isn't set.

Tailwind v3 config

(.tailwind.config.js). Step 1 — wire as a preset in your own config:

js
// tailwind.config.js
const systema = require("./my_theme.tailwind.config.js");

module.exports = {
  presets: [systema],
  content: ["./src/**/*.{html,tsx,jsx}"],
};

Step 2 — name mapping. Token paths translate 1:1 into utility classes (slashes → hyphens):

text
color/brand                       → bg-brand     text-brand     border-brand
color/surfaces-0                  → bg-surfaces-0
color/surfaces-0/on-brand         → text-surfaces-0-on-brand
color/containers/surfaces-0/card  → bg-containers-surfaces-0-card

# Interaction states append the suffix:
color/brand                       → bg-brand
color/brand/hovered               → bg-brand-hovered      (use as hover:bg-brand-hovered)
color/brand/pressed               → bg-brand-pressed      (use as active:bg-brand-pressed)
color/brand/focused               → bg-brand-focused      (use as focus:bg-brand-focused)
color/brand/disabled              → bg-brand-disabled     (use as disabled:bg-brand-disabled)

Step 3 — a complete card-on-surface example. The page sits on a surface, the card sits on top with its own background + border + on-color, and the brand button rolls through every interaction state via Tailwind variants:

jsx
// Card.tsx
export function Card({ title, body, onContinue, disabled }) {
  return (
    // 1. Page background = surface 0 itself
    <div className="bg-surfaces-0 text-surfaces-0-content min-h-screen p-8">

      {/* 2. Card sits ON surface 0, so its tokens live under
              color/containers/surfaces-0/... */}
      <div className="bg-containers-surfaces-0-card
                      text-containers-surfaces-0-on-card
                      border border-containers-surfaces-0-card-border
                      rounded-lg p-4 max-w-md">

        <h2 className="text-base font-semibold mb-2">{title}</h2>
        <p className="text-containers-surfaces-0-on-card-secondary mb-4">{body}</p>

        {/* 3. Brand button — every state suffix wired through Tailwind variants.
              No JS state machine needed, the browser flips the class. */}
        <button
          type="button"
          onClick={onContinue}
          disabled={disabled}
          className="px-4 py-2 rounded font-medium
                     bg-brand text-surfaces-0-on-brand
                     hover:bg-brand-hovered
                     focus:bg-brand-focused focus:outline-none
                       focus:ring-2 focus:ring-brand-focus-ring
                     active:bg-brand-pressed
                     disabled:bg-brand-disabled
                     disabled:text-surfaces-0-on-brand-disabled
                     disabled:cursor-not-allowed"
        >
          Continue
        </button>

      </div>
    </div>
  );
}

Step 4 — switch modes. The v3 preset emits one palette per mode under colors; the runtime selector is up to you (data-attribute, class, or CSS-variables shim). Most teams use the CSS-variables export for runtime mode switching and the Tailwind config purely as a name map. Also works in Tailwind v4 via @config "./my_theme.tailwind.config.js".

Tailwind v4 CSS

(.tailwindv4.css). Step 1 — import alongside Tailwind:

css
/* app.css */
@import "tailwindcss";
@import "./my_theme.tailwindv4.css";

Step 2 — v4 prefixes every token with its variable namespace (--color-*), so utilities all start with bg-color- / text-color- / border-color-:

text
color/brand                       → bg-color-brand
color/brand/hovered               → bg-color-brand-hovered      (hover:bg-color-brand-hovered)
color/surfaces-0/on-brand         → text-color-surfaces-0-on-brand
color/containers/surfaces-0/card  → bg-color-containers-surfaces-0-card

Step 3 — same card example, just with the v4 prefix. Structure is identical:

jsx
// Card.tsx (Tailwind v4)
export function Card({ title, body, onContinue }) {
  return (
    <div className="bg-color-surfaces-0 text-color-surfaces-0-content min-h-screen p-8">
      <div className="bg-color-containers-surfaces-0-card
                      text-color-containers-surfaces-0-on-card
                      border border-color-containers-surfaces-0-card-border
                      rounded-lg p-4 max-w-md">
        <h2 className="text-base font-semibold mb-2">{title}</h2>
        <p className="text-color-containers-surfaces-0-on-card-secondary mb-4">{body}</p>
        <button
          type="button"
          onClick={onContinue}
          className="px-4 py-2 rounded font-medium
                     bg-color-brand text-color-surfaces-0-on-brand
                     hover:bg-color-brand-hovered
                     focus:bg-color-brand-focused focus:outline-none
                     active:bg-color-brand-pressed
                     disabled:bg-color-brand-disabled"
        >
          Continue
        </button>
      </div>
    </div>
  );
}

Step 4 — switch modes at runtime. The exported file emits [data-theme="dark"] { --color-*: ...; } blocks, so flipping the attribute on <html> swaps every utility instantly:

ts
// useThemeMode.ts
export function setMode(mode: "light" | "dark") {
  document.documentElement.setAttribute("data-theme", mode);
}

// Usage:
setMode(prefersDark ? "dark" : "light");

SCSS variables

(.scss). Step 1 — @use (or @import on older Sass) the generated partial:

scss
// main.scss
@use "./my_theme" as theme;

.button {
  background: theme.$brand;
  color: theme.$surfaces-0-on-brand;

  &:hover { background: theme.$brand-hovered; }
}

Step 2 — for extra modes, wrap a scope in the generated mixin (one mixin per non-baseline mode):

scss
.dark-surface {
  @include theme.mode-dark;
  // everything inside resolves to dark-mode values
  background: theme.$surfaces-0;
}

SwiftUI

(.swift). Step 1 — drop my_theme.swift into your Xcode target. Step 2 — use inside any View:

swift
// PrimaryButton.swift
import SwiftUI

struct PrimaryButton: View {
  let title: String
  let action: () -> Void
  @Environment(\.colorScheme) var colorScheme

  var body: some View {
    Button(action: action) {
      Text(title)
        .foregroundColor(colorScheme == .dark ? ThemeDark.surfaces0OnBrand : Theme.surfaces0OnBrand)
        .padding(.horizontal, 16)
        .padding(.vertical, 8)
        .background(colorScheme == .dark ? ThemeDark.brand : Theme.brand)
        .cornerRadius(8)
    }
  }
}

If your theme has only one mode, the Theme{Mode} companion enums aren't emitted — use Theme.brand directly.

Jetpack Compose

(.kt). Step 1 — drop my_theme.kt into app/src/main/java/design/systema/<themeslug>/. Step 2 — pick the mode palette and pass it into Material components:

kotlin
// PrimaryButton.kt
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable

@Composable
fun PrimaryButton(text: String, onClick: () -> Unit) {
  val palette = if (isSystemInDarkTheme()) ThemeDark else Theme
  Button(
    onClick = onClick,
    colors = ButtonDefaults.buttonColors(
      containerColor = palette.brand,
      contentColor = palette.surfaces0OnBrand,
    ),
  ) {
    Text(text)
  }
}

Android XML

(.android-xml.zip). Step 1 — unzip into app/src/main/res/. You get:

text
res/
  values/        my_theme_colors.xml   ← baseline mode (applied by default)
  values-night/  my_theme_colors.xml   ← dark mode (Android auto-picks when device is in dark)
  values-hicon/  my_theme_colors.xml   ← any extra mode (e.g. "High Contrast")

Step 2 — reference colours from XML layouts:

xml
<!-- res/layout/button.xml -->
<com.google.android.material.button.MaterialButton
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Save"
    android:textColor="@color/surfaces_0_on_brand"
    app:backgroundTint="@color/brand" />

Step 3 — or from Kotlin code:

kotlin
val brand = ContextCompat.getColor(context, R.color.brand)
binding.root.setBackgroundColor(
    ContextCompat.getColor(context, R.color.surfaces_0)
)

Android picks the right values-*/ folder automatically based on device configuration (system dark, uiMode, etc.) — no runtime switching code needed for the values-night/ case.

DESIGN.md

(.DESIGN.md). Step 1 — rename to plain DESIGN.md and drop into your project root (the filename is the convention AI agents look for):

text
my-app/
├── DESIGN.md        ← here
├── src/
├── package.json
└── …

Step 2 — tell your AI coding agent to use it. Examples:

text
Claude Code (CLI):   read DESIGN.md and build a pricing page that matches
Cursor:              cmd-L → "use DESIGN.md for styling, build a Button component"
v0.dev / Lovable:    paste the file contents into the prompt and request components

Step 3 — the agent reads the YAML front-matter for exact hex values and the prose body for intent. Token names in colors: map to CSS vars / Tailwind classes one-to-one (slashes replaced with hyphens), so agent output looks like:

css
/* agent-generated */
.button { background: var(--color-brand); color: var(--color-surfaces-0-on-brand); }

Re-export from Systema with a different preview mode selected to get a DESIGN.md for that mode (dark, high-contrast, …) — the YAML values swap to the active mode's palette. See awesome-design-md for community examples and the spec for the full format reference.

Systema JSON envelope shape

Every exported Systema file is a v2 envelope: { version: 2, theme: { id, name, color: { colors, entities, interactions, conditions, modes, settings: { seedColor, seedDirection, collectionStructure, figmaVariablesPublishing, contrastModel } } } }. Future categories (type, size, layout) will appear as sibling fields next to color, each with their own settings subgroup. The importer accepts only this envelope shape — v1 flat-shape JSON and bare Theme objects are no longer supported.

Import

Three import paths:

  • Welcome screen Import tab -- accepts a .json file and creates a new theme. Unique ID assigned, optional fields default gracefully.

  • Theme Settings dialog Import -- accepts a .json file and replaces all settings in the current theme while keeping the existing theme name. All tabs (Interactions, Contexts, Modes, Entities) now use the same unified import UX as the Palette: radio card theme selection, per-item checkboxes for selective import, and a "Load theme file (.json)" button that adds to the list. Entities import is always "replace" (no merge option) and is labeled "Import entity settings". Entity types show readable category names (Surfaces, Containers, Content, Scrims) instead of internal types (level, frame, top-layer, overlay).

  • Import Colors dialog -- a dedicated dialog for importing colors into an existing theme. Three tabs:

    • Theme -- radio card selection from available themes to pull colors from.
    • Paste -- multi-format parser that accepts: hex, rgb, hsl, hct, oklch, hsb/hsv, JSON arrays/objects, CSS/SCSS/LESS variables, Adobe Color XML (.ase files), Coolors & ColorHunt URLs.
    • Image -- Canvas-based palette extraction from uploaded images.

    Each parsed color gets a per-color checkbox for selective import. Names are auto-assigned from the 31k entry color-name-list database.


All rights reserved.