Design System Problems

Composite Tokens Explained

January 15, 2026 • 5 min read

Composite Tokens Explained

Composite tokens explained in practical terms means understanding how tokens can bundle multiple related properties into a single unit. Unlike simple tokens that store single values, composite tokens group values that naturally belong together, such as all properties defining a typography style or shadow definition. This grouping maintains design coherence while simplifying token usage.

What Are Composite Tokens

Composite tokens are tokens whose values contain multiple properties rather than a single value. A typography composite token might include font family, size, weight, line height, and letter spacing. A shadow composite token might include offset, blur, spread, and color values.

Composite tokens capture design decisions that involve multiple interdependent values. Changing one property often requires adjusting others to maintain visual balance, so grouping them ensures they evolve together.

How Composite Tokens Work

Composite token definitions use structured values:

Typography composite:

{
  "typography": {
    "heading": {
      "lg": {
        "$type": "typography",
        "$value": {
          "fontFamily": "{font.family.sans}",
          "fontSize": "{font.size.2xl}",
          "fontWeight": "{font.weight.bold}",
          "lineHeight": "{line.height.tight}",
          "letterSpacing": "{letter.spacing.tight}"
        }
      }
    }
  }
}

Shadow composite:

{
  "shadow": {
    "md": {
      "$type": "shadow",
      "$value": [
        {
          "offsetX": "0px",
          "offsetY": "4px",
          "blur": "6px",
          "spread": "-1px",
          "color": "rgba(0, 0, 0, 0.1)"
        },
        {
          "offsetX": "0px",
          "offsetY": "2px",
          "blur": "4px",
          "spread": "-2px",
          "color": "rgba(0, 0, 0, 0.1)"
        }
      ]
    }
  }
}

Transformation unpacks composites for platform consumption:

CSS output:

.heading-lg {
  font-family: var(--font-family-sans);
  font-size: var(--font-size-2xl);
  font-weight: var(--font-weight-bold);
  line-height: var(--line-height-tight);
  letter-spacing: var(--letter-spacing-tight);
}

:root {
  --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1);
}

Key Considerations

Common Questions

When should composite tokens be used versus individual tokens?

Composite tokens suit properties that are designed together and should change together. Individual tokens suit properties that vary independently.

Use composites for:

Use individual tokens for:

Hybrid approaches can provide both. Individual tokens define the atomic values, while composite tokens bundle them for convenience:

{
  "font": {
    "size": { "lg": { "$value": "18px" } },
    "weight": { "bold": { "$value": "700" } }
  },
  "typography": {
    "body": {
      "$value": {
        "fontSize": "{font.size.lg}",
        "fontWeight": "{font.weight.bold}"
      }
    }
  }
}

How should composite tokens be transformed?

Transformation converts composite structure to platform-specific syntax.

CSS shorthand for simple composites:

// Shadow composite to CSS
function shadowToCSS(shadowToken) {
  return shadowToken.$value
    .map(s => `${s.offsetX} ${s.offsetY} ${s.blur} ${s.spread} ${s.color}`)
    .join(', ');
}

Multiple properties for expanded output:

// Typography composite to CSS properties
function typographyToCSS(typoToken, tokenName) {
  return `
.${tokenName} {
  font-family: ${typoToken.$value.fontFamily};
  font-size: ${typoToken.$value.fontSize};
  font-weight: ${typoToken.$value.fontWeight};
  line-height: ${typoToken.$value.lineHeight};
  letter-spacing: ${typoToken.$value.letterSpacing};
}`;
}

Platform-specific structures for native:

// Typography composite to Swift
struct Typography {
  let font: UIFont
  let lineHeight: CGFloat
  let letterSpacing: CGFloat
}

Style Dictionary requires custom transforms and formats for composite handling beyond basic types.

What about partial composite overrides?

Sometimes only one property of a composite needs to change for a specific context.

Spread and override pattern:

const headingLgEmphasis = {
  ...tokens.typography.heading.lg,
  fontWeight: tokens.font.weight.black
};

CSS with individual property override:

.heading-lg-emphasis {
  /* Apply full composite */
  font-family: var(--typography-heading-lg-family);
  font-size: var(--typography-heading-lg-size);
  /* ... */

  /* Override specific property */
  font-weight: var(--font-weight-black);
}

Composite variants predefine expected overrides:

{
  "typography": {
    "heading": {
      "lg": { "$value": { "fontWeight": "{font.weight.bold}" } },
      "lg-emphasis": { "$value": { "fontWeight": "{font.weight.black}" } }
    }
  }
}

The approach depends on how frequently partial overrides occur and whether they represent intentional design decisions that warrant named tokens.

Summary

Composite tokens bundle multiple related properties into single units, maintaining design coherence for complex attributes like typography and shadows. Token definitions use structured values, while transformations unpack composites into platform-specific syntax. Composites suit properties designed together, while individual tokens serve independently varying values. Partial overrides can be handled through spread patterns, CSS property overrides, or predefining composite variants.

Buoy scans your codebase for design system inconsistencies before they ship

Detect Design Drift Free
← Back to Token Management