Skip to content

Commit da8a1d9

Browse files
CSS Color Mix (#1109)
* Add color/Alpha transformer * use cssColorMix in shadows * add changeset * dont apply new transformer
1 parent 02776ce commit da8a1d9

File tree

6 files changed

+85
-2
lines changed

6 files changed

+85
-2
lines changed

.changeset/poor-kids-trade.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@primer/primitives': minor
3+
---
4+
5+
use cssColorMix instead of converting variables to hex when we use transparency

scripts/buildTokens.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ const getStyleDictionaryConfig: StyleDictionaryConfigGenerator = (
3030
include,
3131
log: {
3232
warnings: 'disabled', // 'warn' | 'error' | 'disabled'
33-
verbosity: 'verbose', // 'default' | 'silent' | 'verbose'
33+
verbosity: 'silent', // 'default' | 'silent' | 'verbose'
3434
errors: {
3535
brokenReferences: 'throw', // 'throw' | 'console'
3636
},

src/primerStyleDictionary.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import {
3737
jsonFigma,
3838
} from './formats/index.js'
3939
import {themeOverrides} from './preprocessors/themeOverrides.js'
40+
import {colorAlphaToCss} from './transformers/colorAlphaToCss.js'
4041

4142
/**
4243
* @name {@link PrimerStyleDictionary}
@@ -104,6 +105,8 @@ PrimerStyleDictionary.registerFormat({
104105
* Transformers
105106
*
106107
*/
108+
PrimerStyleDictionary.registerTransform(colorAlphaToCss)
109+
107110
PrimerStyleDictionary.registerTransform(colorToRgbAlpha)
108111

109112
PrimerStyleDictionary.registerTransform(colorToRgbaFloat)
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import {getMockToken} from '../test-utilities/index.js'
2+
import {colorAlphaToCss} from './colorAlphaToCss.js'
3+
4+
describe('Transformer: colorAlphaToCss', () => {
5+
it('transforms hex3, hex6, hex8 `color` tokens without alpha value', () => {
6+
const input = [
7+
getMockToken({$value: '#123'}),
8+
getMockToken({$value: '#343434'}),
9+
getMockToken({$value: '#34343455'}),
10+
]
11+
const expectedOutput = ['#123', '#343434', '#34343455']
12+
expect(input.map(item => colorAlphaToCss.transform(item, {}, {}))).toStrictEqual(expectedOutput)
13+
})
14+
15+
it('transforms hex3, hex6, hex8 `color` tokens with alpha value', () => {
16+
const input = [
17+
getMockToken({$value: '#123', alpha: 0.25}),
18+
getMockToken({$value: '#343434', alpha: 0.6}),
19+
getMockToken({$value: '#34343466', alpha: 0.1}),
20+
]
21+
const expectedOutput = [
22+
'color-mix(in srgb, #123, transparent 75%)',
23+
'color-mix(in srgb, #343434, transparent 40%)',
24+
'color-mix(in srgb, #34343466, transparent 90%)',
25+
]
26+
expect(input.map(item => colorAlphaToCss.transform(item, {}, {}))).toStrictEqual(expectedOutput)
27+
})
28+
29+
it('transforms references with and without alpha value', () => {
30+
const input = [
31+
getMockToken({$value: '{base.color.green.5}'}),
32+
getMockToken({$value: '{base.color.red.5}', alpha: 0.25}),
33+
]
34+
const expectedOutput = ['{base.color.green.5}', 'color-mix(in srgb, {base.color.red.5}, transparent 75%)']
35+
expect(input.map(item => colorAlphaToCss.transform(item as TransformedToken, {}, {}))).toStrictEqual(expectedOutput)
36+
})
37+
38+
it('transforms color-mix with and without alpha value', () => {
39+
const input = [
40+
getMockToken({$value: 'color-mix(in srgb, {base.color.red.5}, transparent 25%)'}),
41+
getMockToken({$value: 'color-mix(in srgb, {base.color.red.5}, transparent 25%)', alpha: 0.35}),
42+
]
43+
const expectedOutput = [
44+
'color-mix(in srgb, {base.color.red.5}, transparent 25%)',
45+
'color-mix(in srgb, color-mix(in srgb, {base.color.red.5}, transparent 25%), transparent 65%)',
46+
]
47+
expect(input.map(item => colorAlphaToCss.transform(item as TransformedToken, {}, {}))).toStrictEqual(expectedOutput)
48+
})
49+
})

src/transformers/colorAlphaToCss.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import {Transform, TransformedToken} from 'style-dictionary/types'
2+
import {isColorWithAlpha} from '../filters/isColorWithAlpha.js'
3+
import {getTokenValue} from './utilities/getTokenValue.js'
4+
5+
export const cssColorMix = (colorA: string, colorB: string, colorBPercent: number) => {
6+
if (colorBPercent < 0 || colorBPercent > 1) {
7+
throw new Error(
8+
`Invalid argument for "cssColorMix", colorBPercent must be between 0 and 1, ${colorBPercent} provided.`,
9+
)
10+
}
11+
if (colorBPercent === 0) return colorA
12+
if (colorBPercent === 1) return colorB
13+
14+
return `color-mix(in srgb, ${colorA}, ${colorB} ${colorBPercent * 100}%)`
15+
}
16+
17+
export const colorAlphaToCss: Transform = {
18+
name: 'colorAlpha/css',
19+
type: 'value',
20+
transitive: true,
21+
filter: isColorWithAlpha,
22+
transform: (token: TransformedToken) => {
23+
if (!token.alpha || token.alpha === null) return getTokenValue(token)
24+
return cssColorMix(getTokenValue(token), 'transparent', 1 - token.alpha)
25+
},
26+
}

src/transformers/colorToHex.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export const colorToHex: Transform = {
1515
transitive: true,
1616
filter: isColor,
1717
transform: (token: TransformedToken) => {
18-
const alphaValue = token.alpha ?? token.$extensions?.alpha
18+
const alphaValue = token.alpha
1919
if (alphaValue === null || alphaValue === undefined) {
2020
return toHex(getTokenValue(token))
2121
}

0 commit comments

Comments
 (0)