diff --git a/.changeset/poor-kids-trade.md b/.changeset/poor-kids-trade.md new file mode 100644 index 000000000..6c6cbee9f --- /dev/null +++ b/.changeset/poor-kids-trade.md @@ -0,0 +1,5 @@ +--- +'@primer/primitives': minor +--- + +use cssColorMix instead of converting variables to hex when we use transparency diff --git a/scripts/buildTokens.ts b/scripts/buildTokens.ts index bf2b4a2c2..8440ef170 100644 --- a/scripts/buildTokens.ts +++ b/scripts/buildTokens.ts @@ -30,7 +30,7 @@ const getStyleDictionaryConfig: StyleDictionaryConfigGenerator = ( include, log: { warnings: 'disabled', // 'warn' | 'error' | 'disabled' - verbosity: 'verbose', // 'default' | 'silent' | 'verbose' + verbosity: 'silent', // 'default' | 'silent' | 'verbose' errors: { brokenReferences: 'throw', // 'throw' | 'console' }, diff --git a/src/primerStyleDictionary.ts b/src/primerStyleDictionary.ts index 439b3f939..ea72e610d 100644 --- a/src/primerStyleDictionary.ts +++ b/src/primerStyleDictionary.ts @@ -37,6 +37,7 @@ import { jsonFigma, } from './formats/index.js' import {themeOverrides} from './preprocessors/themeOverrides.js' +import {colorAlphaToCss} from './transformers/colorAlphaToCss.js' /** * @name {@link PrimerStyleDictionary} @@ -104,6 +105,8 @@ PrimerStyleDictionary.registerFormat({ * Transformers * */ +PrimerStyleDictionary.registerTransform(colorAlphaToCss) + PrimerStyleDictionary.registerTransform(colorToRgbAlpha) PrimerStyleDictionary.registerTransform(colorToRgbaFloat) diff --git a/src/transformers/colorAlphaToCss.test.ts b/src/transformers/colorAlphaToCss.test.ts new file mode 100644 index 000000000..c3102936e --- /dev/null +++ b/src/transformers/colorAlphaToCss.test.ts @@ -0,0 +1,49 @@ +import {getMockToken} from '../test-utilities/index.js' +import {colorAlphaToCss} from './colorAlphaToCss.js' + +describe('Transformer: colorAlphaToCss', () => { + it('transforms hex3, hex6, hex8 `color` tokens without alpha value', () => { + const input = [ + getMockToken({$value: '#123'}), + getMockToken({$value: '#343434'}), + getMockToken({$value: '#34343455'}), + ] + const expectedOutput = ['#123', '#343434', '#34343455'] + expect(input.map(item => colorAlphaToCss.transform(item, {}, {}))).toStrictEqual(expectedOutput) + }) + + it('transforms hex3, hex6, hex8 `color` tokens with alpha value', () => { + const input = [ + getMockToken({$value: '#123', alpha: 0.25}), + getMockToken({$value: '#343434', alpha: 0.6}), + getMockToken({$value: '#34343466', alpha: 0.1}), + ] + const expectedOutput = [ + 'color-mix(in srgb, #123, transparent 75%)', + 'color-mix(in srgb, #343434, transparent 40%)', + 'color-mix(in srgb, #34343466, transparent 90%)', + ] + expect(input.map(item => colorAlphaToCss.transform(item, {}, {}))).toStrictEqual(expectedOutput) + }) + + it('transforms references with and without alpha value', () => { + const input = [ + getMockToken({$value: '{base.color.green.5}'}), + getMockToken({$value: '{base.color.red.5}', alpha: 0.25}), + ] + const expectedOutput = ['{base.color.green.5}', 'color-mix(in srgb, {base.color.red.5}, transparent 75%)'] + expect(input.map(item => colorAlphaToCss.transform(item as TransformedToken, {}, {}))).toStrictEqual(expectedOutput) + }) + + it('transforms color-mix with and without alpha value', () => { + const input = [ + getMockToken({$value: 'color-mix(in srgb, {base.color.red.5}, transparent 25%)'}), + getMockToken({$value: 'color-mix(in srgb, {base.color.red.5}, transparent 25%)', alpha: 0.35}), + ] + const expectedOutput = [ + 'color-mix(in srgb, {base.color.red.5}, transparent 25%)', + 'color-mix(in srgb, color-mix(in srgb, {base.color.red.5}, transparent 25%), transparent 65%)', + ] + expect(input.map(item => colorAlphaToCss.transform(item as TransformedToken, {}, {}))).toStrictEqual(expectedOutput) + }) +}) diff --git a/src/transformers/colorAlphaToCss.ts b/src/transformers/colorAlphaToCss.ts new file mode 100644 index 000000000..92f8b53c9 --- /dev/null +++ b/src/transformers/colorAlphaToCss.ts @@ -0,0 +1,26 @@ +import {Transform, TransformedToken} from 'style-dictionary/types' +import {isColorWithAlpha} from '../filters/isColorWithAlpha.js' +import {getTokenValue} from './utilities/getTokenValue.js' + +export const cssColorMix = (colorA: string, colorB: string, colorBPercent: number) => { + if (colorBPercent < 0 || colorBPercent > 1) { + throw new Error( + `Invalid argument for "cssColorMix", colorBPercent must be between 0 and 1, ${colorBPercent} provided.`, + ) + } + if (colorBPercent === 0) return colorA + if (colorBPercent === 1) return colorB + + return `color-mix(in srgb, ${colorA}, ${colorB} ${colorBPercent * 100}%)` +} + +export const colorAlphaToCss: Transform = { + name: 'colorAlpha/css', + type: 'value', + transitive: true, + filter: isColorWithAlpha, + transform: (token: TransformedToken) => { + if (!token.alpha || token.alpha === null) return getTokenValue(token) + return cssColorMix(getTokenValue(token), 'transparent', 1 - token.alpha) + }, +} diff --git a/src/transformers/colorToHex.ts b/src/transformers/colorToHex.ts index 99042d39c..d276fcaf9 100644 --- a/src/transformers/colorToHex.ts +++ b/src/transformers/colorToHex.ts @@ -15,7 +15,7 @@ export const colorToHex: Transform = { transitive: true, filter: isColor, transform: (token: TransformedToken) => { - const alphaValue = token.alpha ?? token.$extensions?.alpha + const alphaValue = token.alpha if (alphaValue === null || alphaValue === undefined) { return toHex(getTokenValue(token)) }