Open romainmenke opened 2 years ago
To be honest, I do not think logic or conditions should exist in the spec. Design tokens are meant to contain raw values to be processed.
To be honest, I do not think logic or conditions should exist in the spec. Design tokens are meant to contain raw values to be processed.
I actually agree and should have included this in my initial statement. I think this is best solved by having multiple token files.
But if something like "theming" is handled by the spec I think it should be more abstract and include the cases listed above.
In our PostCSS plugin for style dictionary we solve this outside of token files : https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-design-tokens#is
@design-tokens url('./tokens-light.json') format('style-dictionary3');
@design-tokens url('./tokens-dark.json') when('theme-blue') format('style-dictionary3');
.foo {
color: design-token('color.background.primary');
}
The value of color.background.primary
changes depending on user config when building the CSS.
The same mechanic can be expanded on later to include conditions typically found in @media
or @supports
.
It's true trying to introduce logic is a slippery slope. But similar to themes we can have 'flags' which can change values by their presence, and said flags can be flipped by much more complex conditional logic outside of the tokens file.
On Tue, 13 Sep, 2022, 1:04 pm Romain Menke, @.***> wrote:
To be honest, I do not think logic or conditions should exist in the spec. Design tokens are meant to contain raw values to be processed.
I actually agree and should have included this in my initial statement. I think this is best solved by having multiple token files.
But if something like "theming" is handled by the spec I think it should be more abstract and include the cases listed above.
In our PostCSS plugin for style dictionary we solve this outside of token files : https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-design-tokens#is
@design-tokens url('./tokens-light.json') @.*** url('./tokens-dark.json') when('theme-blue') format('style-dictionary3');
.foo { color: design-token('color.background.primary'); }
The value of color.background.primary changes depending on user config when building the CSS.
The same mechanic can be expanded on later to include conditions typically found in @media or @supports.
— Reply to this email directly, view it on GitHub https://github.com/design-tokens/community-group/issues/169#issuecomment-1245015596, or unsubscribe https://github.com/notifications/unsubscribe-auth/AEKS36BLHIKZIP5QQ66LBJ3V6AU6PANCNFSM6AAAAAAQLEF4XA . You are receiving this because you are subscribed to this thread.Message ID: @.***>
At my last company I put together something similar to the $conditionalValues
@romainmenke is proposing above, to solve for nearly the same requirements (if not an identical solution, the same in spirit). It required a great deal of additional complexity in our token translator, and all formatters had to accommodate these mystery box tokens.
As I am at a new company, and am yet again standing up some design token infrastructure; I’ll be opting to use the file switching method and perhaps some post-translation actions to generate additional token documents with media queries and responsive values.
In any case, I feel like the existing spec could handle the proposed approach by using a custom entry on the $extensions
property.
In any case, I feel like the existing spec could handle the proposed approach by using a custom entry on the $extensions property.
https://tr.designtokens.org/format/#extensions
In order to maintain interoperability between tools that support this format, teams and tools SHOULD restrict their usage of extension data to optional meta-data that is not crucial to understanding that token's value.
Using $extensions
for this use case is discouraged by the spec.
It should be solved with multiple files or in the spec itself.
At my last company I put together something similar to the $conditionalValues @romainmenke is proposing above, to solve for nearly the same requirements (if not an identical solution, the same in spirit). It required a great deal of additional complexity in our token translator, and all formatters had to accommodate these mystery box tokens.
@jeromefarnum Forgot to ask, but did this work well once the tooling was there to support it?
Using
$extensions
for this use case is discouraged by the spec. It should be solved with multiple files or in the spec itself.
Very fair point.
At my last company I put together something similar to the $conditionalValues @romainmenke is proposing above, to solve for nearly the same requirements (if not an identical solution, the same in spirit). It required a great deal of additional complexity in our token translator, and all formatters had to accommodate these mystery box tokens.
@jeromefarnum Forgot to ask, but did this work well once the tooling was there to support it?
@romainmenke let me try to answer this in short, and also with more detail should it help others.
TLDR; it worked well enough in the areas of our workflows where we implemented it, however as we didn’t have it implemented fully in our design tools it became a source of confusion and human error. Our team started with many conditional values as we initially built our design system, but over time greatly reduced the number of conditional tokens as the cost of system complexity wasn’t returned in added value (design systems have a natural inertia towards this, I think). At the time of my departure we were in the process of fully stripping out these conditional tokens in favor of a file swapping approach to handle the relatively few conditional tokens we had.
—
Functionally, after doing all the work to implement the feature, it worked well enough in the token translator. Though the additional complexity did make adding some features later more difficult.
We also had to add support for the feature to our token documentation site so designers could understand that SOME tokens had different values under SOME conditions. Frankly, as the person that was responsible for the token documentation site, I think how I communicated that wasn’t as successful as it should have been. We regularly had designers getting confused as to which value should be used for a token.
Part of the problem was that we didn’t have a proper plugin to connect our tokens to our design tools; or more precisely our plugin was in beta and wasn’t feature complete enough for our team to daily drive it. IIRC these responsive tokens were one of the more complex features to add to the plugin, where many of the other features were completed but this feature was one of a small set that was causing the plugin development to stretch out.
One aspect that made it challenging to implement in design tools was that we initially thought to use the artboard size to determine which conditional value to use, but we discovered that a large portion of the time tokens where being used was in component masters (pretty sensible) which nearly always used arbitrary artboard sizes. In those cases the token value that was selected due to artboard size was often not as desired; requiring the designer to manually configure which condition to use for the artboard.
In the end designers needed to intervene so often to select which conditional values should be used, IMO, it’s simply easier to use a file swapping method, use whatever complex conditional logic to swap files as needed and generate as many themes as needed at the token-translator stage to provide designers with the appropriate options they need (probably a matrix of light/dark and device/platforms covers the large majority of teams.) This approach likely covers most teams’ needs without having to clutter the specification or adding undue complexity for tooling builders.
/rant
@jeromefarnum Thank you for sharing this experience.
From this I read that it makes the simple things a little bit easier while making the hard things much harder. That designers and developers end up having to work around the system instead of using it.
The benefits of solving it outside the spec with multiple files:
I agree that tokens files aren't the right place to store conditional logic that applies on a platform level — the place to solve that problem is on the platform!
Commenting here with a similar issue and a slightly different proposal made on https://github.com/amzn/style-dictionary/issues/909. I believe there's room in the spec for tokens to have multiple value properties, though without embedding the logic as to what they mean.
I have developed multi-brand white-label token systems before and it made perfect sense there to use multiple files, but I believe the use case brought up by @romainmenke is legitimate and distinct.
In my current org, we have responsive size tokens, responsive typography tokens (size and leading), typography fonts that depend on country, dark mode, some token values changing for web vs mobile, and soon high-contrast too. If we were to have a files-based approach,
For those who don't know, SD processes tokens with two layers: transforms first process the value and attributes of individual tokens, and formats then aggregate a group of tokens to produce a platform-specific output.
I've come up with a proposal to only add extra value fields to a token because:
If I were to generalise, additional $value
fields in the spec would:
I believe this proposal could provide much of the value you seek, @romainmenke, whilst avoiding the complexity linked to having logic management in a spec. I'm curious to have your opinion on it.
@Sidnioulz Can you give a concrete example of how a tokens json file would look like under your proposal?
In SD format:
{
"color": {
"value": "#444",
"value_hc": "#222"
},
"size": {
"value": 24,
"value_breakpoint-md": 32
},
"fontFamily": {
"value": "Scto Grotesk",
"value_lang-el": "Noto Sans",
"value_lang-jp": "Noto Sans"
}
}
In W3C token format, I'm not sure what the best would be. I could have the same naive approach:
{
"color": {
"$value": "#444",
"$value_hc": "#222"
},
"size": {
"$value": "24px",
"$value_breakpoint-md": "32px"
},
"fontFamily": {
"$value": "Scto Grotesk",
"$value_lang-el": "Noto Sans",
"$value_lang-jp": "Noto Sans"
}
}
Or one with an $extraValues
that simplifies tooling support for such extra values, and also saves tool users from declaring the names of the custom value fields:
{
"color": {
"$value": "#444",
"$extraValues": [
"high-contrast": "#222"
]
},
"size": {
"$value": "24px",
"$extraValues": [
"breakpoint-md": "32px",
]
},
"fontFamily": {
"$value": "Scto Grotesk",
"$extraValues": [
"lang-el": "Noto Sans",
"lang-jp": "Noto Sans"
]
}
}
Or this could technically be hosted under $extensions
and be invisible for the token spec itself. Because the W3C spec has a wider range of consumers than SD, I wouldn't necessarily propose the same format, but I haven't yet given it enough thought to have an opinion.
I think there is little difference between my original schema and this proposal :)
It is a bit terser because it groups conditional feature names and values in a single string. That makes it seem simpler but it will complicate things for tools.
Yes, the goal is to allow tools to process what the conditions mean as a black box. It's easier for tools to display a name for an extraValue / conditionaValue if it's a simple string than if it's a more advanced format. If it's a more advanced format, with predefined conditions/values defined in the spec, then tool makers will need to continually add support to the specified conditions. Making it more basic, in my opinion, makes it easier to ignore for tools that don't need to have an opinion on it.
Though, to clarify, I'd also be very happy if what you proposed initially made its way into the spec :)
Ideally tools only hide or ignore parts of a tokens file because they recognize the content and know that it is irrelevant for their context.
hidden because known to be irrelevant vs. hidden because unknown
If it's a more advanced format, with predefined conditions/values defined in the spec, then tool makers will need to continually add support to the specified conditions
Initially this can be a major effort because the web and other targets are mature platforms with many conditionals.
But after the initial implementation this is something that moves exceedingly slow. It is very rare for a new dimension to display devices to be specified.
With this in mind it makes more sense to have a format that is more powerful in the long term.
Ideally tools only hide or ignore parts of a tokens file because they recognize the content and know that it is irrelevant for their context.
hidden because known to be irrelevant vs. hidden because unknown
The problem with that in a library, API or standard maintenance context is that it means many consumers must be aware of changes made by one upstream.
If a tool doesn't support a new feature, and finds unknown attributes on a token, it would be better if that tool didn't suddenly implode. If the tool's user community finds it irrelevant for the tool maker to code that the new unknown property is indeed irrelevant, that's time saved. We know how hard it is for FOSS devs to find time to maintain their code, so removing the need for tool makers to target the full tokens spec feels like a healthy thing to me.
Applied to this situation, I think it's ok if there's no fixed standard for what conditional / contextual token values can exist. Token writers will come up with ideas. Tool makers too. Of course, there could be a preset in the standard that grows as token usage matures, but I wouldn't make it a necessary thing to support for tool writers.
Newcomers who want to produce a new tool will also be better off if they can focus on supporting the core of a token and set aside advanced features. Having fewer mandatory features in the spec makes the barrier to entry for new players lower.
We know how hard it is for FOSS devs to find time to maintain their code, so removing the need for tool makers to target the full tokens spec feels like a healthy thing to me.
I strongly disagree with this statement. I am a FOSS dev and I've wasted so much time on software that didn't follow a specification. Either because there wasn't a specification, or because the original author of the software had opinions that differed from the specification.
The healthy thing for maintainers is often money and that a community can grow around a tool. For this to happen a tool needs to be reliable and useful in a wider ecosystem. Following a shared specification also ensures your tool has interoperability with other tools. That is the entire point of this specification.
If you need 5 tools that each interact with your tokens but each of these 5 only implements 80% of the specification, how much of the design token specification will be implemented and useful to you? You will only be able to use 0%-80% of the design tokens feature set.
I think you misread my comment :)
Ideally tools only hide or ignore parts of a tokens file because they recognize the content and know that it is irrelevant for their context.
I did not mean that tools can not have forwards and backwards compatibility by having good error recovery.
They must have these aspects. A tool can not implode when it encounters something unexpected.
I believe there's room in the spec for tokens to have multiple value properties, though without embedding the logic as to what they mean.
But your proposal is to obfuscate important and (initially) structured data by applying a string encoding. $value_lang-el
Tools still need to have general understanding that $value_
+ suffix is a conditional.
But by specifying the suffix part as blackbox it is no longer possible to have interop between tools.
Tools can not assign meaning to lang
in your proposal.
Token writers will come up with ideas. Tool makers too.
This is fine and that is why $extension
exists.
It allows anyone to experiment.
But following the standards process is important. If someone has a good idea and would like to see wider support across multiple tools then they should open an issue in the DTCG and request the specification to be amended. This benefits everyone.
see : https://github.com/design-tokens/community-group/issues/2
That issue was focussed on "theming" and already contains some great thoughts on that but I don't think that themes are the right abstraction. I also do not want to derail that thread.
The basic premise is fairly simple and shared with the concept of themes :
Change token values when X changes, where X is something external to the tokens file
Examples of factors that could affect token values :
The most mature system/API that exists for this is most likely Conditional At Rules in CSS (
@supports
and@media
specifically)In CSS Conditional At Rules "wrap" around style declarations.
For Design Tokens I think the inverse makes the most sense. A token describes a base value and a list of conditional values.
The exact data shape for
"conditions"
is likely non-ideal for all possible cases. But I think it illustrates the point.Condition names would need to be a specced list. Condition values per name would also need to be specced.
Design tools would provide UI to assign conditional values on tokens and would also use them.
Translation tools could generate the code for the conditionals so that developers can use a single token name while still having a dynamic outcome.
Specialised tools could provide validation. You might want to "lint" a tokens file and check that all colors have a dark and light variant.