Skip to content

Conversation

ilikescience
Copy link
Contributor

@ilikescience ilikescience commented Aug 19, 2025

Changes

Groups and Aliases Specification Updates

This PR introduces significant updates to the Groups and Aliases sections of the design tokens specification, addressing key community requests while maintaining backward compatibility and aligning with established JSON standards.

Summary of Changes

Groups Specification

  • Root tokens: Added support for $root tokens within groups to enable default values alongside variants
  • Group inheritance: Introduced $extends property for group inheritance with deep merge semantics
  • JSON Schema alignment: Defined $extends as syntactic sugar over JSON Schema $ref to leverage existing specifications
  • Clear parsing rules: Resolved ambiguity between tokens and groups with explicit validation requirements

Aliases Specification

  • Dual reference syntax: Maintained curly brace syntax for token references while adding required JSON Pointer support
  • Property-level references: Enabled fine-grained references to token properties using JSON Pointer syntax
  • Clear semantic distinction: Differentiated between token-to-token references ({token}) and document references ($ref)

Key Design Decisions and Tradeoffs

1. Root Tokens: $root vs Alternatives

Decision: Use $root as a reserved token name for default values within groups.

Considered alternatives:

  • Empty string ("") - rejected due to reference ambiguity
  • Token-group hybrid - rejected as it violates token/group distinction
  • $default - rejected as less semantically neutral

Tradeoff: Slightly more verbose syntax ({color.accent.$root}) in exchange for unambiguous references and conflict prevention.

{
  "color": {
    "accent": {
      "$root": { "$type": "color", "$value": "#dd0000" },
      "light": { "$type": "color", "$value": "#ff2222" },
      "dark": { "$type": "color", "$value": "#aa0000" }
    }
  }
}

2. Group Inheritance: Deep Merge Semantics

Decision: Define $extends with deep merge behavior where local properties override inherited ones.

Rationale: Addresses community need for component inheritance (issue #116) while avoiding specification complexity by deferring to JSON Schema $ref semantics.

Tradeoff: Added complexity in implementation vs. powerful inheritance capabilities for design systems.

{
  "input": {
    "field": {
      "width": { "$value": "100%" },
      "background": { "$value": "#ffffff" }
    }
  },
  "input-amount": {
    "$extends": "{input}",
    "field": {
      "width": { "$value": "100px" }  // Overrides inherited width
    }
  }
}
// Result: input-amount gets background from input, but custom width

Note

The curly-brace syntax in this instance refers to the object at that pointer, whereas in resolving aliases the curly braces refer to the object in the $value property. I see this being potentially confusing; I think we should resolve this before merging.

3. Reference Syntax: Complementary Approaches

Decision: Maintain curly brace syntax for token references, require JSON Pointer support for property references.

Rationale: Originally attempted to make syntaxes equivalent, but discovered fundamental incompatibility - curly braces implicitly target $value while JSON Pointer provides direct path access.

Tradeoff: Two reference systems to learn vs. appropriate tools for different use cases.

{
  "base": {
    "blue": {
      "$value": {
        "colorSpace": "srgb", 
        "components": [0.2, 0.4, 0.9]
      }
    }
  },
  "derived": {
    "whole-token": { "$value": "{base.blue}" },           // Token reference
    "just-hue": { "$ref": "#/base/blue/$value/components/0" }  // Property reference
  }
}

4. Standards Alignment: JSON Schema Integration

Decision: Define $extends as syntactic sugar over JSON Schema 2020-12 $ref with sibling properties.

Benefits:

  • Leverages battle-tested specifications rather than inventing new semantics
  • Enables use of existing JSON Schema tooling
  • Future-proof as JSON Schema evolves
  • Reduces specification maintenance burden

Tradeoff: Some complexity in explaining the relationship vs. simpler custom merge rules.

Community Impact

Addresses Key Issues

Use Cases Enabled

Component inheritance:

{
  "button": {
    "$type": "color",
    "background": { "$value": "#0066cc" },
    "text": { "$value": "#ffffff" }
  },
  "button-primary": {
    "$extends": "{button}",
    "background": { "$value": "#cc0066" }  // Override just background
  }
}

Semantic color relationships:

{
  "brand": {
    "blue": { "$value": { "components": [0.2, 0.4, 0.9] } }
  },
  "semantic": {
    "primary": {
      "$value": {
        "components": [
          { "$ref": "#/brand/blue/$value/components/0" },  // Same hue
          { "$ref": "#/brand/blue/$value/components/1" },  // Same saturation  
          0.7  // Different lightness
        ]
      }
    }
  }
}

Migration Path

  • Backward compatible: Existing token files continue to work unchanged
  • Incremental adoption: New features are opt-in
  • Tool flexibility: Multiple implementation approaches supported

Implementation Requirements

Tools implementing this specification must:

  1. Support both curly brace and JSON Pointer reference syntax
  2. Implement deep merge semantics for $extends
  3. Validate token vs group distinctions
  4. Detect circular references in both inheritance and aliases
  5. Follow JSON Schema $ref patterns for extension resolution

This update significantly enhances the design tokens specification's expressiveness while maintaining the simplicity and clarity that makes it accessible to design system teams.

How to Review

How can a reviewer review your changes? What should be kept in mind for this review?

ilikescience and others added 3 commits August 17, 2025 13:33
This is a comprehensive update to the groups specification that significantly
expands the capabilities of design token groups while maintaining backward
compatibility. The changes include:

Key Features Added:
- Root tokens using reserved `$root` name for base values within groups
- Group inheritance via `$extends` property with deep merge semantics
- Enhanced group properties including `$deprecated` and expanded `$description`
- Clear distinction between tokens (has `$value`) and groups (no `$value`)
- Empty groups explicitly permitted with use case justification
- Comprehensive error handling and validation requirements

Technical Improvements:
- Aligned `$extends` with JSON Schema `$ref` semantics for consistency
- Detailed processing rules for token resolution order
- Type inheritance hierarchy with clear precedence rules
- Circular reference detection requirements
- Path construction guidelines including `$root` handling

Documentation Enhancements:
- Extensive examples demonstrating all new features
- Migration and compatibility guidance
- Implementation guidance for tool developers
- Clear rationale for design decisions
- Use cases covering simple to complex scenarios

Breaking Changes: None - fully backward compatible
Migration Path: Existing files work without modification

This update positions the specification to support advanced design system
workflows while maintaining the simplicity that makes design tokens accessible
to all teams.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
…tax and enhanced groups specification

- Complete rewrite of aliases.md with dual syntax support (curly brace vs JSON Pointer)
- Add property-level references for color components, dimensions, and typography
- Expand groups.md with root tokens, type inheritance, and $extends functionality
- Include detailed examples for chained references and circular reference detection
- Add implementation guidance for tool authors and advanced use cases

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit resolves all but one broken local anchor references in the technical
reports that were causing ReSpec linter warnings during the build process.

**Cross-file Reference Fixes:**
- broken references in `composite-types.md` that were incorrectly
  pointing to same-file anchors when they should reference other files
- Updated references from `[color](#color)` to `[color](types#color)`
- Updated references from `[dimension](#dimension)` to `[dimension](types#dimension)`
- Fixed `$description` and `$extensions` references to point to `design-token` file
- Updated groups references from `[Groups](#groups)` to `[Groups](groups)`

**Inter-file Reference Corrections:**
- `groups.md`: Fixed `$value`, `$type`, `$extensions` references to `design-token`
- `design-token.md`: Fixed `#type-0` to `#type`, updated groups and aliases references
- `types.md`: Fixed gradient reference to point to `composite-types#gradient`

**Content Cleanup:**
- Removed redundant nested `$type` and `$value` structures in JSON examples
- Cleaned up color object examples to use proper flat structure
- Removed trailing content section in `groups.md`

**Build Configuration:**
- Added `validate:color` script to package.json for comprehensive validation
- Updated main `validate` script to include color validation

The issue was identified through systematic analysis of ReSpec linter output
which reported "Broken local reference found in document" but didn't specify
the exact broken link. All local anchor references were audited against
actual heading structures to ensure proper cross-file linking.

All references now use the correct relative path format (e.g., `types#anchor`)
instead of incorrect same-file format (e.g., `#anchor`) when targeting
content in other markdown files.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
Copy link

netlify bot commented Aug 19, 2025

Deploy Preview for designtokensorg ready!

Name Link
🔨 Latest commit 933b574
🔍 Latest deploy log https://app.netlify.com/projects/designtokensorg/deploys/68c06521240793000868930c
😎 Deploy Preview https://deploy-preview-298--designtokensorg.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

Copy link
Contributor

@drwpow drwpow left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Love this! Thanks for putting this together


#### `$extends`

Groups MAY include an optional `$extends` property to inherit tokens and properties from another group. The `$extends` property is syntactic sugar for JSON Schema's `$ref` keyword and follows the same semantic behavior as defined in [[json-schema-2020-12]].
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: A related concept in OpenAPI schema is allOf, where it can provide an array of items to compose, e.g.:

{
  "Cat": {
    "description": "A representation of a cat. Note that `Cat` will be used as the discriminating value.",
    "allOf": [
      { "$ref": "#/components/schemas/Mammal" },
      { "$ref": "#/components/schemas/Pet" },
      
    ]

This is not JSON Schema; this is OpenAPI which is an extension.

I’m just anticipating someone will ask for this. Because at least in OpenAPI land, this is used a surprising number of times. Do we want to support $extends as an array? Which would move it away from JSON Schema and closer to this OpenAPI concept. Just curious if this enables something, or if this just overcomplicates it (and if the latter, it would be nice to have some example that this breaks etc).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm struggling to think of a case where you'd want to extend multiple groups; I'm certainly open to using an array here but would rather wait for someone to ask before introducing it.

@Sidnioulz
Copy link

Super happy to see $root and $extends, those are both concepts I've needed in the past. I've definitely baked my own support for $root in past SD pipelines.

Decision: Maintain curly brace syntax for token references, require JSON Pointer support for property references.

Rationale: Originally attempted to make syntaxes equivalent, but discovered fundamental incompatibility - curly braces implicitly target $value while JSON Pointer provides direct path access.

@ilikescience I think it's a problem that this shorthand exists. I've wanted to resolve aliases to whole tokens in the past (because they were identical), and not being allowed to do prevented me from reducing my maintenance surface; aliasing individual properties still meant that adding properties in the source required adding aliases in the targets.

Very happy to have $extends as an escape hatch now, but I still don't get why $value should get preferential treatment in aliasing, forcing other use cases to use an alternate syntax. I'd personally prefer a breaking change that brings both syntaxes on a feature parity level. Though I understand this is in the realm of my personal preferences and I expect the vast majority would prefer for the $value shorthand to be preserved :)

I'd curious if curly brackets shorthands could instead resolve to the same property as the one making use of an alias (e.g. "$value": "{my.token}" points to my.token.$value but "$type": "{my.token}" points to its type) ? That would maintain retrocompatibility for the common use case (value -> value aliasing) whilst allowing more coherence and flexibility.

@ilikescience
Copy link
Contributor Author

I'd personally prefer a breaking change that brings both syntaxes on a feature parity level.

@Sidnioulz yeah, I think there's probably a lot of arguments on both sides of this. The proposed $ref + JSON pointer syntax gives a common syntax that can replace the syntactic sugar of $extends and curly-brace aliases; I see a future where we might be able to deprecate the syntactic sugar and restrict token authors to $ref + JSON pointers only.

Having both in the short/medium term increases tool complexity a bit, but I think it's a necessary evil to atone for the sin of introducing curly-brace aliases in the earliest versions of the draft without the deep thinking that we're doing now.

@Sidnioulz
Copy link

I'd personally prefer a breaking change that brings both syntaxes on a feature parity level.

@Sidnioulz yeah, I think there's probably a lot of arguments on both sides of this. The proposed $ref + JSON pointer syntax gives a common syntax that can replace the syntactic sugar of $extends and curly-brace aliases; I see a future where we might be able to deprecate the syntactic sugar and restrict token authors to $ref + JSON pointers only.

Having both in the short/medium term increases tool complexity a bit, but I think it's a necessary evil to atone for the sin of introducing curly-brace aliases in the earliest versions of the draft without the deep thinking that we're doing now.

Good point! It'll be easier to deprecate either of curly brackets or specifically the value shorthand if most folks have been exposed to a new syntax and if major tools encourage the new syntax by default. It would be more painful to community members to make the breaking change now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants