Skip to content

Commit

Permalink
Style dictionary shadow build (#370)
Browse files Browse the repository at this point in the history
* shadows with with referenced color to support color modes

Co-authored-by: Rez <[email protected]>
  • Loading branch information
lukasoppermann and rezrah authored Nov 14, 2022
1 parent f631e4c commit e619ca9
Show file tree
Hide file tree
Showing 20 changed files with 1,002 additions and 28 deletions.
4 changes: 2 additions & 2 deletions build.js
Original file line number Diff line number Diff line change
Expand Up @@ -488,7 +488,7 @@ function _init() {
const outputPath = 'tokens-v2-private'
//build all tokens
buildPrimitives({
source: [`tokens/**/*.json`, `!tokens/**/size-*.json`, `!tokens/**/color/*.json`],
source: [`tokens/**/*.json`, `!tokens/**/size-*.json`, `!tokens/**/color/*.json`, `!tokens/**/shadow/*.json`],
outputPath
})

Expand Down Expand Up @@ -547,7 +547,7 @@ function _init() {

//build docs data
buildPrimitives({
source: [`tokens/**/*.json`, `!tokens/**/color/*.json`],
source: [`tokens/**/*.json`, `!tokens/**/color/*.json`, `!tokens/**/shadow/*.json`],
outputPath,
platforms: {
docs: {
Expand Down
14 changes: 13 additions & 1 deletion config/PrimerStyleDictionary.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
import StyleDictionary from 'style-dictionary'
import {w3cJsonParser} from './parsers'
import {colorToHexAlpha, colorToRgbAlpha, colorToHex, jsonDeprecated, namePathToDotNotation} from './transformers'
import {
colorToHexAlpha,
colorToRgbAlpha,
colorToHex,
jsonDeprecated,
shadowToCss,
namePathToDotNotation
} from './transformers'
import {
scssMixinCssVariables,
javascriptCommonJs,
Expand Down Expand Up @@ -73,6 +80,11 @@ StyleDictionary.registerTransform({
...namePathToDotNotation
})

StyleDictionary.registerTransform({
name: 'shadow/css',
...shadowToCss
})

/**
* @name {@link PrimerStyleDictionary}
* @description Returns style dictionary object with primer preset that includes parsers, formats and transformers
Expand Down
18 changes: 18 additions & 0 deletions config/filters/isShadow.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import {getMockToken} from '~/src/test-utilities'
import {isShadow} from './isShadow'

describe('Filter: isShadow', () => {
it('returns true if $type property is `shadow`', () => {
expect(isShadow(getMockToken({$type: 'shadow'}))).toStrictEqual(true)
})

it('returns false if $type property is not `shadow`', () => {
expect(isShadow(getMockToken({$type: 'pumpkin'}))).toStrictEqual(false)
})

it('returns false if $type property is falsy', () => {
expect(isShadow(getMockToken({$type: false}))).toStrictEqual(false)
expect(isShadow(getMockToken({$type: undefined}))).toStrictEqual(false)
expect(isShadow(getMockToken({$type: null}))).toStrictEqual(false)
})
})
10 changes: 10 additions & 0 deletions config/filters/isShadow.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import StyleDictionary from 'style-dictionary'

/**
* @description Checks if token is of $type `shadow`
* @param arguments [StyleDictionary.TransformedToken](https://github.com/amzn/style-dictionary/blob/main/types/TransformedToken.d.ts)
* @returns boolean
*/
export const isShadow = (token: StyleDictionary.TransformedToken): boolean => {
return token.$type === 'shadow'
}
2 changes: 1 addition & 1 deletion config/platforms/css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {isSource} from '~/config/filters'
export const css: PlatformInitializer = (outputFile, prefix, buildPath, _options): StyleDictionary.Platform => ({
prefix,
buildPath,
transforms: ['name/cti/kebab', 'color/hex', 'color/hexAlpha'],
transforms: ['name/cti/kebab', 'color/hex', 'color/hexAlpha', 'shadow/css'],
options: {
basePxFontSize: 16
},
Expand Down
2 changes: 1 addition & 1 deletion config/platforms/docJson.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {isSource} from '~/config/filters'
export const docJson: PlatformInitializer = (outputFile, prefix, buildPath): StyleDictionary.Platform => ({
prefix,
buildPath,
transforms: ['color/hex', 'color/hexAlpha'],
transforms: ['color/hex', 'color/hexAlpha', 'shadow/css'],
options: {
basePxFontSize: 16
},
Expand Down
2 changes: 1 addition & 1 deletion config/platforms/javascript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {isSource} from '~/config/filters'
export const javascript: PlatformInitializer = (outputFile, prefix, buildPath): StyleDictionary.Platform => ({
prefix,
buildPath,
transforms: ['name/cti/camel', 'color/hex', 'color/rgbAlpha'],
transforms: ['name/cti/camel', 'color/hex', 'color/rgbAlpha', 'shadow/css'],
options: {
basePxFontSize: 16
},
Expand Down
2 changes: 1 addition & 1 deletion config/platforms/json.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {isSource} from '~/config/filters'
export const json: PlatformInitializer = (outputFile, prefix, buildPath): StyleDictionary.Platform => ({
prefix,
buildPath,
transforms: ['color/hex', 'color/hexAlpha'],
transforms: ['color/hex', 'color/hexAlpha', 'shadow/css'],
options: {
basePxFontSize: 16
},
Expand Down
2 changes: 1 addition & 1 deletion config/platforms/scss.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export const scss: PlatformInitializer = (outputFile, prefix, buildPath): StyleD
return {
prefix,
buildPath,
transforms: ['name/cti/kebab', 'color/hex', 'color/hexAlpha'],
transforms: ['name/cti/kebab', 'color/hex', 'color/hexAlpha', 'shadow/css'],
options: {
basePxFontSize: 16
},
Expand Down
2 changes: 1 addition & 1 deletion config/platforms/typeDefinitions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {upperCaseFirstCharacter} from '~/config/utilities'
export const typeDefinitions: PlatformInitializer = (outputFile, prefix, buildPath): StyleDictionary.Platform => ({
prefix,
buildPath,
transforms: ['name/cti/camel', 'color/hex', 'color/hexAlpha'],
transforms: ['name/cti/camel', 'color/hex', 'color/hexAlpha', 'shadow/css'],
files: [
{
format: 'typescript/export-definition',
Expand Down
2 changes: 1 addition & 1 deletion config/platforms/typescript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {isSource} from '~/config/filters'
export const typescript: PlatformInitializer = (outputFile, prefix, buildPath): StyleDictionary.Platform => ({
prefix,
buildPath,
transforms: ['name/cti/camel', 'color/hex', 'color/hexAlpha'],
transforms: ['name/cti/camel', 'color/hex', 'color/hexAlpha', 'shadow/css'],
options: {
basePxFontSize: 16
},
Expand Down
1 change: 1 addition & 0 deletions config/transformers/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export {shadowToCss} from './shadowToCss'
export {colorToHex} from './colorToHex'
export {colorToHexAlpha} from './colorToHexAlpha'
export {colorToRgbAlpha} from './colorToRgbAlpha'
Expand Down
170 changes: 170 additions & 0 deletions config/transformers/shadowToCss.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
import {getMockToken} from '~/src/test-utilities'
import {shadowToCss} from './shadowToCss'

describe('Transformer: shadowToCss', () => {
it('transforms `shadow` token to css shadow string', () => {
const input = [
getMockToken({
value: {
color: '#000000',
offsetX: '0px',
offsetY: '2px',
blur: '1px',
spread: '0'
}
})
]
const expectedOutput = ['0px 2px 1px 0 #000000']
expect(input.map(item => shadowToCss.transformer(item))).toStrictEqual(expectedOutput)
})

it('transforms inset `shadow` token to css shadow string', () => {
const input = [
getMockToken({
value: {
color: '#000000',
offsetX: '0px',
offsetY: '2px',
blur: '1px',
spread: '0px',
inset: true
}
}),
getMockToken({
value: {
color: '#000000',
offsetX: '0px',
offsetY: '2px',
blur: '1px',
spread: '0px',
inset: false
}
})
]
const expectedOutput = ['inset 0px 2px 1px 0px #000000', '0px 2px 1px 0px #000000']
expect(input.map(item => shadowToCss.transformer(item))).toStrictEqual(expectedOutput)
})

it('throws an error when required values are missing', () => {
// missing blur
expect(() =>
shadowToCss.transformer(
getMockToken({
value: {
color: '#000000',
offsetX: '2px',
offsetY: '2px',
blur: '1px'
}
})
)
).toThrowError()

// missing spread
expect(() =>
shadowToCss.transformer(
getMockToken({
value: {
color: '#000000',
offsetX: '2px',
offsetY: '2px',
blur: '1px'
}
})
)
).toThrowError()

// missing offsets
expect(() =>
shadowToCss.transformer(
getMockToken({
value: {
color: '#000000',
offsetX: '2px',
spread: '0px',
blur: '1px'
}
})
)
).toThrowError()

expect(() =>
shadowToCss.transformer(
getMockToken({
value: {
color: '#000000',
offsetY: '2px',
spread: '0px',
blur: '1px'
}
})
)
).toThrowError()
// missing color
expect(() =>
shadowToCss.transformer(
getMockToken({
value: {
offsetX: '0px',
offsetY: '2px',
spread: '0px',
blur: '1px'
}
})
)
).toThrowError()
})

it('transforms `shadow` token alpha value to css shadow string', () => {
const input = [
getMockToken({
value: {
color: '#000000',
offsetX: '0px',
offsetY: '2px',
blur: '1px',
spread: '0',
alpha: 0.5
}
}),
getMockToken({
value: {
color: '#22222266',
offsetX: '0px',
offsetY: '2px',
blur: '1px',
spread: '0',
alpha: 0.5
}
})
]
const expectedOutput = ['0px 2px 1px 0 #00000080', '0px 2px 1px 0 #22222280']
expect(input.map(item => shadowToCss.transformer(item))).toStrictEqual(expectedOutput)
})

it('transforms multi-layer `shadow` token to css shadow string', () => {
const input = getMockToken({
value: [
{
color: '#000000',
offsetX: '0px',
offsetY: '2px',
blur: '1px',
spread: '0',
alpha: 0.5
},
{
color: '#22222266',
offsetX: '0px',
offsetY: '8px',
blur: '16px',
spread: '0',
alpha: 0.2
}
]
})

const expectedOutput = '0px 2px 1px 0 #00000080, 0px 8px 16px 0 #22222233'
expect(shadowToCss.transformer(input)).toStrictEqual(expectedOutput)
})
})
46 changes: 46 additions & 0 deletions config/transformers/shadowToCss.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import {toHex} from 'color2k'
import StyleDictionary from 'style-dictionary'
import {ShadowTokenValue} from '../../types/ShadowTokenValue'
import {isShadow} from '../filters/isShadow'
import {alpha} from '../utilities'

/**
* checks if all required properties exist on shadow token
* @param object - ShadowTokenValue
* @returns void or throws error
*/
const checkForShadowTokenProperties = (shadow: ShadowTokenValue) => {
const requiredShadowProperties = ['color', 'offsetX', 'offsetY', 'blur', 'spread']
for (const prop of requiredShadowProperties) {
if (prop in shadow === false) {
throw new Error(`Missing propery: ${prop} on shadow token ${JSON.stringify(shadow)}`)
}
}
}
/**
* @description converts w3c shadow tokens in css shadow string
* @type value transformer — [StyleDictionary.ValueTransform](https://github.com/amzn/style-dictionary/blob/main/types/Transform.d.ts)
* @matcher matches all tokens of $type `shadow`
* @transformer returns css shadow `string`
*/
export const shadowToCss: StyleDictionary.Transform = {
type: `value`,
transitive: true,
matcher: isShadow,
transformer: ({value}: {value: ShadowTokenValue | ShadowTokenValue[]}) => {
// turn value into array
const shadowValues = !Array.isArray(value) ? [value] : value
return shadowValues
.map((shadow: ShadowTokenValue) => {
// if value === string it was probably already transformed
if (typeof shadow === 'string') return shadow

checkForShadowTokenProperties(shadow)
/*css box shadow: inset? | offset-x | offset-y | blur-radius | spread-radius | color */
return `${shadow.inset === true ? 'inset ' : ''}${shadow.offsetX} ${shadow.offsetY} ${shadow.blur} ${
shadow.spread
} ${toHex(alpha(shadow.color, shadow.alpha || 1))}`
})
.join(', ')
}
}
5 changes: 5 additions & 0 deletions config/types/Shadow.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/**
* @description a css shadow
* @format inset? | offset-x | offset-y | blur-radius? | spread-radius? | color?
*/
type Shadow = string
Loading

0 comments on commit e619ca9

Please sign in to comment.