Skip to content

Commit 296bc3e

Browse files
WIP transformation for shadow to figma (#809)
* transformation for shadow to figma tokens
1 parent 11f00db commit 296bc3e

File tree

7 files changed

+407
-19
lines changed

7 files changed

+407
-19
lines changed

.changeset/ten-pots-applaud.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@primer/primitives': patch
3+
---
4+
5+
Adding shadow tokens for figma

scripts/buildPlatforms/buildFigma.ts

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,118 @@ export const buildFigma = (buildOptions: ConfigGeneratorOptions): void => {
7373
},
7474
}).buildAllPlatforms()
7575

76+
/** -----------------------------------
77+
* Shadow tokens
78+
* ----------------------------------- */
79+
const shadowFiles = [
80+
{
81+
name: 'light',
82+
source: [`src/tokens/functional/shadow/light.json5`],
83+
include: [
84+
`src/tokens/base/color/light/light.json5`,
85+
`src/tokens/functional/color/light/primitives-light.json5`,
86+
`src/tokens/functional/color/light/patterns-light.json5`,
87+
],
88+
mode: 'light',
89+
},
90+
{
91+
name: 'light-high-contrast',
92+
source: [`src/tokens/functional/shadow/light.json5`],
93+
include: [
94+
`src/tokens/base/color/light/light.json5`,
95+
`src/tokens/base/color/light/light.high-contrast.json5`,
96+
`src/tokens/functional/color/light/primitives-light.json5`,
97+
`src/tokens/functional/color/light/patterns-light.json5`,
98+
],
99+
mode: 'light high contrast',
100+
},
101+
{
102+
name: 'light-colorblind',
103+
source: [`src/tokens/functional/shadow/light.json5`],
104+
include: [
105+
`src/tokens/base/color/light/light.json5`,
106+
`src/tokens/base/color/light/light.protanopia-deuteranopia.json5`,
107+
`src/tokens/functional/color/light/primitives-light.json5`,
108+
`src/tokens/functional/color/light/patterns-light.json5`,
109+
],
110+
mode: 'light colorblind',
111+
},
112+
{
113+
name: 'light-tritanopia',
114+
source: [`src/tokens/functional/shadow/light.json5`],
115+
include: [
116+
`src/tokens/base/color/light/light.json5`,
117+
`src/tokens/base/color/light/light.tritanopia.json5`,
118+
`src/tokens/functional/color/light/primitives-light.json5`,
119+
`src/tokens/functional/color/light/patterns-light.json5`,
120+
],
121+
mode: 'light tritanopia',
122+
},
123+
{
124+
name: 'dark',
125+
source: [`src/tokens/functional/shadow/dark.json5`],
126+
include: [
127+
`src/tokens/base/color/dark/dark.json5`,
128+
`src/tokens/functional/color/dark/primitives-dark.json5`,
129+
`src/tokens/functional/color/dark/patterns-dark.json5`,
130+
],
131+
mode: 'dark',
132+
},
133+
{
134+
name: 'dark-high-contrast',
135+
source: [`src/tokens/functional/shadow/dark.json5`],
136+
include: [
137+
`src/tokens/base/color/dark/dark.json5`,
138+
`src/tokens/base/color/dark/dark.high-contrast.json5`,
139+
`src/tokens/functional/color/dark/primitives-dark.json5`,
140+
`src/tokens/functional/color/dark/patterns-dark.json5`,
141+
],
142+
mode: 'dark high contrast',
143+
},
144+
{
145+
name: 'dark-dimmed',
146+
source: [`src/tokens/functional/shadow/dark.json5`],
147+
include: [
148+
`src/tokens/base/color/dark/dark.json5`,
149+
`src/tokens/base/color/dark/dark.dimmed.json5`,
150+
`src/tokens/functional/color/dark/primitives-dark.json5`,
151+
`src/tokens/functional/color/dark/patterns-dark.json5`,
152+
],
153+
mode: 'dark dimmed',
154+
},
155+
{
156+
name: 'dark-colorblind',
157+
source: [`src/tokens/functional/shadow/dark.json5`],
158+
include: [
159+
`src/tokens/base/color/dark/dark.json5`,
160+
`src/tokens/base/color/dark/dark.protanopia-deuteranopia.json5`,
161+
`src/tokens/functional/color/dark/primitives-dark.json5`,
162+
`src/tokens/functional/color/dark/patterns-dark.json5`,
163+
],
164+
mode: 'dark colorblind',
165+
},
166+
{
167+
name: 'dark-tritanopia',
168+
source: [`src/tokens/functional/shadow/dark.json5`],
169+
include: [
170+
`src/tokens/base/color/dark/dark.json5`,
171+
`src/tokens/base/color/dark/dark.tritanopia.json5`,
172+
`src/tokens/functional/color/dark/primitives-dark.json5`,
173+
`src/tokens/functional/color/dark/patterns-dark.json5`,
174+
],
175+
mode: 'dark tritanopia',
176+
},
177+
]
178+
//
179+
for (const {name, source, include, mode} of shadowFiles) {
180+
PrimerStyleDictionary.extend({
181+
source,
182+
include,
183+
platforms: {
184+
figma: figma(`figma/shadows/${name}.json`, buildOptions.prefix, buildOptions.buildPath, {mode}),
185+
},
186+
}).buildAllPlatforms()
187+
}
76188
/** -----------------------------------
77189
* Create list of files
78190
* ----------------------------------- */

src/formats/jsonFigma.ts

Lines changed: 87 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@ import StyleDictionary from 'style-dictionary'
22
import {format} from 'prettier'
33
import type {FormatterArguments} from 'style-dictionary/types/Format'
44
import {transformNamePathToFigma} from '../transformers/namePathToFigma'
5+
import type {ShadowTokenValue} from '../types/ShadowTokenValue'
6+
import {hexToRgbaFloat} from '../transformers/utilities/hexToRgbaFloat'
7+
import type {RgbaFloat} from '../transformers/utilities/isRgbaFloat'
8+
import {isRgbaFloat} from '../transformers/utilities/isRgbaFloat'
59
const {sortByReference} = StyleDictionary.formatHelpers
610

711
const isReference = (string: string): boolean => /^\{([^\\]*)\}$/g.test(string)
@@ -28,33 +32,98 @@ const getFigmaType = (type: string): string => {
2832
throw new Error(`Invalid type: ${type}`)
2933
}
3034

35+
const shadowToVariables = (
36+
name: string,
37+
values: Omit<ShadowTokenValue, 'color'> & {color: string | RgbaFloat},
38+
token: StyleDictionary.TransformedToken,
39+
) => {
40+
// floatValue
41+
const floatValue = (property: 'offsetX' | 'offsetY' | 'blur' | 'spread') => ({
42+
name: `${name}/${property}`,
43+
value: parseInt(values[property].replace('px', '')),
44+
type: 'FLOAT',
45+
scopes: ['EFFECT_FLOAT'],
46+
mode,
47+
collection,
48+
group,
49+
})
50+
51+
const {attributes} = token
52+
const {mode, collection, group} = attributes || {}
53+
54+
return [
55+
floatValue('offsetX'),
56+
floatValue('offsetY'),
57+
floatValue('blur'),
58+
floatValue('spread'),
59+
{
60+
name: `${name}/color`,
61+
value: isRgbaFloat(values.color)
62+
? {...values.color, ...(values.alpha ? {a: values.alpha} : {})}
63+
: hexToRgbaFloat(values.color, values.alpha),
64+
type: 'COLOR',
65+
scopes: ['EFFECT_COLOR'],
66+
mode,
67+
collection,
68+
group,
69+
},
70+
]
71+
}
72+
3173
/**
3274
* @description Converts `StyleDictionary.dictionary.tokens` to javascript esm (javascript export statement)
3375
* @param arguments [FormatterArguments](https://github.com/amzn/style-dictionary/blob/main/types/Format.d.ts)
3476
* @returns formatted `string`
3577
*/
3678
export const jsonFigma: StyleDictionary.Formatter = ({dictionary, file: _file, platform}: FormatterArguments) => {
37-
// sort tokens by reference
38-
const tokens = dictionary.allTokens.sort(sortByReference(dictionary)).map(token => {
79+
// array to store tokens in
80+
const tokens: Record<string, unknown>[] = []
81+
// loop through tokens sorted by reference
82+
for (const token of dictionary.allTokens.sort(sortByReference(dictionary))) {
83+
// deconstruct token
3984
const {attributes, value, $type, comment: description, original, alpha, mix} = token
4085
const {mode, collection, scopes, group, codeSyntax} = attributes || {}
41-
42-
return {
43-
name: token.name,
44-
value,
45-
type: getFigmaType($type),
46-
alpha,
47-
isMix: mix ? true : undefined,
48-
description,
49-
refId: [collection, token.name].filter(Boolean).join('/'),
50-
reference: getReference(dictionary, original.value, platform),
51-
collection,
52-
mode,
53-
group,
54-
scopes,
55-
codeSyntax,
86+
// shadows need to be specifically dealt with
87+
if ($type === 'shadow') {
88+
const shadowValues = !Array.isArray(value) ? [value] : value
89+
// if only one set of shadow values is present
90+
if (shadowValues.length === 1) {
91+
tokens.push(
92+
...shadowToVariables(token.name, shadowValues[0], {
93+
...token,
94+
...(platform.mode ? {mode: platform.mode} : {}),
95+
}),
96+
)
97+
} else {
98+
// if multiple shadow sets values are present we need loop through them and add the index to the name
99+
for (const [index, stepValue] of shadowValues.entries()) {
100+
tokens.push(
101+
...shadowToVariables(`${token.name}/${index + 1}`, stepValue, {
102+
...token,
103+
...(platform.mode ? {mode: platform.mode} : {}),
104+
}),
105+
)
106+
}
107+
}
108+
// other tokens just get added to tokens array
109+
} else {
110+
tokens.push({
111+
name: token.name,
112+
value,
113+
type: getFigmaType($type),
114+
alpha,
115+
isMix: mix ? true : undefined,
116+
description,
117+
refId: [collection, token.name].filter(Boolean).join('/'),
118+
reference: getReference(dictionary, original.value, platform),
119+
collection,
120+
mode,
121+
group,
122+
scopes,
123+
codeSyntax,
124+
})
56125
}
57-
})
126+
}
58127
// add file header and convert output
59128
const output = JSON.stringify(tokens, null, 2)
60129
// return prettified

src/platforms/figma.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import type {PlatformInitializer} from '~/src/types/PlatformInitializer'
33
import {isSource} from '~/src/filters'
44

55
const validFigmaToken = (token: StyleDictionary.TransformedToken) => {
6-
const validTypes = ['color', 'dimension']
6+
const validTypes = ['color', 'dimension', 'shadow']
77
// is a siource token, not an included one
88
if (!isSource(token)) return false
99
// has a collection attribute
@@ -41,6 +41,7 @@ export const figma: PlatformInitializer = (outputFile, prefix, buildPath, option
4141
format: `json/figma`,
4242
options: {
4343
outputReferences: true,
44+
mode: options?.mode,
4445
},
4546
},
4647
],

src/schemas/shadowToken.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {colorHexValue} from './colorHexValue'
55
import {alphaValue} from './alphaValue'
66
import {dimensionValue} from './dimensionValue'
77
import {tokenType} from './tokenType'
8+
import {collection, mode} from './collections'
89

910
export const shadowValue = z
1011
.object({
@@ -22,5 +23,23 @@ export const shadowToken = baseToken
2223
.extend({
2324
$value: z.union([shadowValue, z.array(shadowValue), referenceValue]),
2425
$type: tokenType('shadow'),
26+
$extensions: z
27+
.object({
28+
'org.primer.figma': z.object({
29+
collection: collection(['mode']).optional(),
30+
mode: mode([
31+
'light',
32+
'dark',
33+
'dark dimmed',
34+
'light high contrast',
35+
'dark high contrast',
36+
'light colorblind',
37+
'dark colorblind',
38+
'light tritanopia',
39+
'dark tritanopia',
40+
]).optional(),
41+
}),
42+
})
43+
.optional(),
2544
})
2645
.strict()

0 commit comments

Comments
 (0)