Open kevinmpowell opened 2 years ago
The CSS part is maybe a bit misleading. Having keywords/enums for certain effects, values, ... is common in all contexts.
https://github.com/design-tokens/community-group/issues/102#issuecomment-1299315744
@chris-dura said :
I've run into a scenario where I want to use the keyword normal for letterSpacing, but the current draft requires that it be a $type: dimension, which needs a px or rem, afaict.
Definitely agree that we need a type that passes through a string of text, for all sorts of string-y values, not just CSS keywords. I'd want to broaden the type to something like $string
, which can be any valid text string, including escaped characters (see #167 and #171)
Definitely agree that we need a type that passes through a string of text, for all sorts of string-y values, not just CSS keywords
I think you misunderstood :) This issue is not about string values, it is about keywords.
Keyword:
.foo {
text-align: center;
}
String:
.foo {
content: "string content";
}
But in JSON both need to be stored as strings :
{
"$value": "center"
}
{
"$value": "string content"
}
But all languages/platforms have keywords:
{
"$value": "infinity"
}
const foo = Infinity;
This issue is partly resolved by these recent resolutions :
$type
required : https://github.com/design-tokens/community-group/issues/139#issuecomment-1331346032These removed a lot of ambiguity.
For some platforms the fontFamily
is still ambiguous.
It is impossible to determine if something like "serif"
is meant as a string or a keyword.
@font-face {
font-family: "serif";
src: ...
}
.foo {
font-family: "serif";
}
or
.foo {
font-family: serif;
}
iOS seems to have the same concepts : https://developer.apple.com/documentation/uikit/uifont/3042484-monospacedsystemfont
class func monospacedSystemFont(
ofSize fontSize: CGFloat,
weight: UIFont.Weight
) -> UIFont
I think the underlying issue is this :
User defined values :
"#ff00ff"
"Comic Sans MS"
Typically strings, numbers, ...
Platform defined values :
Typically keywords, enums, functions, ...
Some platform defined values have equivalents in all or most platforms. How do we define token values that are intended to use these shared platform defined values.
This issue is not about string values, it is about keywords
Yes, sorry, I was using "string" to mean "a piece of text".
I think many use cases can be covered by defining escape sequences (#171). That will help an author in cases like:
{
"keyword-like-font-name": {
"$value": "Helvetica",
"$type": "string"
},
"string-like-font-name": {
"$value": "\"Helvetica\"",
"$type": "string"
}
}
(maybe string
isn't the right word here, but I don't think keyword
is, either. Maybe something like raw
or preformatted
? see #91 )
Which, when translated into css variables, would result in the following.
:root {
--keyword-like-font-name: Helvetica;
--string-like-font-name: "Helvetica";
}
If you'll forgive the pedantry, in this example,
{
"$value": "infinity"
}
const foo = Infinity;
Infinity
is a number, not a keyword (is this a good argument for having a number
type in the spec?)
The cases that are not covered by the string
type and escaping are where each platform has a different convention for referring to the exact same thing.
Your font example is the most salient one. In CSS, the keyword for "the default system font" is system-ui
. In Swift, (please correct me if i'm wrong), you need to use the .system()
class with a required size
property (eg .system(size: 34)
).
Which leads to the question: should translators be required to maintain the mapping these one-to-many keyword values in tokens to the correct platform-specific versions? Or should the spec allow for the author to provide the mapping? Or both?
Infinity is a number, not a keyword (is this a good argument for having a number type in the spec?)
That is incorrect.
There is no numeric way to express Infinity
.
It is a keyword that equates to a number when evaluated.
To address the underlying numeric value you need to use a platform specific API.
This can be a keyword like Infinity
, e
pi
, ...
Escaping quotes will not work. These are not raw characters.
What if one of the target platforms uses parentheses as string start and end tokens. Or less exotic, a platform that uses only single quotes. This is unusual but not impossible.
"$value": "\"Helvetica\"",
makes this specification specific to platforms that can express strings with double quotes.
Which leads to the question: should translators be required to maintain the mapping these one-to-many keyword values in tokens to the correct platform-specific versions? Or should the spec allow for the author to provide the mapping? Or both?
Generally, I think "the Tokens spec" should be as agnostic as possible... obviously it's very "CSS-leaning" (as evidenced by the title of this issue)... but "how to represent platform-specific syntax" seems fully the responsibility of the translators/transformers/tools, in my opinion.
But, fwiw, the spec already allows for authors to provide custom metadata (via the $extensions
) and I've noticed that I've had to make extensive (pun intended) use of it to add custom logic around specific platform outputs.
For example, on a css
platform I need to convert certain JSON string values to an unquoted keyword
, while maybe I have to convert it to something else in a Swift-based output... so, without additions to the spec, if I had to handle this issue, I'd probably do something like this:
{
"keyword-like-font-name": {
"$value": "Helvetica",
"$type": "string"
"$extensions": {
"isCssKeyword": true,
},
},
}
... then in the transform layer, I'd hook into isCssKeyword
to output what I needed to.
but "how to represent platform-specific syntax" seems fully the responsibility of the translators/transformers/tools, in my opinion.
This is partly true. It is the responsibility of translation tools to transform to the correct platform specific API. The specification can be agnostic of all the platform specific conversions.
But this specification does need to define the supported/adopted concepts.
A font family that matches the OS UI
system-ui
.system()
This specification does not define system-ui
the CSS value, or .system
the iOS API.
It only needs to define some way to represent the concept.
Translation tools might also provide a public API so that users of those tools can provide fallback/custom values for concepts that do not have a platform specific equivalent.
But, fwiw, the spec already allows for authors to provide custom metadata (via the $extensions) and I've noticed that I've had to make extensive (pun intended) use of it to add custom logic around specific platform outputs.
Do you have concrete examples? Are these for supported token types in this specification?
Maybe best to open specific issues here for each.
Afaik this specification is not intended to depend on $extensions
for basic things.
Maybe best to open specific issues here for each. Afaik this specification is not intended to depend on
$extensions
for basic things.
Fair, but I'm not sure platform-specific things qualify as "basic"... at least not across the board. It kind of smells to me that an arbitrary update to iOS/Swift that Apple decides to make would/could require an update to the Tokens spec to support some new type or syntax in iOS 17? But, as you somewhat said, the "concept" of a keyword (or even more agnostic... the concept of an "unquoted string" type) may be universal enough to warrant placement in the spec?
Do you have concrete examples? Are these for supported token types in this specification?
Tbf, I'm mostly using $extensions
to patch holes because the transformer I use (StyleDictionary) hasn't implemented the spec, yet...
$extensions: {
deprecated: {
renamed: "new-token-name",
message: "@deprecated: Replace with 'new-token-name' token",
},
swift: {
available: {
platform: "iOS 13",
introduced: "4.0.0",
deprecated: "4.1.0",
obsoleted: "5.0.0",
unavailable: true,
},
optional: true,
type: "UInt",
},
tier: "option" | "intent" | "component" | "decision"
},
Most of the above values are used in some custom output formats I've created. For example, I have a custom Swift format that outputs some @available()
syntax that provides helpful dev info in Xcode.
The swift.type: "UInt"
property basically forces it to be a UInt
, instead of the default CGFloat
in the final Swift output.
However, the tier
property could probably be handled by Groups
as detailed in the spec, it's just Style Dictionary hasn't implemented that yet. (And, to be honest, even if they do, I may not want to author the Tokens that way, so the possibility to still want some custom metadata to exist is high)
But, as you somewhat said, the "concept" of a keyword (or even more agnostic... the concept of an "unquoted string" type) may be universal enough to warrant placement in the spec?
Hehe, no, it has to be the other way around. An unquoted string type would be platform specific.
{
"$type": "unquoted-string", // or "keyword", "raw", ...
"$value": "system-ui"
}
This token can only work in CSS. It is platform specific.
{
"$type": "fontFamilyKeyword",
"$value": "systemUI"
}
This is different from the regular fontFamily
token type.
Translation tools can then provide platform specific transforms.
Instead of generating a string value ("systemUI"
) then can do :
system-ui
.system()
This works because there are two bits of information :
There are multiple ways to have the same outcome :
{
"$type": "fontFamily",
"$keyword": true,
"$value": "systemUI"
}
...
abusing/overloading the word "keyword" a bit, but it is closest to what is intended
Do we need types for CSS Keywords that aren't strings: like
text-align
values:left
,right
,center
etc.