-
Notifications
You must be signed in to change notification settings - Fork 31
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for HEX8 in HEXRGBa transform #341
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
'@tokens-studio/sd-transforms': patch | ||
--- | ||
|
||
Fix support for HEX8 and shorthand HEX formats in the HEXRGBa transform. |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -2,7 +2,7 @@ import Color from 'colorjs.io'; | |||||||||||||||||||||||
import { DesignToken } from 'style-dictionary/types'; | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
/** | ||||||||||||||||||||||||
* Helper: Transforms hex rgba colors used in figma tokens: | ||||||||||||||||||||||||
* Helper: Transforms hex to rgba colors used in figma tokens: | ||||||||||||||||||||||||
* rgba(#ffffff, 0.5) =? rgba(255, 255, 255, 0.5). | ||||||||||||||||||||||||
* This is kind of like an alpha() function. | ||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||
|
@@ -11,17 +11,62 @@ export function transformHEXRGBaForCSS(token: DesignToken): DesignToken['value'] | |||||||||||||||||||||||
const type = token.$type ?? token.type; | ||||||||||||||||||||||||
if (val === undefined) return undefined; | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
const transformHEXRGBa = (val: string) => { | ||||||||||||||||||||||||
const regex = /rgba\(\s*(?<hex>#.+?)\s*,\s*(?<alpha>\d*(\.\d*|%)*)\s*\)/g; | ||||||||||||||||||||||||
return val.replace(regex, (match, hex, alpha) => { | ||||||||||||||||||||||||
try { | ||||||||||||||||||||||||
const [r, g, b] = new Color(hex).srgb; | ||||||||||||||||||||||||
return `rgba(${r * 255}, ${g * 255}, ${b * 255}, ${alpha})`; | ||||||||||||||||||||||||
} catch (e) { | ||||||||||||||||||||||||
console.warn(`Tried parsing "${hex}" as a hex value, but failed.`); | ||||||||||||||||||||||||
return match; | ||||||||||||||||||||||||
const transformHexColor = (hex: string) => { | ||||||||||||||||||||||||
try { | ||||||||||||||||||||||||
// Fast path for invalid hex | ||||||||||||||||||||||||
if (hex.length < 4) return hex; | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
// Determine format based on length | ||||||||||||||||||||||||
const hexLength = hex.length - 1; // subtract 1 for # | ||||||||||||||||||||||||
Comment on lines
+16
to
+20
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
This makes more sense, otherwise as a reader of the code I'd be wondering "what about hex3, 3 is less than 4" |
||||||||||||||||||||||||
|
||||||||||||||||||||||||
// Only transform hex colors with alpha channel | ||||||||||||||||||||||||
const hasAlpha = hexLength === 4 || hexLength === 8; | ||||||||||||||||||||||||
if (!hasAlpha) return hex; | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
let hexColor = hex; | ||||||||||||||||||||||||
let alpha = '1'; | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
// Convert shorthand to full format if necessary | ||||||||||||||||||||||||
if (hexLength === 4) { | ||||||||||||||||||||||||
const r = hex[1], | ||||||||||||||||||||||||
g = hex[2], | ||||||||||||||||||||||||
b = hex[3], | ||||||||||||||||||||||||
a = hex[4]; | ||||||||||||||||||||||||
hexColor = `#${r}${r}${g}${g}${b}${b}`; | ||||||||||||||||||||||||
alpha = (parseInt(a + a, 16) / 255).toString(); | ||||||||||||||||||||||||
} else if (hexLength === 8) { | ||||||||||||||||||||||||
alpha = (parseInt(hex.slice(7), 16) / 255).toString(); | ||||||||||||||||||||||||
hexColor = hex.slice(0, 7); | ||||||||||||||||||||||||
Comment on lines
+22
to
+39
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This can be deleted entirely, as Colorjs.io can parse hex3/4/6/8 just fine. |
||||||||||||||||||||||||
} | ||||||||||||||||||||||||
}); | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
const [r, g, b] = new Color(hexColor).srgb; | ||||||||||||||||||||||||
return `rgba(${r * 255}, ${g * 255}, ${b * 255}, ${alpha})`; | ||||||||||||||||||||||||
Comment on lines
+42
to
+43
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||
} catch (e) { | ||||||||||||||||||||||||
return hex; | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
}; | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
const transformHEXRGBa = (val: string) => { | ||||||||||||||||||||||||
// Handle standalone hex colors | ||||||||||||||||||||||||
if (val.startsWith('#')) { | ||||||||||||||||||||||||
return transformHexColor(val); | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
Comment on lines
+51
to
+53
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can be deleted right? If it's already hex format, there's no point in transforming it to rgba()? |
||||||||||||||||||||||||
|
||||||||||||||||||||||||
// Handle rgba() with hex colors | ||||||||||||||||||||||||
if (val.includes('rgba(')) { | ||||||||||||||||||||||||
return val.replace(/rgba\(\s*#[A-Fa-f0-9]+\s*,\s*([0-9.%]+)\s*\)/g, (match, alpha) => { | ||||||||||||||||||||||||
const hex = match.substring(match.indexOf('#'), match.indexOf(',')); | ||||||||||||||||||||||||
try { | ||||||||||||||||||||||||
const [r, g, b] = new Color(hex).srgb; | ||||||||||||||||||||||||
return `rgba(${r * 255}, ${g * 255}, ${b * 255}, ${alpha})`; | ||||||||||||||||||||||||
Comment on lines
+60
to
+61
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What about |
||||||||||||||||||||||||
} catch (e) { | ||||||||||||||||||||||||
console.warn(`Tried parsing "${hex}" as a hex value, but failed.`); | ||||||||||||||||||||||||
return match; | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
}); | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
return val; | ||||||||||||||||||||||||
}; | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
const transformProp = (val: Record<string, unknown>, prop: string) => { | ||||||||||||||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -103,4 +103,30 @@ describe('transform HEXRGBa', () => { | |
).to.eql({ width: '2px', style: 'solid', color: 'rgba(0, 0, 0, 0.5)' }); | ||
}); | ||
}); | ||
|
||
it('transforms shorthand hex formats correctly', () => { | ||
// 3-digit hex (#RGB) | ||
expect(transformHEXRGBaForCSS({ value: '#F00' })).to.equal('#F00'); | ||
|
||
// 4-digit hex (#RGBA) | ||
expect(transformHEXRGBaForCSS({ value: '#F00F' })).to.equal('rgba(255, 0, 0, 1)'); | ||
expect(transformHEXRGBaForCSS({ value: '#F000' })).to.equal('rgba(255, 0, 0, 0)'); | ||
Comment on lines
+112
to
+113
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would expect these to have the same output as input, since it's valid hex format. |
||
|
||
// Mixed formats in a single value | ||
expect( | ||
transformHEXRGBaForCSS({ | ||
value: 'linear-gradient(180deg, rgba(#000, 0.5), rgba(#F00F, 0.5))', | ||
}), | ||
).to.equal('linear-gradient(180deg, rgba(0, 0, 0, 0.5), rgba(255, 0, 0, 0.5))'); | ||
}); | ||
|
||
it('handles invalid hex values gracefully', () => { | ||
expect(transformHEXRGBaForCSS({ value: 'rgba(#GGG, 0.5)' })).to.equal('rgba(#GGG, 0.5)'); | ||
expect(transformHEXRGBaForCSS({ value: 'rgba(#12, 0.5)' })).to.equal('rgba(#12, 0.5)'); | ||
}); | ||
|
||
it('transforms HEX8 format correctly', () => { | ||
expect(transformHEXRGBaForCSS({ value: '#000000FF' })).to.equal('rgba(0, 0, 0, 1)'); | ||
expect(transformHEXRGBaForCSS({ value: '#00000000' })).to.equal('rgba(0, 0, 0, 0)'); | ||
Comment on lines
+129
to
+130
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same here, so far this transform only is there to handle rgba(hex, alpha), not transform hex4/8 to rgba. I'm not entirely against repurposing this transform to also do this, but it would definitely be a significant breaking change. |
||
}); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.