Open TravisSpomer opened 2 years ago
As you pointed out, there are differences in naming between CSS and WinUI (also iOS / macOS has its own system colors with different names as well). Which set of values should be allowed for color? Or, just throwing this out there, should this be its own type where you could specify anything?
{
"color": {
"highlight": {
"type": "system-color",
"value": "Highlight",
}
}
}
Or taking this further, should arbitrary values just be a generic constant
type that accept any string? e.g.:
{
"color": {
"active": {
"type": "constant",
"value": "ActiveText"
}
}
}
With the idea being that constants are meaningless references that one would have to implement platform-specific code (in your CSS vs WinUI example).
Note: ignore the actual type
s and names used; only just spitballing a general idea
I think you missed something there—my proposal is exactly the set of high contrast accessibility colors that are defined in the CSS spec, not just random words. I'm not sure what allowing arbitrary strings would accomplish here. Every platform will have its own names for those colors, so this spec needs to agree on what to call them, and it seems logical to use the same names for those colors that CSS does.
That’s fair; thanks for clarifying. I guess a followup question would be: are there any non-web system colors that don’t exist in the CSS spec?
I'm sure other platforms have a bunch. But even if there weren't, sometimes things aren't just a solid, fixed color: just on Windows, you have these and more:
And of course Windows isn't unique; each platform has its transparent materials and other fancy stuff. All of those things would have the same core problem and solution as the high contrast colors do in today's spec, in that you'd have to specify them as a fallback color and then add the real information using extensions. That's unavoidable, I think, but I believe that accessibility warrants special treatment since (1) it's functionally important, and (2) it can be represented in a cross-platform way by using the existing CSS system colors. (I'm assuming that the 15 CSS system colors were chosen as a good, representative set that are meaningful across multiple platforms.)
(This is what I mean by a fallback color with extensions:)
{
"MenuBackground":
{
"type": "color",
"value": "#e0e0e0",
"extensions": {
"com.microsoft.acrylic": {
"color": "#0000ff",
"opacity": 0.7,
"luminocityOpacity": 1
}
}
}
}
First off, I really like this idea. I agree that we should expand our color
type to allow some form of system color keywords.
Design tokens are platform-agnostic. As far as our spec is concerned, I believe that means it should allow people to express the broadest range of possible values for a given type. However, where different platforms use different keywords / syntax to express the same value, I don't think that means that our format needs to allow all of them. Quite the opposite in fact. IMHO having lots of different ways to say the same thing just makes the format harder to learn and creates more complexity for implementors.
It is the job of export tools like StyleDictionary or Theo to translate tokens in DTCG format files into what ever the target platform equivalents are. Or to substitute a sensible fallback value if a platform doesn't support something.
In this case, it seems CSS, WinUI and perhaps other platforms that have some notion of system colors use different names for them. For our spec we could invent our own set of system color keywords and provide some examples to show how they map to the CSS or WinUI equivalents. But, where an established standard exists, we may as well copy that. I'm therefore in favour of @TravisSpomer's proposal to just use the CSS system color keywords.
If at some point it turns out that some platforms have a wider range of system colors than what CSS covers, there's nothing stopping us expanding our set accordingly. Just because we start by copying something like CSS, doesn't necessarily mean we need to be constrained by that forever more.
One possibility is to take the lead from CSS and allow for a user-agent-defined value through something like env()
. In this case, you might define a stack of possible user agent values, with a static value as a fallback. Like this:
{
"color": {
"highlight": {
"type": "color",
"value": "env(Highlight, SystemColorHighlightColor, #00ffff)",
}
}
}
Kind of like how font stacks work: the parser would evaluate the list from right to left, returning the first match it finds in the user agent. So in a browser, that'd get interpreted as using the UA Highlight color, in WinUI it gets the value of SystemColorHighlightColor, and anywhere those things don't exist it just ends up being #00ffff.
Hmm, what would be an advantage of that over using just the CSS keywords? That would increase the parsing complexity of the format, and shift the responsibility of knowing each UI platform's quirks to the design tools producing the token format instead of on the tools converting design tokens to code, but the benefit is not clear to me.
Also, there would be an unlikely-but-possible ambiguity where that would yield unexpected results if, say, Highlight
was a text color on one platform (paired with, say, HighlightBackground
), but another platform had Highlight
as a background color and HighlightText
as the foreground color.
As you mentioned in your initial proposal, using the CSS keywords alone would require the parser to have an opinion of how color keywords map to each other between platforms. Say, for instance, I write a parser and I think that the keyword ButtonText
should be interpreted as the keyword controlTextColor
when used in a MacOS context. This is pretty logical, but someone writing the design token would have to know about and agree with my mapping; they couldn't decide "I think it would be more consistent for this token to be ButtonText
on the web and textColor
on MacOS.
The stack approach wouldn't necessarily require the user to write out every platform's keyword, just the ones they're writing for; if you wanted to just indicate env(Highlight)
, and only parsed your tokens into a web context, it'd probably be fine. But env(Highlight, SystemColorHighlightColor, highlightColor, #00ffff)
would allow a user to indicate which keyword they'd like the parser to use depending on what makes sense for each given platform.
As to the name collision issue, I agree that it's a potential problem; kind of like how with CSS I can indicate that I want text to be set in "Times New Roman." If the user installed some bootleg font on their computer that calls itself "Times New Roman" but is actually Papyrus, they'll be seeing something that I didn't intend. The same is true generally with color keywords; if multiple platforms use the same keyword to mean entirely different things, it makes it hard to accurately target that keyword. I don't know the solution to that one, but I imagine it's a problem no matter how you parse keywords.
Got it. Platform-specific variances will be necessary for all types of tokens, though, so those need a solution that's not specific to system colors. For example, maybe your text needs to be size 16 on everything except Android phones, where it's 15. That couldn't be specified like a font-family
stack—the only way you could represent that in the current system is with multiple JSON files: tokens.json
and a secondary tokens-android.json
that contains new tokens and/or token overrides that only apply to Android.
(Those definitely aren't theoretical problems; in the design tokens file that I'm working with currently, we have different font families, font sizes, font weights, line heights, corner radii, and paddings for some platforms, as well as different names for the exported tokens.)
I definitely agree that platform-specific variance is necessary, and that the way you've outlined to represent them (multiple JSON files) is the right approach.
I think we're puzzling through the use case where you don't want variance (or, want as little variance as possible) when using color keywords. But correct me if I'm misunderstanding.
For example, if you want your text size to be 16px on every platform, that's pretty easy. Whack 16px
in your tokens.json
level text token, done.
If you want your text color to be the user agent-defined text color is, on every platform?
I agree that extensions
isn't quite right for this; that seems to be more intended as a place for token authoring tools to keep tool-specific data, not necessarily as hints for compiling code out to end users.
But I also don't think that anchoring the spec on CSS's keywords is quite right; it would also require to the spec to map out the translation between CSS keywords and other platform-specific keywords, lest tools implement their own, inconsistent mappings. It seems in the spirit of the spec to allow for a much more extensible, platform-agnostic way of using color keywords. The env()
formatting is just one way to do this, there are probably lots. In fact, I imagine that the issue of platform-specific strings (oh no I think we're talking about localization) goes beyond just color, too.
I propose that the 15 values defined in the system colors section of CSS Color Module Level 4 be added: "ActiveText", "ButtonBorder", and so on.
I suggest linking to the actual section in CS Color 4 rather than the MDN sumary, which in this case is out of date. There are 19 non-deprecated system colors.
I've seen a few conversations where this shape of problem is bubbling up: authors shouldn't be prevented from using features of a platform (like CSS system color keywords) just because that feature does not exist in ALL platforms.
The more I think about it, the more I think @drwpow 's suggestion is the right path. (11 months ago lol)
If the author is writing tokens for a single platform, and thus no translation problems are likely. If the platform was CSS, a token might be defined like:
{
"color": {
"highlight": {
"$value": "Highlight",
"$type": "constant"
}
}
}
The "constant" type would tell the translator to pass the value through unchanged. (maybe "preformatted" is a better word for this type? or "keyword"?).
If the author is writing tokens for multiple platforms, then we don't want to pass the value through unchanged, since Highlight
is meaningless to anything but CSS. So we have to translate the value somehow.
Using Highlight
in CSS is saying "use whichever color the browser or user has stored as the value of the keyword Highlight
." As we've discussed, not all platforms have this feature. If I'm trying to translate to a platform that doesn't support forced color modes, should the translator throw an error? Should it try to "coerce" this value into an equivalent? None of these seem like good solutions.
One possible route is to explore platform-specific token values, but that's a topic for another thread.
The current draft of the spec allows colors to be specified as
#112233
or#112233aa
. That would mean that it's impossible to specify a color using the system colors used in high contrast modes. But, it's essential that a CSS file or XAML resources file (or whatever) produced by processing the token file be able to reference those colors, so a user who needs the background of their apps and web pages to be navy blue for readability can achieve that.I propose that the 15 values defined in the system colors section of CSS Color Module Level 4 be added:
"ActiveText"
,"ButtonBorder"
, and so on.Tools could leave those values as-is when exporting to CSS, map them to the equivalent platform-specific value for other platforms (for example,
"Highlight"
in CSS is"SystemColorHighlightColor"
in WinUI), or interpret them as aliases to hard-coded values (say,#00ffff
) in a UI design tool such as Figma where mapping them to a system color might not make sense.Without this, organizations that support high contrast accessibility modes would have to use hard-coded colors for those items along with additional data in the
extensions
node, and then have tools that know what to do with that extension data.