composite typography fontFamily token reference gets interpreted as string
From my understanding in the spec it says the fontFamily can be a value or token, however SD always transforms it as a string.
spec: fontFamily
Input:
{
"heading-xxl": {
"$type": "typography",
"$value": {
"fontFamily": "{Typography.Heading.Family Sans}",
"fontSize": "{Typography.Heading.Size XXL}",
"fontWeight": "{Typography.Heading.Weight Bold}",
"fontStyle": "normal",
"lineHeight": "{Typography.Line Height.LH 9}"
}
}
}
Output:
:root {
--heading-xxl: normal var(--typography-heading-weight-bold)
var(--typography-heading-size-xxl) /
var(--typography-line-height-lh-9)
'var(--typography-heading-family-sans)';
}
Expected output:
:root {
--heading-xxl: normal var(--typography-heading-weight-bold)
var(--typography-heading-size-xxl) /
var(--typography-line-height-lh-9)
var(--typography-heading-family-sans);
}
+1 – I'm running into the same problem in my project.
The transform logic seems to unconditionally quote fontFamily values, without checking whether the resolved value is already a CSS function like var(). This leads to invalid CSS output.
According to the DTCG spec, fontFamily explicitly allows token references (aliases), not just string values. The current behavior violates this part of the specification.
Is there a recommended workaround, or would you accept a PR to fix the quoting logic in the typography transform?
I can't seem to reproduce this easily, https://configurator.tokens.studio/#project=dVTBjtsgEP0VZFXaJkqc7WF7cE+tVts9N5V6qPeADXboErBgnMaN/O8dDM5CNsklwMy8N35v4JQdqLF5bW1WZJvlslRkSR41URoIZwIIE4bXIIcVgZ2wpBGSk7/UEtqDXrdccUOBs9zVbUpVqsJoLD3hnqzXtCB3X42gknyTtH69++KPq4J8+twdw64uyMP9fdgwDOUPYbPjlAnVro9HWWBHZo9A2O5HrFmERbXY+AWbT+gCq8dSZavMQs+Ezv9YrfDrpp7K7OfQ6dbQbjeUWeEbdcfPnis6c6dPdC/kQLZU2STiYh9g6Lg7LbNGK/CpZbZKcg5U9iEpEqLM5qTRL6a/ceU7rJLGIhom9lxZodWZJWVwonroGaq+BeU6/sVFu4OrWGhIDMNuwUih+PNtGLQyhon8vAUIkTtXACNrYs1d5enN2DyYmUf2jZEzvngr/gXSU/U+Ooszxesr1TDIUO4nM8mIdZkQ2Dh77q3GEcUBNbwVFrjZPj71qgZ01uK04qxirNaqEe3F8Frdm3qi/T2LEw25p3gJakvdJiofuKm0FRD0YryhvYR0XjrDO6Nrbq02NqEB/Yqzt/ZsKVEnKTSogU3o8Em5cAwM+uASvxvdd76LKSuWFt+XmNj9ols3DSO3IBSF6R44jPkFS+6e9wmNgTOReykErSaCi0zdTeJf3PEQ66Hr4QdvuOGq9t2BwXmM8sa3zXn5cun3+B8=
The output there is correct (partial snippet):
:root {
--typography-heading-family-sans: 'Arial Black';
--heading-xxl: normal var(--c) var(--b)/var(--d) var(--typography-heading-family-sans);
}
Can you post a reproduction?
ahhh, we are missing the "$type": "fontFamily" in our primitive tokens - thanks for the help and sorry for the issue
I respectfully disagree - this is a Style Dictionary bug, not a user configuration issue
While adding "$type": "fontFamily" to primitive tokens works as a workaround, I believe the underlying issue is still a bug in Style Dictionary. Here's why:
1. $type is optional according to DTCG spec
The Design Tokens Community Group specification states that $type can be inherited from group or referenced tokens. It's not required on every token.
2. Figma exports tokens without $type on references
When exporting Figma Variables, reference tokens typically don't have an explicit $type:
{
"Typography": {
"Heading": {
"Family Sans": {
"$value": "{Font Family.Sans}"
}
}
}
}
This is standard behavior for Figma → Design Tokens workflows. Requiring users to manually add $type to every reference token breaks this workflow.
3. The bug is in createPropertyFormatter.js
The actual issue is in the reference replacement logic. When outputReferences: true:
- Font name
Open Sansgets quoted →'Open Sans'(correct) - Reference replacement finds
'Open Sans'and replaces withvar(--...) - Bug: Only the inner value is replaced, not the surrounding quotes
- Result:
'var(--typography-heading-family-sans)'instead ofvar(--typography-heading-family-sans)
Workaround / Patch
For anyone hitting this issue, here's a patch for createPropertyFormatter.js (around line 226):
// When replacing object values, also check for quoted versions of the value
// This fixes typography tokens where fontFamily values are quoted (e.g., 'Open Sans')
// and need to be replaced with CSS variables without the surrounding quotes
const stringValue = `${value}`;
const quotedRefVal = `'${refVal}'`;
const doubleQuotedRefVal = `"${refVal}"`;
if (originalIsObject && stringValue.includes(quotedRefVal)) {
value = stringValue.replace(quotedRefVal, replaceFunc());
} else if (originalIsObject && stringValue.includes(doubleQuotedRefVal)) {
value = stringValue.replace(doubleQuotedRefVal, replaceFunc());
} else {
value = stringValue.replace(
originalIsObject ? refVal : new RegExp(`{${ref.path.join('\\.')}(\\.\\$?value)?}`, 'g'),
replaceFunc,
);
}
How to apply with different package managers
pnpm:
pnpm patch [email protected]
# Apply the fix above, then:
pnpm patch-commit <temp-folder-path>
npm/yarn (using patch-package):
npm install patch-package --save-dev
# Edit node_modules/style-dictionary/lib/common/formatHelpers/createPropertyFormatter.js
npx patch-package style-dictionary
Yeah this is still a bug, agreed with @jensoppermann
The variable should not be in single quotes in the CSS output, regardless of whether the type is missing. Types are indeed completely optional, although somewhat required if you're expecting certain Style Dictionary transforms to do their job since transforms generally filter by token type. That said, outputting refs is something that happens in the format step and should work properly without $type being specified.
PR welcome!
I've opened a PR and took a shot at fixing this.