-
Notifications
You must be signed in to change notification settings - Fork 30
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
[Feature]: Allow having both the output of ts/typography/css/shorthand and expand.typography = true #128
Comments
Feel free to contribute :) let me know if you need help! |
Here's a formatter in a gist which adds a letter spacing token after a typography font css shorthand token. It would be really cool to expand the typography token into all of its tokens (as per the That would provide ultimate flexibility and themability. I'm not sure if we need that though, so I haven't implemented it. --type-100-weight: 800;
--type-100-size: 10px;
--type-100-line-height: 16px;
--type-100-font-family: var(--font-nunito-sans);
--type-100: var(--type-100-weight) var(--type-100-size)/var(--type-100-line-height) var(--type-100-font-family); |
Here's another gist. It moves the expansion logic into its own class, where it can be called from a custom formatter. |
just my 2 cents as I ran into a similar issue with This splits them into individual fields and removes the main token from the output (could be configured to keep if needed). /**
* Expands typography whilst keeping references to the original typography object.
*
* This formatter creates an individual token for each of the typography token keys.
* For example:
* --token-key-font-family: var(--global-reference-font-family);
* --token-key-font-weight: var(--global-reference-font-weight);
* --token-key-line-height: var(--global-reference-line-height);
* --token-key-font-size: var(--global-reference-font-size);
* --token-key-letter-spacing: var(--global-reference-letter-spacing);
* --token-key-paragraph-spacing: var(--global-reference-paragraph-spacing);
* --token-key-text-case: var(--global-reference-text-case);
* --token-key-text-decoration: var(--global-reference-text-decoration);
*/
StyleDictionary.registerFormat({
name: 'css/customFormatter',
formatter: function ({ dictionary, platform, options, file }) {
const { selector, outputReferences } = options
const basePropertyFormatter = createPropertyFormatter({
outputReferences,
dictionary,
format: 'css',
})
function propertyFormatter(token) {
const cssVariables = []
if (token.type === 'typography') {
const values = {
fontFamily: {
name: 'font-family',
value: getByTypographyKey(token, 'fontFamily'),
},
fontWeight: {
name: 'font-weight',
value: getByTypographyKey(token, 'fontWeight'),
},
lineHeight: {
name: 'line-height',
value: getByTypographyKey(token, 'lineHeight'),
},
fontSize: {
name: 'font-size',
value: getByTypographyKey(token, 'fontSize'),
},
letterSpacing: {
name: 'letter-spacing',
value: getByTypographyKey(token, 'letterSpacing'),
},
paragraphSpacing: {
name: 'paragraph-spacing',
value: getByTypographyKey(token, 'paragraphSpacing'),
},
textCase: {
name: 'text-case',
value: getByTypographyKey(token, 'textCase'),
},
textDecoration: {
name: 'text-decoration',
value: getByTypographyKey(token, 'textDecoration'),
},
}
Object.keys(values).forEach((typographyKey) => {
const data = values[typographyKey]
if (data.value) {
cssVariables.push(` --${token.name}-${data.name}: ${data.value};`)
}
})
} else {
// run for 'typography' types too if you wish to keep the original token here
cssVariables.push(basePropertyFormatter(token))
}
return cssVariables.join('\n')
}
return `${fileHeader({ file })}${selector} {
${dictionary.allTokens.map(propertyFormatter).join('\n')}
}`
function getByTypographyKey(token, key) {
const original = token.original
let value = ''
/*
* A typography token can either be an object, which defines each part (font size, line height, etc).
* Or it might be an alias (a string) which references another token.
*/
// If it contains the data we need, use that data.
if (typeof original.value == 'object') {
value = getValue(token, key)
} else {
// If it's a reference, get the value that it's referring to.
// Note that if outputReferences is true, instead of returning the source tokens value, it'll return a
// var(--...) based on the source tokens name.
if (!dictionary.usesReference(original.value)) {
throw 'Typography string must be a reference?'
}
value = getValue(dictionary.getReferences(original.value)[0], key)
}
return value
}
function getValue(token, key) {
if (!(key in token.original.value)) {
return ''
}
const originalValueForKey = token.original.value[key]
if (dictionary.usesReference(originalValueForKey)) {
const source = dictionary.getReferences(originalValueForKey)[0]
return options.outputReferences ? `var(--${source.name})` : source.value
}
return originalValueForKey
}
},
}) |
I did some more investigation and the short answer to this issue is that this is not at all trivial to do because we're mixing up the concept of a Imagine this typography token: {
"foo": {
"value": {
"fontSize": "16px",
"fontFamily": "Arial",
"fontWeight": "Bold",
"lineHeight": "1"
},
"type": "typography"
}
} After expanding this becomes: {
"foo": {
"fontSize": {
"value": "16px",
"type": "fontSizes"
},
"fontFamily": {
"value": "Arial",
"type": "fontFamilies"
},
"fontWeight": {
"value": "Bold",
"type": "fontWeights"
},
"lineHeight": {
"value": "1",
"type": "lineHeights"
}
}
} Now let's imagine we want to keep the original composite value as well, the structure would look something like: {
"foo": {
"fontSize": {
"value": "16px",
"type": "fontSizes"
},
"fontFamily": {
"value": "Arial",
"type": "fontFamilies"
},
"fontWeight": {
"value": "Bold",
"type": "fontWeights"
},
"lineHeight": {
"value": "1",
"type": "lineHeights"
}
},
"value": {
"fontSize": "16px",
"fontFamily": "Arial",
"fontWeight": "Bold",
"lineHeight": "1"
},
"type": "typography"
} Making A potential solution is to make the structure something like this: {
"foo": {
"fontSize": {
"value": "16px",
"type": "fontSizes"
},
"fontFamily": {
"value": "Arial",
"type": "fontFamilies"
},
"fontWeight": {
"value": "Bold",
"type": "fontWeights"
},
"lineHeight": {
"value": "1",
"type": "lineHeights"
},
"__sd_keep_original__": {
"value": {
"fontSize": "16px",
"fontFamily": "Arial",
"fontWeight": "Bold",
"lineHeight": "1"
},
"type": "typography"
}
}
} Which would result in :root {
--foo-font-size: 16px;
--foo-font-family: Arial;
--foo-font-weight: 700;
--foo-line-height: 1;
--foo-sd-keep-original: 700 16px/1 Arial;
} So the only thing that's left to do in sd-transforms is to register a name transform by default takes into account the import { kebabCase, camelCase, capitalCase, snakeCase, constantCase } from 'change-case';
const casingMap = new Map([
['kebab', kebabCase],
['camel', camelCase],
['pascal', capitalCase],
['snake', snakeCase],
['constant', constantCase],
]);
StyleDictionary.registerTransform({
type: "name",
name: "ts/name",
transformer: (token) => {
return casingMap.get(casing)(token.path.filter(p => p !== '__sd_keep_original__').join('_'));
}
}); Which then results in: :root {
--foo-font-size: 16px;
--foo-font-family: Arial;
--foo-font-weight: 700;
--foo-line-height: 1;
--foo: 700 16px/1 Arial;
} Let me know if this makes sense, then I can go ahead and implement this when I get prio for it, or someone can speed it along by doing it themselves, hopefully it's clear enough from my explanation how to implement it, feel free to send me a message on slack if you need help |
Marking as external since Style Dictionary has its own expand utils now for composite type tokens, so this feature should be added there. |
What feature would you like?
The reason we want this is so we can still use the typography shorthand as we find it very handy but also use the css variable for letter spacing which value doesn't exist in the shorthand.
maybe an expand.keepOriginal option with the same signature (true|false|Filter )
Threat in Slack: https://tokens-studio.slack.com/archives/C0336AEQ06Q/p1684318079922049
Would you be available to contribute this feature?
The text was updated successfully, but these errors were encountered: