Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/ten-pots-applaud.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@primer/primitives': patch
---

Adding shadow tokens for figma
112 changes: 112 additions & 0 deletions scripts/buildPlatforms/buildFigma.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,118 @@ export const buildFigma = (buildOptions: ConfigGeneratorOptions): void => {
},
}).buildAllPlatforms()

/** -----------------------------------
* Shadow tokens
* ----------------------------------- */
const shadowFiles = [
{
name: 'light',
source: [`src/tokens/functional/shadow/light.json5`],
include: [
`src/tokens/base/color/light/light.json5`,
`src/tokens/functional/color/light/primitives-light.json5`,
`src/tokens/functional/color/light/patterns-light.json5`,
],
mode: 'light',
},
{
name: 'light-high-contrast',
source: [`src/tokens/functional/shadow/light.json5`],
include: [
`src/tokens/base/color/light/light.json5`,
`src/tokens/base/color/light/light.high-contrast.json5`,
`src/tokens/functional/color/light/primitives-light.json5`,
`src/tokens/functional/color/light/patterns-light.json5`,
],
mode: 'light high contrast',
},
{
name: 'light-colorblind',
source: [`src/tokens/functional/shadow/light.json5`],
include: [
`src/tokens/base/color/light/light.json5`,
`src/tokens/base/color/light/light.protanopia-deuteranopia.json5`,
`src/tokens/functional/color/light/primitives-light.json5`,
`src/tokens/functional/color/light/patterns-light.json5`,
],
mode: 'light colorblind',
},
{
name: 'light-tritanopia',
source: [`src/tokens/functional/shadow/light.json5`],
include: [
`src/tokens/base/color/light/light.json5`,
`src/tokens/base/color/light/light.tritanopia.json5`,
`src/tokens/functional/color/light/primitives-light.json5`,
`src/tokens/functional/color/light/patterns-light.json5`,
],
mode: 'light tritanopia',
},
{
name: 'dark',
source: [`src/tokens/functional/shadow/dark.json5`],
include: [
`src/tokens/base/color/dark/dark.json5`,
`src/tokens/functional/color/dark/primitives-dark.json5`,
`src/tokens/functional/color/dark/patterns-dark.json5`,
],
mode: 'dark',
},
{
name: 'dark-high-contrast',
source: [`src/tokens/functional/shadow/dark.json5`],
include: [
`src/tokens/base/color/dark/dark.json5`,
`src/tokens/base/color/dark/dark.high-contrast.json5`,
`src/tokens/functional/color/dark/primitives-dark.json5`,
`src/tokens/functional/color/dark/patterns-dark.json5`,
],
mode: 'dark high contrast',
},
{
name: 'dark-dimmed',
source: [`src/tokens/functional/shadow/dark.json5`],
include: [
`src/tokens/base/color/dark/dark.json5`,
`src/tokens/base/color/dark/dark.dimmed.json5`,
`src/tokens/functional/color/dark/primitives-dark.json5`,
`src/tokens/functional/color/dark/patterns-dark.json5`,
],
mode: 'dark dimmed',
},
{
name: 'dark-colorblind',
source: [`src/tokens/functional/shadow/dark.json5`],
include: [
`src/tokens/base/color/dark/dark.json5`,
`src/tokens/base/color/dark/dark.protanopia-deuteranopia.json5`,
`src/tokens/functional/color/dark/primitives-dark.json5`,
`src/tokens/functional/color/dark/patterns-dark.json5`,
],
mode: 'dark colorblind',
},
{
name: 'dark-tritanopia',
source: [`src/tokens/functional/shadow/dark.json5`],
include: [
`src/tokens/base/color/dark/dark.json5`,
`src/tokens/base/color/dark/dark.tritanopia.json5`,
`src/tokens/functional/color/dark/primitives-dark.json5`,
`src/tokens/functional/color/dark/patterns-dark.json5`,
],
mode: 'dark tritanopia',
},
]
//
for (const {name, source, include, mode} of shadowFiles) {
PrimerStyleDictionary.extend({
source,
include,
platforms: {
figma: figma(`figma/shadows/${name}.json`, buildOptions.prefix, buildOptions.buildPath, {mode}),
},
}).buildAllPlatforms()
}
/** -----------------------------------
* Create list of files
* ----------------------------------- */
Expand Down
105 changes: 87 additions & 18 deletions src/formats/jsonFigma.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ import StyleDictionary from 'style-dictionary'
import {format} from 'prettier'
import type {FormatterArguments} from 'style-dictionary/types/Format'
import {transformNamePathToFigma} from '../transformers/namePathToFigma'
import type {ShadowTokenValue} from '../types/ShadowTokenValue'
import {hexToRgbaFloat} from '../transformers/utilities/hexToRgbaFloat'
import type {RgbaFloat} from '../transformers/utilities/isRgbaFloat'
import {isRgbaFloat} from '../transformers/utilities/isRgbaFloat'
const {sortByReference} = StyleDictionary.formatHelpers

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

const shadowToVariables = (
name: string,
values: Omit<ShadowTokenValue, 'color'> & {color: string | RgbaFloat},
token: StyleDictionary.TransformedToken,
) => {
// floatValue
const floatValue = (property: 'offsetX' | 'offsetY' | 'blur' | 'spread') => ({
name: `${name}/${property}`,
value: parseInt(values[property].replace('px', '')),
type: 'FLOAT',
scopes: ['EFFECT_FLOAT'],
mode,
collection,
group,
})

const {attributes} = token
const {mode, collection, group} = attributes || {}

return [
floatValue('offsetX'),
floatValue('offsetY'),
floatValue('blur'),
floatValue('spread'),
{
name: `${name}/color`,
value: isRgbaFloat(values.color)
? {...values.color, ...(values.alpha ? {a: values.alpha} : {})}
: hexToRgbaFloat(values.color, values.alpha),
type: 'COLOR',
scopes: ['EFFECT_COLOR'],
mode,
collection,
group,
},
]
}

/**
* @description Converts `StyleDictionary.dictionary.tokens` to javascript esm (javascript export statement)
* @param arguments [FormatterArguments](https://github.com/amzn/style-dictionary/blob/main/types/Format.d.ts)
* @returns formatted `string`
*/
export const jsonFigma: StyleDictionary.Formatter = ({dictionary, file: _file, platform}: FormatterArguments) => {
// sort tokens by reference
const tokens = dictionary.allTokens.sort(sortByReference(dictionary)).map(token => {
// array to store tokens in
const tokens: Record<string, unknown>[] = []
// loop through tokens sorted by reference
for (const token of dictionary.allTokens.sort(sortByReference(dictionary))) {
// deconstruct token
const {attributes, value, $type, comment: description, original, alpha, mix} = token
const {mode, collection, scopes, group, codeSyntax} = attributes || {}

return {
name: token.name,
value,
type: getFigmaType($type),
alpha,
isMix: mix ? true : undefined,
description,
refId: [collection, token.name].filter(Boolean).join('/'),
reference: getReference(dictionary, original.value, platform),
collection,
mode,
group,
scopes,
codeSyntax,
// shadows need to be specifically dealt with
if ($type === 'shadow') {
const shadowValues = !Array.isArray(value) ? [value] : value
// if only one set of shadow values is present
if (shadowValues.length === 1) {
tokens.push(
...shadowToVariables(token.name, shadowValues[0], {
...token,
...(platform.mode ? {mode: platform.mode} : {}),
}),
)
} else {
// if multiple shadow sets values are present we need loop through them and add the index to the name
for (const [index, stepValue] of shadowValues.entries()) {
tokens.push(
...shadowToVariables(`${token.name}/${index + 1}`, stepValue, {
...token,
...(platform.mode ? {mode: platform.mode} : {}),
}),
)
}
}
// other tokens just get added to tokens array
} else {
tokens.push({
name: token.name,
value,
type: getFigmaType($type),
alpha,
isMix: mix ? true : undefined,
description,
refId: [collection, token.name].filter(Boolean).join('/'),
reference: getReference(dictionary, original.value, platform),
collection,
mode,
group,
scopes,
codeSyntax,
})
}
})
}
// add file header and convert output
const output = JSON.stringify(tokens, null, 2)
// return prettified
Expand Down
3 changes: 2 additions & 1 deletion src/platforms/figma.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type {PlatformInitializer} from '~/src/types/PlatformInitializer'
import {isSource} from '~/src/filters'

const validFigmaToken = (token: StyleDictionary.TransformedToken) => {
const validTypes = ['color', 'dimension']
const validTypes = ['color', 'dimension', 'shadow']
// is a siource token, not an included one
if (!isSource(token)) return false
// has a collection attribute
Expand Down Expand Up @@ -41,6 +41,7 @@ export const figma: PlatformInitializer = (outputFile, prefix, buildPath, option
format: `json/figma`,
options: {
outputReferences: true,
mode: options?.mode,
},
},
],
Expand Down
19 changes: 19 additions & 0 deletions src/schemas/shadowToken.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {colorHexValue} from './colorHexValue'
import {alphaValue} from './alphaValue'
import {dimensionValue} from './dimensionValue'
import {tokenType} from './tokenType'
import {collection, mode} from './collections'

export const shadowValue = z
.object({
Expand All @@ -22,5 +23,23 @@ export const shadowToken = baseToken
.extend({
$value: z.union([shadowValue, z.array(shadowValue), referenceValue]),
$type: tokenType('shadow'),
$extensions: z
.object({
'org.primer.figma': z.object({
collection: collection(['mode']).optional(),
mode: mode([
'light',
'dark',
'dark dimmed',
'light high contrast',
'dark high contrast',
'light colorblind',
'dark colorblind',
'light tritanopia',
'dark tritanopia',
]).optional(),
}),
})
.optional(),
})
.strict()
Loading