@justeat/pie-design-tokens
Advanced tools
Comparing version 5.0.1 to 5.1.0
@@ -5,2 +5,3 @@ const { | ||
convertHexValueToRGB, | ||
convertHexValueToHSL, | ||
convertWordToPascalCase, | ||
@@ -24,3 +25,34 @@ getConvertedShadowsToBoxShadowValues, | ||
// Temporary additional CSS output for HSL values of color tokens | ||
const cssOutputHslStart = ':root {\n'; | ||
let cssOutputHsl = ''; | ||
const cssOutputHslEnd = '}'; | ||
/** | ||
* Builds up a String of HSL colour values (as CSS Custom properties) | ||
* so that they can be output as a separate CSS file. | ||
* | ||
* This function will be a temporary addition until native color conversion | ||
* is supported by browsers (see https://caniuse.com/?search=relative%20color) | ||
* | ||
* @param {string} value – Color value (e.g. `#ff1111`) or alias token reference | ||
* @param {string} varName – Token Variable Name to be used as part of the CSS Custom Property name | ||
*/ | ||
const createHslString = (value, varName) => { | ||
if (value.startsWith('#')) { | ||
// if hexcode, it's a global token - convert to hsl | ||
const { h, s, l } = convertHexValueToHSL(value); | ||
cssOutputHsl += ` ${getCssVarName(varName)}-h: ${h};\n ${getCssVarName(varName)}-s: ${s}%;\n ${getCssVarName(varName)}-l: ${l}%;\n`; | ||
} else if (!value.endsWith('%') && !value.startsWith('rgb')) { | ||
// this is an alias token, so point to the correct global token hsl fragments | ||
const valueArr = value.split(')'); | ||
const h = `${valueArr[0]}-h)`; | ||
const s = `${valueArr[0]}-s)`; | ||
const l = `${valueArr[0]}-l)`; | ||
cssOutputHsl += ` ${getCssVarName(varName)}-h: ${h};\n ${getCssVarName(varName)}-s: ${s};\n ${getCssVarName(varName)}-l: ${l};\n`; | ||
} | ||
}; | ||
/** | ||
* Returns a token value using the specified category and name properties passed in. | ||
@@ -85,2 +117,6 @@ * Also handles some special cases (such as rgb format with opacity for colour aliases). | ||
if (category === 'color') { | ||
createHslString(value, varName); | ||
} | ||
// shortens css variable name i.e. `font-size-12-font-size` becomes `font-size-12` | ||
@@ -206,2 +242,5 @@ const shortenedVar = varName.replace('-font-size', ''); | ||
writeToFile(themeKey, 'css', cssString); | ||
const hslOutput = cssOutputHslStart + cssOutputHsl + cssOutputHslEnd; | ||
writeToFile(`${themeKey}-hsl-colors`, 'css', hslOutput); | ||
}); | ||
@@ -208,0 +247,0 @@ return true; |
@@ -197,2 +197,71 @@ const fs = require('fs'); | ||
/** | ||
* Converts a hex color value to HSL color value. | ||
* | ||
* @param {string} hexcode - The hex color value to convert. E.G. #000 or #000000 | ||
* @throws {Error} Will throw an error if the provided color is not a valid hex color. | ||
* @returns {Object} An object with properties h, s, l representing the hue, saturation, and lightness. | ||
*/ | ||
const convertHexValueToHSL = hexcode => { | ||
if (typeof hexcode !== 'string' || !/^#[0-9A-F]{3}$|^#[0-9A-F]{6}$/i.test(hexcode)) { | ||
throw new Error('Invalid hex color'); | ||
} | ||
// Convert hex to RGB first | ||
let red = 0; | ||
let green = 0; | ||
let blue = 0; | ||
if (hexcode.length === 4) { | ||
red = `0x${hexcode[1]}${hexcode[1]}`; | ||
green = `0x${hexcode[2]}${hexcode[2]}`; | ||
blue = `0x${hexcode[3]}${hexcode[3]}`; | ||
} else if (hexcode.length === 7) { | ||
red = `0x${hexcode[1]}${hexcode[2]}`; | ||
green = `0x${hexcode[3]}${hexcode[4]}`; | ||
blue = `0x${hexcode[5]}${hexcode[6]}`; | ||
} | ||
// Normalize RGB values to the [0, 1] range | ||
red /= 255; | ||
green /= 255; | ||
blue /= 255; | ||
// Convert RGB to HSL | ||
const cmin = Math.min(red, green, blue); | ||
const cmax = Math.max(red, green, blue); | ||
const delta = cmax - cmin; | ||
let hue = 0; | ||
let saturation = 0; | ||
let lightness = 0; | ||
if (delta === 0) { | ||
hue = 0; | ||
} else if (cmax === red) { | ||
hue = ((green - blue) / delta) % 6; | ||
} else if (cmax === green) { | ||
hue = (blue - red) / delta + 2; | ||
} else { | ||
hue = (red - green) / delta + 4; | ||
} | ||
hue = Math.round(hue * 60); | ||
if (hue < 0) { | ||
hue += 360; | ||
} | ||
lightness = (cmax + cmin) / 2; | ||
saturation = delta === 0 ? 0 : delta / (1 - Math.abs(2 * lightness - 1)); | ||
saturation = +(saturation * 100).toFixed(1); | ||
lightness = +(lightness * 100).toFixed(1); | ||
return { | ||
h: hue, | ||
l: lightness, | ||
s: saturation | ||
}; | ||
}; | ||
// /** | ||
@@ -239,2 +308,3 @@ // * Deprecated – will be converted to support platform value replacement in later PR | ||
convertGlobalTokenValueToStylesValue, | ||
convertHexValueToHSL, | ||
convertHexValueToRGB, | ||
@@ -241,0 +311,0 @@ convertWordToPascalCase, |
@@ -60,3 +60,3 @@ const { writeToFile } = require('../helpers'); | ||
describe('with a single theme', () => { | ||
it('should write out a single file', () => { | ||
it('should write out two files - one for regular theme, one for HSL colors', () => { | ||
// Act | ||
@@ -66,3 +66,7 @@ const result = compileToCss(TEST_THEME_TOKENS); | ||
// Assert | ||
expect(writeToFile).toHaveBeenCalledTimes(1); | ||
const [[firstCall], [secondCall]] = writeToFile.mock.calls; | ||
expect(writeToFile).toHaveBeenCalledTimes(2); | ||
expect(firstCall).toBe('jet'); | ||
expect(secondCall).toBe('jet-hsl-colors'); | ||
expect(result).toBe(true); | ||
@@ -91,3 +95,3 @@ }); | ||
describe('with two themes', () => { | ||
it('should write to two files', () => { | ||
it('should write to four files - one for each theme and another for HSL colors for each theme', () => { | ||
// Act | ||
@@ -97,3 +101,9 @@ const result = compileToCss(TEST_TOKENS_WITH_TWO_THEMES); | ||
// Assert | ||
expect(writeToFile).toHaveBeenCalledTimes(2); | ||
const [[firstCall], [secondCall], [thirdCall], [fourthCall]] = writeToFile.mock.calls; | ||
expect(writeToFile).toHaveBeenCalledTimes(4); | ||
expect(firstCall).toBe('jet'); | ||
expect(secondCall).toBe('jet-hsl-colors'); | ||
expect(thirdCall).toBe('newjet'); | ||
expect(fourthCall).toBe('newjet-hsl-colors'); | ||
expect(result).toBe(true); | ||
@@ -100,0 +110,0 @@ }); |
@@ -7,2 +7,3 @@ const { | ||
convertHexValueToRGB, | ||
convertHexValueToHSL, | ||
variableGetterFactory | ||
@@ -344,1 +345,37 @@ } = require('../helpers'); | ||
}); | ||
describe('convertHexValueToHSL', () => { | ||
it('should convert #ffffff to { h: 0, s: 0, l: 100 }', () => { | ||
const result = convertHexValueToHSL('#ffffff'); | ||
expect(result).toEqual({ h: 0, s: 0, l: 100 }); | ||
}); | ||
it('should convert #000000 to { h: 0, s: 0, l: 0 }', () => { | ||
const result = convertHexValueToHSL('#000000'); | ||
expect(result).toEqual({ h: 0, s: 0, l: 0 }); | ||
}); | ||
it('should convert #ff0000 to { h: 0, s: 100, l: 50 }', () => { | ||
const result = convertHexValueToHSL('#ff0000'); | ||
expect(result).toEqual({ h: 0, s: 100, l: 50 }); | ||
}); | ||
it('should convert #008000 to { h: 120, s: 100, l: 25.1 }', () => { | ||
const result = convertHexValueToHSL('#008000'); | ||
expect(result).toEqual({ h: 120, s: 100, l: 25.1 }); | ||
}); | ||
it('should convert #0000ff to { h: 240, s: 100, l: 50 }', () => { | ||
const result = convertHexValueToHSL('#0000ff'); | ||
expect(result).toEqual({ h: 240, s: 100, l: 50 }); | ||
}); | ||
it('should convert short form hex #f00 to { h: 0, s: 100, l: 50 }', () => { | ||
const result = convertHexValueToHSL('#f00'); | ||
expect(result).toEqual({ h: 0, s: 100, l: 50 }); | ||
}); | ||
it('should throw an error when input is not a valid hex color', () => { | ||
expect(() => convertHexValueToHSL('invalid')).toThrow(); | ||
}); | ||
}); |
@@ -6,4 +6,4 @@ const TEST_FILE_CONTENTS = 'TEST_CONTENT'; | ||
color: { | ||
orange: '#globalorange', | ||
red: '#globalred' | ||
orange: '#f36805', | ||
red: '#d50525' | ||
} | ||
@@ -18,4 +18,4 @@ } | ||
global: { | ||
blue: '#jetglobalblue', | ||
orange: '#jetglobalorange' | ||
blue: '#125fca', | ||
orange: '#41301f' | ||
} | ||
@@ -30,3 +30,3 @@ } | ||
color: { | ||
orange: '#globalorange' | ||
orange: '#f36805' | ||
} | ||
@@ -38,3 +38,3 @@ }, | ||
global: { | ||
blue: '#jetglobalblue' | ||
blue: '#125fca' | ||
} | ||
@@ -49,4 +49,4 @@ } | ||
color: { | ||
blue: '#globalblue', | ||
orange: '#globalorange' | ||
blue: '#c1dade', | ||
orange: '#f36805' | ||
} | ||
@@ -58,3 +58,3 @@ }, | ||
global: { | ||
blue: '#jetglobalblue' | ||
blue: '#125fca' | ||
} | ||
@@ -69,3 +69,3 @@ } | ||
color: { | ||
orange: '#globalorange' | ||
orange: '#f36805' | ||
} | ||
@@ -77,3 +77,3 @@ }, | ||
global: { | ||
blue: '#jetglobalblue' | ||
blue: '#125fca' | ||
} | ||
@@ -85,3 +85,3 @@ } | ||
global: { | ||
blue: '#newjetglobalblue' | ||
blue: '#ebf6fa' | ||
} | ||
@@ -96,4 +96,4 @@ } | ||
color: { | ||
blue: '#globalblue', | ||
orange: '#globalorange' | ||
blue: '#c1dade', | ||
orange: '#f36805' | ||
} | ||
@@ -105,3 +105,3 @@ }, | ||
global: { | ||
blue: '#jetglobalblue' | ||
blue: '#125fca' | ||
} | ||
@@ -113,3 +113,3 @@ } | ||
global: { | ||
blue: '#newjetglobalblue' | ||
blue: '#ebf6fa' | ||
} | ||
@@ -116,0 +116,0 @@ } |
@@ -1771,3 +1771,3 @@ { | ||
}, | ||
"version": "5.0.1" | ||
"version": "5.1.0" | ||
} |
{ | ||
"name": "@justeat/pie-design-tokens", | ||
"version": "5.0.1", | ||
"version": "5.1.0", | ||
"description": "Design Tokens for the PIE Component System", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
379099
31
4379