Design tokens

Design tokens are the foundation of sugarcube. They express design decisions in a platform-agnostic way that both designers and developers can understand. Following the W3C Design Token Format, sugarcube transforms these tokens into CSS variables.

Why design tokens matter

Design tokens solve fundamental problems in modern product and website development:

  1. Shared Language: Tokens create a common vocabulary between designers and developers. When a designer says “use the primary color,” everyone knows exactly what that means, regardless of their tool.

  2. Consistency: By storing design decisions in a structured format, you ensure that colors, spacing, typography, and other design elements are used consistently across your project.

  3. Future-Proofing: As new platforms and tools emerge, your design decisions remain consistent because they’re stored in a platform- and tool-agnostic format.

Token structure

A W3C design token file is a JSON object that can contain both tokens and token groups. Here’s the basic structure:

{
"color": { // Token group
"primary": { // Token
"$value": "#0066cc", // The actual value
"$type": "color", // The type of token
"$description": "Primary brand color" // Optional metadata
}
}
}

Key components

  1. Token groups: Objects that organize related tokens (like color or spacing)
  2. Tokens: Individual design decisions with:
    • $value: The actual value of the token
    • $type: The type of token (required for validation)
    • $description: Optional documentation
    • $extensions: Optional custom metadata

References

Tokens can reference other tokens using the {group.token} syntax:

{
"color": {
"primary": {
"$value": "#0066cc",
"$type": "color"
},
"button": {
"background": {
"$value": "{color.primary}", // References the primary color
"$type": "color"
}
}
}
}

Type inheritance

Token groups can specify a $type that applies to all tokens within them:

{
"spacing": {
"$type": "dimension", // All tokens in this group are dimensions
"small": {
"$value": {
"value": 8,
"unit": "px"
}
}
}
}

CSS variable generation

In sugarcube, CSS variables are automatically generated from the token path. For example:

{
"color": {
"primary": {
"$value": "#0066cc"
}
}
}

Becomes:

--color-primary: #0066cc;

This means the structure of your token groups directly influences your CSS variable names. Plan your token organization accordingly!

For more details about the W3C Design Token Format structure, see the official specification.

Token Types

Note: Sugarcube implements the W3C Design Token Format, which is currently in progress and subject to change. For the most up-to-date and complete specification, please refer to the official W3C documentation. All efforts are made to keep sugarcube up to date with the latest W3C specification. If you find any discrepancies, please open an issue.

Important: Sugarcube enforces strict compliance with the W3C Design Token Format. All tokens are validated against the specification during the token-to-CSS generation pipeline. This validation happens automatically, but you can also validate your tokens manually using the validate command.

Sugarcube supports all W3C token types, plus our own fluidDimension type.

Simple types

TypeDescriptionExample
colorCSS color values"#0066cc"
dimensionFixed size values with units (px, rem){ "value": 8, "unit": "px" }
fluidDimensionResponsive size values that scale between min/max{ "min": { "value": 1, "unit": "rem" }, "max": { "value": 2, "unit": "rem" } }
durationTime values with units (ms, s){ "value": 200, "unit": "ms" }
cubicBezierAnimation timing functions defined by four control points[0.4, 0, 0.2, 1]
fontFamilyFont stack as string or array of strings"Inter, sans-serif" or ["Inter", "sans-serif"]
fontWeightFont weight as number (1-1000) or semantic string400 or "normal"
numberNumeric values without units1.5

Composite types

TypePropertiesDescription
typographyfontFamily
fontSize
fontWeight (optional)
letterSpacing (optional)
lineHeight (optional)
Font combinations with size, weight, and spacing
bordercolor
width
style
Border definitions with color, width, and style
shadowcolor
offsetX
offsetY
blur
spread
inset (optional)
Box shadows with position, blur, and spread
gradientArray of { color, position }Color gradients defined by stops
transitionduration
delay (optional)
timingFunction
Animation transitions with timing
strokeStyleEither predefined keywords or { dashArray, lineCap }Line styles for borders and strokes

Example of a composite type:

{
"typography": {
"heading": {
"$value": {
"fontFamily": "Inter, sans-serif",
"fontSize": { "value": 24, "unit": "px" },
"fontWeight": 700,
"lineHeight": 1.2
},
"$type": "typography"
}
}
}

Fluid dimensions

Sugarcube extends the W3C spec with a fluidDimension type that generates CSS clamp() values. This allows you to define dimensions that scale between a minimum and maximum size:

{
"spacing": {
"small": {
"$value": {
"min": { "value": 1, "unit": "rem" },
"max": { "value": 2, "unit": "rem" }
},
"$type": "fluidDimension"
}
}
}
--spacing-small: clamp(1rem, 1rem + 1vw, 2rem);

The value will fluidly scale between 1rem and 2rem based on viewport width.

The viewport width min and max can be configured in the sugarcube.config.json file. See the configuration documentation for more details.

Composite Token Patterns

When working with composite tokens (like borders, shadows, and gradients), sugarcube uses a layered approach:

  1. Base Properties: Individual properties defined in the token system

    {
    "stroke": {
    "width": {
    "thin": { "$value": { "value": 1, "unit": "px" } }
    },
    "style": { "$value": "solid" },
    "color": {
    "primary": { "$value": "{color.neutral.200}" }
    }
    }
    }
  2. Component Properties: Component-specific overrides of base properties

    {
    "card": {
    "stroke": {
    "width": { "$value": "{stroke.width.thin}" },
    "style": { "$value": "{stroke.style}" },
    "color": { "$value": "{stroke.color.primary}" }
    }
    }
    }
  3. Composition: Tokens that combine individual properties

    {
    "border": {
    "card": {
    "$value": {
    "width": "{card.stroke.width}",
    "style": "{card.stroke.style}",
    "color": "{card.stroke.color}"
    }
    }
    }
    }
  4. Usage: CSS variables with fallbacks

    --card-border: var(--border-card);
    .card {
    border: var(--card-border, none);
    }

This pattern provides flexibility while maintaining consistency:

  • Individual properties can be overridden in themes
  • Composed values can be overridden as a whole
  • Fallbacks are handled at the usage site

All components should aim to follow this pattern:

border-radius: var(--[component]-[element]-radius, var(--[semantic]-radius, var(--[primitive]-radius, [raw-value])));

This gives you the best of both worlds: semantic consistency with design flexibility when you need it. The component-specific token becomes your “get out of jail free” card for when the semantic radius doesn’t fit the design requirements.

Component Token Philosophy

Sugarcube components follow a semantic token system that balances consistency with flexibility. Here’s how it works:

Semantic Radius System

Components use three semantic radius categories:

  • --button-radius: Interactive button-like elements (buttons, dropdown triggers, select triggers)
  • --input-radius: Form input elements (inputs, textareas, checkboxes, radio groups)
  • --panel-radius: Content containers (cards, alerts, dialogs, popovers, dropdown content)

Token Generation Strategy

Sugarcube generates different .tokens.json files based on your setup:

For Starter Kit Users:

{
"dropdown": {
"content": {
"radius": { "$value": "{panel.radius}" },
"bg": { "$value": "{surface.primary}" },
"text": { "$value": "{text.primary}" }
}
}
}

For Non-Starter Kit Users:

{
"dropdown": {
"content": {
"radius": { "$value": "0.5rem" },
"bg": { "$value": "#ffffff" },
"text": { "$value": "#000000" }
}
}
}

Clean CSS Implementation (No Fallbacks Needed)

Component CSS files use simple, clean variable references without complex fallback chains:

.dropdown-menu-content {
border-radius: var(--dropdown-content-radius);
background-color: var(--dropdown-content-bg);
color: var(--dropdown-content-text);
padding: var(--dropdown-content-padding);
}

Why No Fallbacks?

  1. Tokens Are Always Provided: Sugarcube ensures that all component tokens are always available, either as semantic references or raw values
  2. Fail Fast: If tokens are missing, it indicates a real problem that should be fixed, not masked
  3. Cleaner CSS: No complex fallback chains that make CSS harder to read and maintain
  4. Better Debugging: Missing tokens cause clear errors rather than silent fallbacks to unexpected values

Customization and Overrides

Users can customize components in multiple ways:

  1. Edit component tokens: Modify the .tokens.json file to reference your own semantic tokens
  2. Override CSS variables: Set component-specific CSS variables in your stylesheets
  3. Use semantic tokens: Let components automatically inherit from your design system

Migration Path

  • Start without starter kit: Get raw value tokens that work immediately
  • Add your own tokens: Manually update component tokens to reference your semantic system
  • Adopt starter kit: Component tokens automatically work with the new semantic system

GUI Integration

When building tools or GUIs for token management, the system supports:

  • Raw values: Write direct values to any token
  • Semantic references: Reference other tokens in the system
  • Component overrides: Override any component-specific token
  • Semantic token updates: Update semantic tokens that affect multiple components

This approach provides semantic consistency by default while maintaining complete customization flexibility when needed.