# How to Create Component Tokens with Variants in DESIGN.md

> Learn to create component tokens with variants in DESIGN.md. Define base components and use state suffixes to customize UI elements effectively. Master variant creation now.

- Repository: [Google Labs Code/design.md](https://github.com/google-labs-code/design.md)
- Tags: how-to-guide
- Published: 2026-06-27

---

**Component tokens with variants in DESIGN.md are created by defining a base component in the YAML front matter under the `components` key, then adding sibling entries that share the base name plus a state suffix (e.g., `-hover` or `-active`) to override specific properties for different UI states.**

The [`google-labs-code/design.md`](https://github.com/google-labs-code/design.md/blob/main/google-labs-code/design.md) repository provides a specification for managing design tokens in Markdown files. When building reusable UI components, you need to handle multiple interaction states without duplicating entire token sets. The component variant system allows you to define base styles and layer state-specific overrides efficiently.

## Understanding the Component Token Structure

According to the official specification in [`docs/spec.md`](https://github.com/google-labs-code/design.md/blob/main/docs/spec.md) (lines 305-317), component tokens follow a specific naming convention that enables automatic merging by consumers.

### Base Component Definition

Every variant starts with a base component that defines the default styling. In the YAML front matter of your [`DESIGN.md`](https://github.com/google-labs-code/design.md/blob/main/DESIGN.md), create an entry under the `components` map:

```yaml
components:
  button-primary:
    backgroundColor: "{colors.primary}"
    textColor: "{colors.on-primary}"
    typography: "{typography.label-md}"
    rounded: "{rounded.lg}"
    padding: 12px
    height: 48px

```

This base entry establishes the complete token set for the component's default state.

### Variant Naming Convention

Variants are defined as separate entries that share the base component's prefix and append a state suffix. The specification recognizes states such as **active**, **hover**, and **pressed**. For example, if your base is `button-primary`, valid variants include:

- `button-primary-hover`
- `button-primary-active`
- `button-primary-disabled`

This naming pattern signals to consumers that these entries should be merged with the base component when the corresponding UI state is active.

## How to Define Component Tokens with Variants

Follow this pattern to implement variants in your [`DESIGN.md`](https://github.com/google-labs-code/design.md/blob/main/DESIGN.md) file:

1. **Open the YAML front matter** of your [`DESIGN.md`](https://github.com/google-labs-code/design.md/blob/main/DESIGN.md) (the section between `---` delimiters).
2. **Ensure the `components` map exists** at the root level of the front matter.
3. **Define your base component** with all default properties.
4. **Create variant entries** as siblings to the base, using the `{base-name}-{state}` format.
5. **Include only overriding properties** in variants—values that differ from the base.

A real-world example from [`examples/totality-festival/DESIGN.md`](https://github.com/google-labs-code/design.md/blob/main/examples/totality-festival/DESIGN.md) (lines 101-110) demonstrates this pattern:

```yaml
---

# …other token sections (colors, typography, etc.) …

components:
  # Base component – default styling for the primary button

  button-primary:
    backgroundColor: "{colors.primary}"
    textColor: "{colors.on-primary}"
    typography: "{typography.label-md}"
    rounded: "{rounded.lg}"
    padding: 12px
    height: 48px

  # Hover variant – only the properties that change are listed

  button-primary-hover:
    backgroundColor: "{colors.primary-fixed}"

  # Active (pressed) variant – another override

  button-primary-active:
    backgroundColor: "{colors.primary-80}"
    textColor: "{colors.on-primary-80}"

```

Another implementation example exists in [`examples/paws-and-paths/DESIGN.md`](https://github.com/google-labs-code/design.md/blob/main/examples/paws-and-paths/DESIGN.md), showing how the same pattern applies across different project types.

## Referencing and Inheriting Tokens

The variant system relies on **token references** and **selective overrides**. Use the `{path.to.token}` syntax to reference global tokens (like `colors.primary` or `typography.label-md`) and keep your definitions DRY.

When a variant omits a property, the consumer merges the base component with the variant, preserving base values for any properties not explicitly overridden. This means:

- **Full replacement**: A variant can redefine any token, like `backgroundColor` in the hover state.
- **Inheritance**: Unspecified properties automatically fall back to the base component values.

The consumer (CLI tool, AI agent, or build system) handles this merge at runtime or build time.

## Consuming Component Variants in Code

Applications consuming these tokens implement logic to merge base and variant tokens based on UI state. As shown in the repository's [`README.md`](https://github.com/google-labs-code/design.md/blob/main/README.md), tokens are accessed via dot-notation paths like `"components.button-primary"`.

A TypeScript implementation demonstrates the merge strategy:

```typescript
function getButtonTokens(state: 'default' | 'hover' | 'active') {
  const base = tokens.components['button-primary'];
  const variant = tokens.components[`button-primary-${state}`] ?? {};
  return { ...base, ...variant };
}

```

The spread operator performs a shallow merge, where variant properties override base properties when present. This yields a complete token set appropriate for the current component state.

## Summary

- Component tokens live in the YAML front matter of [`DESIGN.md`](https://github.com/google-labs-code/design.md/blob/main/DESIGN.md) under the `components` key.
- Variants follow the naming pattern `{base-name}-{state}` (e.g., `button-primary-hover`).
- Define only changed properties in variants; consumers merge them with the base component.
- Reference global tokens using `{path.to.token}` syntax to maintain consistency.
- The specification is documented in [`docs/spec.md`](https://github.com/google-labs-code/design.md/blob/main/docs/spec.md) (lines 305-317) with examples in [`examples/totality-festival/DESIGN.md`](https://github.com/google-labs-code/design.md/blob/main/examples/totality-festival/DESIGN.md) and [`examples/paws-and-paths/DESIGN.md`](https://github.com/google-labs-code/design.md/blob/main/examples/paws-and-paths/DESIGN.md).

## Frequently Asked Questions

### What is the difference between a base component and a variant token?

A base component defines the complete set of design tokens for a UI element's default state, including properties like `backgroundColor`, `textColor`, and `padding`. A variant token is a partial definition that shares the base name plus a state suffix (like `-hover` or `-active`) and contains only the properties that change for that specific state. Consumers merge the base with the variant to generate the final styles.

### How do I reference other tokens inside component definitions?

Use the curly brace syntax `{path.to.token}` to reference tokens defined elsewhere in the YAML front matter. For example, `backgroundColor: "{colors.primary}"` references the `primary` color token from the `colors` section. This keeps component definitions synchronized with your global design system and avoids hardcoded values.

### Can a component have multiple variants for different states?

Yes, a single base component can have any number of variant siblings. The specification supports states like `active`, `hover`, `pressed`, and `disabled`. Each variant is defined as a separate entry in the `components` map (e.g., `button-primary-hover`, `button-primary-active`, `button-primary-disabled`), allowing granular control over styling across all interaction states.

### Where does the merging of base and variant tokens happen?

The merge logic is implemented by the consumer—whether that's a CLI tool, AI agent, or build system processing the [`DESIGN.md`](https://github.com/google-labs-code/design.md/blob/main/DESIGN.md) file. The specification in [`docs/spec.md`](https://github.com/google-labs-code/design.md/blob/main/docs/spec.md) defines the expected behavior, but the actual JavaScript/TypeScript merge (typically using object spread like `{ ...base, ...variant }`) occurs in the consuming application or build pipeline.