Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to do tokens that share a name with a group #97

Open
c1rrus opened this issue Jan 12, 2022 · 16 comments
Open

How to do tokens that share a name with a group #97

c1rrus opened this issue Jan 12, 2022 · 16 comments

Comments

@c1rrus
Copy link
Member

c1rrus commented Jan 12, 2022

A question that's come up various discussions and for which the current spec draft doesn't really have an answer is this: How can you have design tokens that share the same name as a group?

For example, imagine you wanted to author a token file that could be exported to SASS like this:

$color-accent: #dd0000;
$color-accent-light: #ff2222;
$color-accent-dark: #aa0000;

...and you wanted to use groups to organise your token file. You could create a color group, with an accent group inside it and put your light and dark tokens in there. But then, where does the token that gets exported as $color-accent go?

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

      // Does the token go here, inside the "accent" group? If so, what is it called?
    }

    // Or does the "accent" token go here, but then how do we prevent the
    // name clash with the "accent" group?
  }
}

This issue came up at the last format editors' meeting and we feel like this is something our format should support in some way. Also, debating this issue may generate some ideas or highlight considerations that could be useful for the "reserved words" discussion over in issue #61.

We'd love to hear your thoughts and ideas on this!

@c1rrus
Copy link
Member Author

c1rrus commented Jan 12, 2022

To get the ball rolling, I'll outline a workaround and some potential approaches:

Work around
Authors can side-step the issue by not using a group. The example in the issue description could just put all 3 tokens into the "color" group and not have an "accent" group at all:

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

While this works, it's not ideal. Part of the benefit of groups is that avoids having to repeat common prefixes in names, such as "accent" in this example. By using this work-around authors miss out on that benefit.

Also, groups can have their own descriptions (and potentially other properties future spec versions may add), so by using this work-around authors lose the ability to add such descriptive information to all the "accent*" tokens as there is no "accent" group anymore.


Suggestion 1: Empty name token

It turns out that an empty string "" is a valid name in JSON. Authors could therefore do the following:

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

Since this apprach retains the "accent" group, authors can add things like a description to that group and that would be indendent of any description they give the "" token itself. For example:

{
  "color": {
    "accent": {
      "description": "Our brand's accent color and its tints & shades",
      "": {
        "type": "color",
        "value": "#dd0000",
        "description": "Our brand's accent color"
      },
      "light": {
        "type": "color",
        "value": "#ff2222",
        "description": "Tinted version of our brand's accent color"
      },
      // ...
    }
  }
}

There is actually nothing in the current spec draft that forbids this. However, if you wanted to alias the "color" / "accent" / "" token, the reference would need to be {color.accent.} (note the trailing period .). Without the period at the end, the reference would be pointing to the group, but we don't allow that (reference must always point to tokens).

Such reference might look a bit odd and are perhaps unintuitive. For convenience we may therefore want to modify our spec to have a rule stating that a reference that points to a group should be interpreted a pointing to a token with an empty name ("") within that group. So, if such a token exists the reference is valid and otherwise it is not. That way {color.accent} would be a valid reference.


Suggestion 2: Token and group hybrid

In the current spec draft, groups and tokens are two very distinct concepts. Only tokens have values and only groups can contain tokens and other groups.

What if we allowed hybrids that are both token and group at the same time. They'd have a value (like a token), but could also contain other tokens or groups.

Our example might then become something like this:

{
  "color": {
    "accent": {
      "type": "color",
      "value": "#dd0000",

      "light": {
        "type": "color",
        "value": "#ff2222"
      },
      "dark": {
        "type": "color",
        "value": "#aa0000"
      }
    }
  }
}

While this may seem quite elegant, it does introduce some challenges:

  • How do we allow separate descriptions (or other "special" properties) for the group and token that make up the hybrid?
  • Currently the type property on a group acts as a default type for any tokens within that group - the group itself doesn't actually have a type. Therefore, how do we allow authors to specify a separate group (default) type and token type for the hybrid?
  • This potentially increases the number of reserved words that cannot be used as group or token names. Every property that is allowed for tokens (value, type, description, extensions and whatever else we may add in future) must necessarily also be a reserved word within groups, since every group has the potential to be a hybrid.

You could resolve the first 2 challenges by have group-specific property names that don't clash with their token equivalents (e.g. group-description, group-type etc.), but that then means there even more reserved words.

For example, you might end up with something like this:

{
  "color": {
    // "accent" is a group & token hybrid
    "accent": {
      // "accent" group special props
      "group-description": "Our brand's accent color and its tints & shades",

      // "accent" token special props
      "type": "color",
      "value": "#dd0000",
      "description": "Our brand's accent color"

      // "light", "dark" etc. are items in the "accent" group as per usual
      "light": {
        "type": "color",
        "value": "#ff2222",
        "description": "Tinted version of our brand's accent color"
      },
      // ...
    }
  }
}

I'm sure there's other pros/cons and approaches too. So, let's hear 'em! :-)

@drwpow
Copy link
Contributor

drwpow commented Jan 12, 2022

Suggestion 2: Token and group hybrid

This is so interesting to see this suggestion because that exact idea has been something I’ve been mulling about for a while. I posted a comment in the theme discussion with a similar syntax, but I tried to format it according to previous comments.

Here’s one idea for your “hybrid” suggestion but with a syntax I’d prefer:

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

Note: mode inherits type from parent; it must be the same type and each value in the object is essentially another value

By namespacing it under mode (idea), it hopefully resolves the reserved word issue. And the type inheritance hopefully alleviates some complexity. But all just suggestions! I’m sure I’m missing something and there are likely tradeoffs to this idea.

@ilikescience
Copy link

ilikescience commented Jan 13, 2022

If groups and tokens can share a name, my inclination is that the token/group hybrid is the right way to go.

However, I think we ought to hash out if it makes sense to allow groups and tokens to share a name. I feel like we end up making naming tokens a little more flexible at the cost of a lot of complexity in parsing/reading.

In our examples above, what is the value of color-accent? In one sense, it's a group of two tokens named light and dark. In another sense, it's a token with the value of #dd0000. In both reasoning about the system and using it, we'd run into a lot of challenges working with two distinct things called color-accent.

I'd suggest that we keep things simple and lean on the requirement of unique keys within JSON to provide some guard rails.

Say you define the group like this

{
  "color": {
    "type": "group",
    "value": {
      "accent": {
        "type": "group",
        "value": {
          "light": {
            "type": "color",
            "value": "#ff2222"
          }, {
          "dark": {
            "type": "color",
            "value": "#aa0000"
          }
        }
      }
    }
  }
}

The color group can only have one value for the key accent. Since you've already defined it as a group, you can't re-use it for a color.

You do lose some flexibility in naming tokens, but gain a lot of precision.

@TravisSpomer
Copy link

If all one cares about is the exported token's name, you could still achieve that pretty easily in the export process! Tokens are already necessarily getting renamed when exporting to code, to turn . into -, to change case, or so on. So you could just have a rule in your export that anything named "_" or base or default or whatever gets dropped when exporting to SCSS.

color.accent.default = #dd0000$color-accent: #dd0000;
color.accent.light = #ff2222$color-accent-light: #ff2222;
color.accent.dark = #aa0000$color-accent-dark: #aa0000;

One scenario that's somewhat important to me for the future is group aliasing. For example:

{
  "neutralcontrol": {
    "foreground": {
      "type": "color",
      "rest": { "value": "{...}" },
      "hover": { "value": "{...}" },
      "pressed": { "value": "{...}" },
      "disabled": { "value": "{...}" }
    },
    "background": {
      "type": "color",
      "rest": { "value": "{...}" },
      "hover": { "value": "{...}" },
      "pressed": { "value": "{...}" },
      "disabled": { "value": "{...}" }
    }
  },
  "button":
  {
    "text": {
      "color": { "value": "{neutralcontrol.foreground}" }
    },
    "base": {
      "color": { "value": "{neutralcontrol.background}" }
    }
  }
}

(In that example, button has 8 tokens that just reference existing sets of colors that were designed to go together, so no accidentally mixing and matching incompatible sets. button.text.color.rest = neutralcontrol.foreground.rest, button.base.color.disabled = neutralcolor.background.disabled, and so on.)

It's not possible with today's spec, but I want to try to make sure it's possible someday. If neutralcolor.foreground could have a value and be a group, then {neutralcolor.foreground} becomes ambiguous. It's not a total blocker, but that's one reason I'm in favor of tokens and groups not being allowed to share a name.

@c1rrus
Copy link
Member Author

c1rrus commented Jan 13, 2022

@drwpow: I think the mode thing you're proposing could be a good be a solution to theming, dark/light modes, etc. - essentially anything where a single token may have alternative values in certain contexts. However, what I was trying to describe in this issue is (at least in my mind) a different problem. I'm trying to define 3 distinct design tokens, but one of them happens to have a name that clashes with a group that contains the other 2.

Perhaps I chose the bad names for my example - the "light" and "dark" colors weren't intended as alternative values for light and dark mode, but rather just 2 extra colors. In hindsight, I probably should have used names like "tint" and "shade".

I'm less keen on the mode approach as a way of doing a group/token hybrid as it's not clear to me how you'd put nested groups into it (after all, groups can contain groups as well as tokens) and/or tokens with their own, different types. I'd prefer something that keeps hybrids as close as possible to group and token's current syntax.

But, we should totally look into it for theming, dark/light modes, etc. - feel free to open an issue for that if you like, btw. It's a topic we'll need to address sooner or later.

@c1rrus
Copy link
Member Author

c1rrus commented Jan 13, 2022

@ilikescience So, if I've understood your example you're proposing that we alter the syntax of groups to be more token-like? I.e. rather than just a JSON object where any properties are treated as the names of the nested tokens/groups (with the exception of a few reserved ones like description), the need to have a type property set to "group" and a value, which is then an object containing the items in that group. Is that right?

If so, I'm not sure what that provides over the current draft spec. I'd argue we already have a precise way of differentiating between groups and tokens - any object with a value key must be a token (and all its other properties are then interpreted accordingly) and otherwise it must be a group (and gets interpreted accordingly).

It doesn't appear to solve the issue - i.e. there's no way of have "color.accent" somehow be a color that is distinct from "color.accent.light / dark".

Please do correct me, if I've misunderstood though!

@c1rrus
Copy link
Member Author

c1rrus commented Jan 13, 2022

@TravisSpomer In light of the recent comments on the reserved words issue (#61), I wonder if combination of the prefix idea there and what you've suggested could work:

Let's imagine for a moment we use a special prefix like $ or _ for all properties in our spec that have a special meaning (value, type, description, etc.) across both groups and tokens. Then we could have a "special" property on groups, for example $default, that can be used when you want a token in that group which shares the same name in some contexts (e.g. when being exported as code).

So, it could look like this (if we assume _ as our universal reserved word prefix):

{
  "color": {
    "accent": {
      "_default": {
        "_type": "color",
        "_value": "#dd0000"
      },
      "light": {
        "_type": "color",
        "_value": "#ff2222"
      },
      "dark": {
        "_type": "color",
        "_value": "#aa0000"
      }
    }
  }
}

I've been thinking lately about the tree data model that our token files describe (similar to how HTML files define a DOM) and, in the context of this issue, was wondering if that default color token is logically a child of the accent group or a child of the color group. Personally, I'm leaning towards the latter. In this example it makes more sense to me to think of all 3 as accent colors, as opposed to 1 arbitrary color + 2 accent colors. It's just so happens that one of those accent colors doesn't need a further suffix appended to its name when exported as code.

I therefore like this kind of approach, where that default token is still something within the group. The hybrid approaches lift it up a level in the hierarchy and that doesn't feel right to me.

If we did something like _default, I think it could referenced as {color.accent._default}, but perhaps as a convenient shorthand you'd also be allowed to do {color.accent}. The rule that parsers could follow could be that a reference pointing to a group would resolve to the _default token in that group if it exists or be invalid otherwise.

But, I am intrigued by @TravisSpomer's desire to be able reference groups some day. I think there is a way that could co-exist with the _default referencing shorthand I've just proposed. My reading of his example is that button.text is actually a group referencing another group as a shortcut to create a batch of alias tokens.

So, rather than make the value of button.text be something that looks like a token (since it has a value property), why not do this:

{
  "neutralcontrol": {
    "foreground": {
      "_type": "color",
      "rest": { "_value": "#..." },
      "hover": { "_value": "#..." },
      "pressed": { "_value": "#..." },
      "disabled": { "_value": "#..." }
    },
    "background": {
      "_type": "color",
      "rest": { "_value": "#..." },
      "hover": { "_value": "#..." },
      "pressed": { "_value": "#..." },
      "disabled": { "_value": "#..." }
    }
  },
  "button":
  {
    "text": {
      // The value is no longer an object with a token property and therefore
      // not a token. So, when a "bare" reference like this is encountered parsers
      // are required to interpret it as a *group* reference.
      "color": "{neutralcontrol.foreground}" 
    },
    "base": {
      "color": "{neutralcontrol.background}"
    }
  }
}

Even if {neutralcontrol.foreground} contained a _default token, parsers would know not treat the group reference as a shortcut to that default token. Instead, you'd get button.text.color._default being an alias token for neutralcontrol.foreground._default.

That could actually be quite nice if you'd prefer to use _default instead of rest in your naming:

{
  "neutralcontrol": {
    "foreground": {
      "_type": "color",
      "_default": { "_value": "#..." },
      "hover": { "_value": "#..." },
      "pressed": { "_value": "#..." },
      "disabled": { "_value": "#..." }
    },
    "background": {
      "_type": "color",
      "_default": { "_value": "#..." },
      "hover": { "_value": "#..." },
      "pressed": { "_value": "#..." },
      "disabled": { "_value": "#..." }
    }
  },
  "button":
  {
    "text": {
      // The value is no longer an object with a token property and therefore
      // not a token. So, when a "bare" reference like this is encountered parsers
      // are required to interpret it as a *group* reference.
      "color": "{neutralcontrol.foreground}" 
    },
    "base": {
      "color": "{neutralcontrol.background}"
    }
  }
}

An export tool outputting SASS might then produce something along the lines of:

$neutralcontrol-foreground: #... ;
$neutralcontrol-foreground-hover: #...;
$neutralcontrol-foreground-pressed: #...;
// etc...

$button-text-color: $neutralcontrol-foreground;
$button-text-color-hover: $neutralcontrol-foreground-hover;
$button-text-color-pressed: $neutralcontrol-foreground-pressed;
// etc...

What do you think?

@c1rrus
Copy link
Member Author

c1rrus commented Jan 13, 2022

Going slightly (more) off-topic, it occurs to me that it might be nice to for "bare" references to also be usable as a shorthand for creating alias tokens. For example:

{
  "some-group": {
    "original-token": {
      "value": "2rem",
      "type": "dimension"
    }
  },

  // What we already know and love...
  "long-hand-alias": {
    "value": "{some-group.original-token}"
  },

  // ...could be equivalent to:
  "short-hand-alias": "{some-group.original-token}"
}

However, then we'd still need to find a way to reference groups. So maybe there's a slight extension to the reference syntax to allow that:

{
  "neutralcontrol": {
    "foreground": {
      "_type": "color",
      "_default": { "_value": "#..." },
      "hover": { "_value": "#..." },
      "pressed": { "_value": "#..." },
      "disabled": { "_value": "#..." }
    }
  },
  
  "button": {
    "text": {
      "color": "{neutralcontrol.foreground.*}", // notice the .* at the end!

      // and the above is basically a short-hand for doing this:
      "color-longhand": {
        "_default": { "_value": "{neutralcontrol.foreground}" },
        "hover": { "_value": "{neutralcontrol.foreground.hover}" },
        "pressed": { "_value": "{neutralcontrol.foreground.pressed}" },
        "disabled": { "_value": "{neutralcontrol.foreground.disabled}" }
      }
    }
  }
}

Probably none of this stuff will be in our initial spec, but the more I think about it, the more I'd like something like this to be possible in the future!

@TravisSpomer
Copy link

My reading of his example is that button.text is actually a group referencing another group as a shortcut to create a batch of alias tokens.

Yes, precisely.

The system I'm using right now already has groups and aliases to groups implemented, and I'd say that it's partly-successful: It definitely does achieve its goal of allowing easier reuse of groups of tokens, but about half of people find the concept confusing, and it's not split down engineer-or-designer lines. Some designers find groups intuitive, but the one currently in charge of our design system finds them hopelessly complicated. Most engineers grasp the idea easily, but some are resistant because they keep thinking that it's something they'll need to support at runtime instead of getting "compiled out."

@ilikescience
Copy link

I'm mostly just toying with two different approaches to the group syntax, which may necessitate a separate issue. So I'm gonna open up a new discussion to continue that thread to keep things on-topic here.

I do want to be direct about the question: should we allow a group and a token to have the same name?

In once sense, it makes it slightly easier to organize your tokens if you like to have things like accent and accent.light both refer to tokens.

However, it might make it very difficult to talk about (and parse) the thing called accent. Are you talking about the accent group, or the accent token? Say you wanted to compile the accent token into one file, but the contents of the accent group into a different file? I think it'd be a bit fuzzy as to the right way to do that.

I'd love to try and motivate the requirement that tokens and groups should be able to share a name before going deep on solving it - but please excuse me if that conversation was had in an editors meeting.

@c1rrus
Copy link
Member Author

c1rrus commented Jan 17, 2022

@ilikescience :

I'd love to try and motivate the requirement that tokens and groups should be able to share a name before going deep on solving it - but please excuse me if that conversation was had in an editors meeting.

Good question. It certainly wasn't discussed in any depth and may well be an edge case we don't need to make special accommodations for.

My (vague) recollection is that @dbanksdesign once mentioned folks trying to achieve something similar in Style Dictionary. I've just been through their old issues and found this one where someone wanted to do this (and interestingly used DEFAULT as the name): amzn/style-dictionary#716

So, if this is something folks want to do, it would be nice if our format's spec had an official answer for how to do it - even it's just some kind of work-around rather than a first class feature of the format.

At our last format editor's meeting I suggested opening an issue for this now as this discussion might help inform the ongoing reserved words discussion (#61). My hunch was this might highlight the need for an additional group-level property and that in turn would potentially be new reserved word we'd need to accommodate somehow.

@c1rrus
Copy link
Member Author

c1rrus commented Jan 17, 2022

@TravisSpomer

about half of people find the concept confusing, and it's not split down engineer-or-designer lines.

That's really interesting! Thanks for sharing.

Makes me think there could be a market for DCTG file linters someday. People already use things like ESLint and Stylelint to enforce their organisations coding standards. Perhaps one day an equivalent will exist for our spec's file format. Then, if one organisation or team finds group aliases confusing they could discourage their use by configuring their linter to flag it (and suggest creating several token aliases as an alternative).

@ilikescience
Copy link

@c1rrus thanks for the clarification! I see the use case and I also want to see if we can accommodate it if possible.

The hard part for me is that the thing (say, color.accent) should be formatted and behave like a token when it's used as a token, and be formatted and behave like a group when it's used as a group.

So, say we were working with color.accent as a token. The spec currently defines a token as an object with a "value" property, so we should expect color.accent to have a value. So far, none of the examples we've explored meet this expectation!

Next, we work with color.accent as a group - say, put its children into their own css file when translating to css. This is an easier case since there's no real format requirements for a group - but since any object can be a token or a group, the parser will essentially have to ask "are there any items in this object that aren't reserved words?" — iteratively! — before concluding that it's safe to move back up the tree.

Thinking through this now, I see how some of the things we've discussed resolve the bulk of the issue. Especially _default (or something like that) — _default should be a regular old token with a value (as you've shown).

I do think that these cases outline one argument for why groups should have "_type": "group" — if they didn't, a parser would have to do some extra work to figure out how to work with the object or token. It isn't a world-ending requirement, and it's definitely a tradeoff either way. I'm going to open my group formatting issue so we can explore further.

@ChucKN0risK ChucKN0risK added the To be reviewed by editors Issues that need to be reviewed in an upcoming meeting between editors. label Mar 8, 2022
@kaelig kaelig added dtcg-format All issues related to the format specification. Needs Feedback/Review Open for feedback from the community, and may require a review from editors. labels Mar 8, 2022
@kevinmpowell kevinmpowell added this to the Next Draft Priority milestone Oct 3, 2022
@kevinmpowell kevinmpowell removed Needs Feedback/Review Open for feedback from the community, and may require a review from editors. dtcg-format All issues related to the format specification. To be reviewed by editors Issues that need to be reviewed in an upcoming meeting between editors. labels Oct 3, 2022
@c1rrus c1rrus self-assigned this May 18, 2023
@ilikescience
Copy link

Ok, returning to this after having some experience with trying to parse larger token files, I think some kind of explicit key is necessary. The main reason is that groups can have properties that are named the same as tokens. For example:

{
  "color": {
    "$type": "color",
    "$description": "lorem ipsum",
    "$value": "blue",
    "primary": {
      "$value": "red"
    }
  }
}

In this example, it's unclear if the "$type" at the root applies to the root token, "color" (blue) or is intended to cascade down to child tokens like "color.primary". Same with description; is it a description for the group? or a description of the root token?

So, I'd like to recommend the following change to the group format:

"groupName": {
  "$type": "..."          // optional type that can be inherited by children
  "$description": "..."   // optional description of the group
  "$rootToken": {}        // optional root token
  "$extensions": {}       // optional extensions as usual
  "childName": {}         // required, can be a group or a token.
  }

I'm open to other names, but I chose "rootToken" to be very explicit that this should be a token that is associated with the root of the group. I also considered "root" and "base". I deliberately avoided "default", as this doesn't quite make sense grammatically — a default implies options, which isn't how groups work.

@IanVS
Copy link

IanVS commented Feb 28, 2024

Just wanted to chime in briefly here to say that I'm experimenting with setting up design tokens using the DTCG format, and our current color variables are set up something like this:

:root {
  --color-gray: var(--color-gray-50);
  --color-gray-10: hsl(214deg, 15%, 10%);
  --color-gray-20: hsl(214deg, 15%, 20%);
  --color-gray-30: hsl(214deg, 15%, 30%);
  --color-gray-40: hsl(214deg, 15%, 40%);
  --color-gray-50: hsl(214deg, 15%, 50%);
}

My first intuition was to try to replicate this by using $value, without worrying about the reference/alias to start with:

{
  "color": {
    "$type": "color",
    "base": {
      "gray": {
        "$value": "#6c7d93"
        "10" : { "$value": "#16191d" },
        "15": { "$value": "#21252c" },
        "20"  : { "$value": "#2b323b" },
        "30"  : { "$value": "#414b58" },
        "40"  : { "$value": "#576475" },
        "50"  : { "$value": "#6c7d93" }
      },
    }
  }
}

This does not work in style-dictionary, so I came here and found this issue. I think it would be great to have some kind of guidance on how to accomplish this. I don't have strong opinions on how it should be done or the various tradeoffs, but what I posted above was the most intuitive for me personally. I'm just getting started with this, so I thought I'd share my "beginner mindset" in case that's useful. :)

@whoisryosuke
Copy link

@IanVS I don't think parsers like Style Dictionary support a top-level variable and nested variables inside. It's either one or the other.

If I had to guess it's probably an issue with some exports, like the JS object syntax used by CSS in JS platforms. If you had a top-level variable defined, you couldn't also have an array of child elements, one would override the other.

{
   colors: {
    // It can't be a string and object at same time
    gray: "#6c7d93",
    gray: {
      10: "#16191d",
    },
  },
}

You'd have to do something like this and make your top level gray something like --color-gray-0:

{
  "color": {
    "$type": "color",
    "base": {
      "gray": {
        "0": { "$value": "#6c7d93" },
        "10" : { "$value": "#16191d" },
        "15": { "$value": "#21252c" },
        "20"  : { "$value": "#2b323b" },
        "30"  : { "$value": "#414b58" },
        "40"  : { "$value": "#576475" },
        "50"  : { "$value": "#6c7d93" }
      },
    }
  }
}

This kind of pattern happens more often with variants and component/composite tokens. It's easy to want to do a button.background and then a button.background.disabled -- but that causes similar collisions. So it's best to name the "default" state as something (usually default, initial, etc): button.background becomes button.background.default.


To acknowledge the discussion: I'm not sure how having a group type would resolve the consolidation of the tokens into variables. Unless each variable is unique (like CSS exports, where you get individual --color-gray names -- or individual JS var export like const colorGray and const colorGray10), this system won't work with parsers like Style Dictionary.

For example, I wanted to theme the gray colors, this definitely works with individual variables:

// Dark theme
export const colorGray = "#6c7d93";
export const colorGray10 = "#16191d";

Then I could use these inside my project:

import lightTheme from "./dark"
import darkTheme from "./dark"

const theme = isDarkMode ? darkTheme : lightTheme;

But if I had an object based syntax, this doesn't work.

And ideally the goal of these tokens would be to be used across all -- if not most platforms? So I feel like this would just always conflict with dynamic languages like JS and the common practice of theming (like CSS in JS).

If it were supported, there'd definitely have to be some sort of guidelines for parsers to understand that groups would only be supported in "split variable" contexts. This way they could throw an error to help user understand why the tokens aren't properly reflected in more dynamic contexts (e.g. JS object syntax, SASS maps, etc).

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

No branches or pull requests

9 participants