Design Token Aliases vs Canonical Token Names: Understanding the Difference in DESIGN.md
Canonical token names are the authoritative identifiers used by tooling, while aliases are human-friendly alternatives that map to those canonical names for improved author ergonomics.
The google-labs-code/design.md specification uses a dual-naming system to balance machine readability with human flexibility. Understanding the distinction between design token aliases and canonical token names is essential for writing valid DESIGN.md files that pass linting while remaining intuitive for authors.
What Are Canonical Token Names?
Canonical token names are the primary, authoritative identifiers for sections or token groups within the DESIGN.md specification. These are the official identifiers used by the spec and by tooling when generating output. According to the source configuration in packages/cli/src/linter/spec-config.yaml (lines 31-35), canonical names are defined under the canonical key and provide the single source of truth for validation and rendering.
When the linter checks section order or validates document structure, it always compares against these canonical names. The CANONICAL_ORDER constant in spec-config.ts represents the definitive sequence that all DESIGN.md files must follow.
What Are Design Token Aliases?
Design token aliases are alternative headings that the parser treats as equivalent to a canonical name. Aliases improve author ergonomics by allowing writers to use human-friendly or legacy phrasing without breaking validation. For example, an author can write ## Brand & Style instead of ## Overview, and the linter will treat both as the same section.
The alias mapping is defined in packages/cli/src/linter/spec-config.yaml (lines 33-35) under the aliases array within each section definition. The SECTION_ALIASES map in spec-config.ts (lines 52-57) stores these relationships, ensuring that each alias points to exactly one canonical name.
How the Resolution System Works
The resolution system normalizes any heading—whether canonical or alias—to its canonical form before validation occurs.
Configuration in spec-config.yaml
The YAML configuration establishes the relationship between canonical names and their aliases:
# packages/cli/src/linter/spec-config.yaml
sections:
- canonical: Overview
aliases:
- Brand & Style # ← alias
- canonical: Colors
- canonical: Layout
aliases:
- Layout & Spacing
Source: packages/cli/src/linter/spec-config.yaml lines 31-40.
The resolveAlias Function
The resolveAlias function in packages/cli/src/linter/spec-config.ts (lines 59-62) handles the normalization:
import { resolveAlias } from '@google/design.md/linter/spec-config';
// Author wrote "Brand & Style" – we get the canonical name:
const heading = 'Brand & Style';
const canonical = resolveAlias(heading); // "Overview"
Source: packages/cli/src/linter/spec-config.ts lines 59-62.
This function checks the SECTION_ALIASES map and returns the canonical equivalent. If the input is already a canonical name, it returns the input unchanged.
Practical Usage in Linting
The normalization process is critical for the linter's section-order validation. In packages/cli/src/linter/linter/rules/section-order.ts (line 34), the rule calls resolveAlias on every section heading before checking against CANONICAL_ORDER:
// packages/cli/src/linter/linter/rules/section-order.ts
import { resolveAlias, CANONICAL_ORDER } from '../../spec-config';
const normalized = sections.map(s => resolveAlias(s));
// Now `normalized` contains only canonical names for order checking.
When a DESIGN.md file contains the alias ## Brand & Style, the linter calls resolveAlias('Brand & Style'), which returns 'Overview'. The validation then treats the section as Overview, ensuring the canonical ordering rule validates correctly regardless of which alias the author chose.
Code Examples
Resolving an Alias to Canonical Form
import { resolveAlias } from '@google/design.md/linter/spec-config';
// Maps alias to canonical
const result = resolveAlias('Layout & Spacing'); // Returns "Layout"
Source: packages/cli/src/linter/spec-config.ts lines 59-62.
Configuring Aliases in YAML
# packages/cli/src/linter/spec-config.yaml
sections:
- canonical: Overview
aliases:
- Brand & Style
- Introduction
Source: packages/cli/src/linter/spec-config.yaml lines 31-35.
Testing Alias Resolution
// packages/cli/src/linter/spec-config.test.ts
it('resolveAlias returns canonical for known alias', () => {
const alias = 'Layout & Spacing';
expect(resolveAlias(alias)).toBe('Layout');
});
Source: packages/cli/src/linter/spec-config.test.ts lines 182-187.
Summary
- Canonical names are the authoritative identifiers used by tooling and defined in
spec-config.yamlunder thecanonicalkey. - Aliases are human-friendly alternatives mapped to canonical names via the
aliasesarray in the YAML configuration. - The
resolveAliasfunction inspec-config.tsnormalizes any heading to its canonical form before validation. - The linter uses this normalization to validate section order in
section-order.tswithout requiring authors to memorize exact canonical names.
Frequently Asked Questions
Can I use aliases interchangeably with canonical names?
Yes. The resolveAlias function treats both aliases and canonical names identically, returning the canonical form in either case. According to the implementation in spec-config.ts (lines 59-62), if you pass a canonical name to resolveAlias, it returns the name unchanged; if you pass an alias, it returns the mapped canonical name.
Where are canonical names and aliases defined?
They are defined in packages/cli/src/linter/spec-config.yaml (lines 31-40). Each section entry contains a canonical field and an optional aliases array. The TypeScript loader in spec-config.ts (lines 52-57) parses this YAML to create the SECTION_ALIASES mapping used at runtime.
How does the linter handle unknown aliases?
The resolveAlias function returns the input string unchanged if it does not match a known alias or canonical name. In the section-order rule (section-order.ts, line 34), this results in the unknown heading failing validation against the CANONICAL_ORDER array, triggering a lint error.
What happens if an alias conflicts with a canonical name?
The test suite in spec-config.test.ts (lines 182-187) ensures that aliases do not collide with canonical names during resolution. The configuration loader creates a bidirectional mapping where aliases always resolve to their declared canonical names, and canonical names resolve to themselves, preventing ambiguity in the lookup logic.
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 →