Re: [community-group] Proposal: JSON Schema $ref for aliases (breaking change) (#259)

Started playing around with an implementation in Terrazzo, and I realized there’s an error with the proposal that needs to be corrected. And it may affect some peoples’ opinions about the approach:

#### 1. `$value` shortcuts

The way this is described, both would essentially be the same:

```jsonc
{
  "color-blue-500": { "$type": "color", "$value": "#218bff" },
  "color-action-bg": { "$ref": "#/color-blue-500" }
}
```

```jsonc
{
  "color-blue-500": { "$type": "color", "$value": "#218bff" },
  "color-action-bg": { "$type": "color", "$value": { "$ref": "#/color-blue-500/$value" } }
}
```

The difference in the latter is the `$value` MUST be required, otherwise the reference would be invalid (the value for a color token couldn’t be a token itself). But that’s a little bit of extra boilerplate, isn’t it? We could take 2 approaches

##### Solution 1: Adjust the spec (simple, correct, but verbose)

We could simply require the full value: `"$value": { "$ref": "#/color-blue-500/$value" }`. This is correct. And it’s requiring precision because it’s necessary.

##### Solution 2: Allow reserved words (more complex, more ambiguous, but less verbose)

Or we could just say “when the key its own is a reserved key (`$value`, `$type`, `$description`, etc.), and the reference resolves to have that **same reserved word**, pretend the alias points there, e.g.:

- ✅ `"$value": { "$ref": "#/color-blue-500" }`: `color-blue-500` also has `$value` so it can be shortened
- ✅ `"$value": { "$ref": "#/color-blue-500/$value" }`: still allowed because it’s a full pointer
- ❌ `"$description": { "$ref": "#/color-blue-500" }`: **error** because `color-blue-500` doesn’t have a `$description` key defined

The obvious downside is **this increases ambiguity** and introduces the concept of conditional lookups, which is usually a bad practice. This may also not be how toolmakers implement JSON pointers naïvely, so it could likely lead to errors. But again, it is easier-to-type.

#### 2. Alias boundaries

Not an “error” per se, but an omission that needs to be clarified, is **what, then, counts as an alias**? For example, if we were to say “`color-action-bg` aliases `color-blue-500`,” which of the following code would still keep that statement true?

1. `"color-action-bg": { "$ref": "#/color-blue-500" }` (whole-token)
2. `"color-action-bg": { "$value": { "$ref": "#/color-blue-500/$value" } }` ($value only)
3. `"color-action-bg": { "$description": { "$ref": "#/color-blue-500/$description" } }` ($description, or any other non-$value property)

The difference here is **aliases can’t be “flattened” until the very last step** under the current language, and the reference must be preserved. But if users can compose any part of their schema, what constitutes an “alias” now? 

##### Solutions

How should we amend the language?

1. A token is an alias of another token if its `$value` points to another token’s, e.g.:

    - ✅ `"color-action-bg": { "$ref": "#/color-blue-500" }`: this is an alias because $value is aliased by proxy of the entire object pointing to another token
    - ✅ `"color-action-bg": { "$value": { "$ref": "#/color-blue-500/$value" } }`: this is an alias because $value directly points to another token’s
    - ❌ `"color-action-bg": { "$description": { "$ref": "#/color-blue-500/$description" } }`: this is NOT an alias because $value does not point to another token

2. A token is **only** an alias of another token if the entire token points to another token AND $value is not overridden, e.g.:

    - ✅ `"color-action-bg": { "$ref": "#/color-blue-500", "$description": "Action BG color" }`: this is an alias because the entire token points to another token AND $value is not overridden (even if some other properties are)
    - ❌ `"color-action-bg": { "$ref": "#/color-blue-500", "$value": "#218bff" }`: this is NOT an alias because even though the token originally pointed to another token, $value was overridden making this a unique token.
    - ❌ `"color-action-bg": { "$description": { "$ref": "#/color-blue-500/$description" } }`: this is NOT an alias because $value does not point to another token

3. A token is **only** an alias of another token if $value is aliased directly, e.g.:

    - ❌ `"color-action-bg": { "$ref": "#/color-blue-500" }`: this is NOT an alias because it is happening one level up from $value
    - ✅ `"color-action-bg": { "$value": { "$ref": "#/color-blue-500/$value" } }`: this is an alias because $value directly points to another token’s
    - ❌ `"color-action-bg": { "$description": { "$ref": "#/color-blue-500/$description" } }`: this is NOT an alias because $value does not point to another token

Out of all the approaches, #3 would be beneficial being the **easiest to statically-analyze.** You could tell which tokens are aliases without having to resolving any pointers. But #3 would be the most restrictive, too, **preventing people from aliasing entire groups** like other methods could.

As the spec proposer, just for the sake of argument, I would probably stick with #1 where even though it’s hard to statically-analyze the number of final tokens without doing work resolving everything, that seems like a worthwhile tradeoff to give schema authors more raw power to generate tokens and create aliases more freely without restrictions. In other words, I don’t really know if it’s advantageous to make token counts easier up front, especially considering the aliases have to be resolved one way or another, and all approaches are the same amount of work in the end.

---

I’ll update the PR description with **Solution 1** and **Solution 1** respectively as my rough proposals, but I could be easily swayed 🙂. Would really just love thoughts in general and poking holes in this more.

-- 
GitHub Notification of comment by drwpow
Please view or discuss this issue at https://github.com/design-tokens/community-group/pull/259#issuecomment-2631976574 using your GitHub account


-- 
Sent via github-notify-ml as configured in https://github.com/w3c/github-notify-ml-config

Received on Monday, 3 February 2025 20:14:44 UTC