Valid Component Token Properties in DESIGN.md: A Complete Guide to Variants and Hover States
DESIGN.md supports eight valid component token properties—backgroundColor, textColor, typography, rounded, padding, size, height, and width—that accept either literal values or token references, while variants like hover states are defined using hyphenated suffixes (e.g., button-primary-hover) that override specific base properties while inheriting the rest.
The google-labs-code/design.md repository defines a strict schema for design tokens within its front-matter YAML. Understanding the valid component token properties in DESIGN.md and how variant states like hover or active are expressed is essential for building consistent, maintainable design systems that compile correctly through the CLI toolchain.
Valid Component Token Properties
According to docs/spec.md, the specification enforces a closed set of eight component-property tokens. Each property expects a specific type and may be expressed as a literal value or a reference to another token using curly brace syntax (e.g., {colors.primary}).
The Eight Core Properties
The following table maps each valid property to its expected type and specification location:
| Property | Expected Type | Spec Reference |
|---|---|---|
backgroundColor |
<Color> |
docs/spec.md |
textColor |
<Color> |
docs/spec.md |
typography |
<Typography> |
docs/spec.md |
rounded |
<Dimension> |
docs/spec.md |
padding |
<Dimension> |
docs/spec.md |
size |
<Dimension> |
docs/spec.md |
height |
<Dimension> |
docs/spec.md |
width |
<Dimension> |
docs/spec.md |
Token references allow values to reference global tokens defined elsewhere in the file, enabling single-source-of-truth updates across components.
How Component Variants and Hover States Work
Variants in DESIGN.md follow a declarative inheritance model. Rather than defining state machines, you create separate component entries that override specific properties of a base component.
Variant Naming Conventions
As documented in docs/spec.md, variant keys append a state suffix to the base component name using hyphenation:
button-primary(base)button-primary-hover(hover state)button-primary-active(active/pressed state)button-primary-pressed(alternative pressed state)
Property Inheritance and Override Behavior
When the CLI exporter or consuming application resolves a component, it loads the base definition first, then overlays any matching variant properties. Unspecified properties in the variant automatically inherit from the base.
Consider this implementation from examples/totality-festival/DESIGN.md:
components:
button-primary:
backgroundColor: "{colors.primary}"
textColor: "{colors.on-primary}"
typography: "{typography.label-md}"
rounded: "{rounded.lg}"
padding: 12px
height: 48px
button-primary-hover:
backgroundColor: "{colors.primary-fixed}"
In this example, button-primary-hover only overrides backgroundColor. The consumer merges the maps, resulting in a component that retains the base typography, padding, and dimensions while swapping the background color to {colors.primary-fixed}.
Practical Implementation Examples
Defining Multi-State Components
Define comprehensive state coverage by chaining variant suffixes:
components:
button-primary:
backgroundColor: "{colors.primary}"
textColor: "{colors.on-primary}"
typography: "{typography.label-md}"
rounded: "{rounded.lg}"
padding: 12px
height: 48px
button-primary-hover:
backgroundColor: "{colors.primary-fixed}"
button-primary-active:
backgroundColor: "{colors.primary-80}"
textColor: "{colors.on-primary-80}"
Referencing Component Tokens
You can reference component properties within other components using dot notation:
components:
card-glass-level-2:
backgroundColor: "rgba(52, 52, 58, 0.2)"
rounded: "{rounded.xl}"
padding: "{spacing.gutter}"
typography: "{components.button-primary.typography}"
Consuming Components via CLI
The parser in packages/cli/src/linter/spec-config.ts validates these structures during build. Access resolved components programmatically:
import { parseDesign } from '@designmd/cli';
const design = await parseDesign('examples/totality-festival/DESIGN.md');
const btn = design.components['button-primary'];
const btnHover = design.components['button-primary-hover'];
// Merge base + hover (simplified resolution)
const hoverStyle = { ...btn, ...btnHover };
console.log(hoverStyle.backgroundColor); // → value of colors.primary-fixed
Summary
- Eight valid properties define component tokens:
backgroundColor,textColor,typography,rounded,padding,size,height, andwidth. - Values may be literal (e.g.,
12px,#ff0000) or token references (e.g.,{colors.primary}). - Variants use hyphenated suffixes (
-hover,-active,-pressed) appended to base component names. - Inheritance works through map merging: variants override specific properties while inheriting unspecified ones from the base component.
- Validation occurs in
packages/cli/src/linter/spec-config.tsagainst the schema defined indocs/spec.md.
Frequently Asked Questions
What happens if a variant references a property not defined in the base component?
The variant simply adds that property to the resolved component. According to the docs/spec.md specification, variant maps may contain any subset of the eight valid component properties. If a property exists only in the variant, it becomes part of the final resolved style without requiring a base definition.
Can I nest variant suffixes for complex states like hover-active?
The specification in docs/spec.md implies linear suffixes rather than nested combinations. For complex multi-state scenarios, define explicit combined keys such as button-primary-hover-active or rely on your consumer application to layer states programmatically. The DESIGN.md schema itself does not enforce hierarchical state inheritance beyond single-suffix variants.
How does the CLI validate that my component properties are correct?
The linter in packages/cli/src/linter/spec-config.ts reads the canonical schema from docs/spec.md and validates component tokens during the build process. Tests in packages/cli/src/linter/spec-config.test.ts ensure that only the eight recognized properties are accepted and that token references resolve correctly. Invalid properties trigger linting errors before export.
Are variant properties required to use the same value types as the base component?
Yes, type consistency is enforced. If backgroundColor expects a <Color> type in the base component, the variant's override must also resolve to a valid color value or reference. The schema validation ensures that overrides maintain type compatibility with the base property definitions.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →