@@ -52,39 +52,56 @@ const formatHsla = (h: number, s: number, l: number, a: number): string => {
5252 : `hsl(${ hh } , ${ ss } %, ${ ll } %)`
5353}
5454
55+ const isModifyColor = ( val : unknown ) : val is ModifyColor =>
56+ typeof val === 'object' &&
57+ val !== null &&
58+ typeof ( val as ModifyColor ) . value === 'string' &&
59+ typeof ( val as ModifyColor ) . modify === 'object' &&
60+ ( val as ModifyColor ) . modify !== null
61+
62+ const resolveModifyColor = ( { value, modify } : ModifyColor ) : string => {
63+ if ( modify . type === 'darken' || modify . type === 'lighten' ) {
64+ const { h, s, l, a } = colorToHsla ( value )
65+ const newL = modifyLightness ( l , modify . value , modify . type )
66+ return formatHsla ( h , s , newL , a )
67+ }
68+ return value
69+ }
70+
5571/**
5672 * Resolves a component theme object by applying color modifiers to any entries
5773 * shaped as `{ value, modify: { type, value } }`. Entries with `modify.type` of
5874 * `'darken'` or `'lighten'` are transformed in HSL space using the same math
5975 * Tokens Studio applies (`space: "hsl"`): `amount` is a 0–1 fraction of the
60- * remaining distance to black/white. Plain string values and unrecognized
61- * modifier types are passed through unchanged.
76+ * remaining distance to black/white. Plain values and unrecognized modifier
77+ * types are passed through unchanged. Nested plain objects are walked
78+ * recursively so modifiers anywhere in the tree get resolved.
6279 *
63- * @param componentTheme - Theme map whose values are either plain CSS color strings
64- * or `ModifyColor` objects describing a base color and a darken/lighten modifier .
65- * @returns A new theme object with the same keys, where modifier objects have been
66- * collapsed to their final resolved color string.
80+ * @param componentTheme - Theme map whose values can be CSS strings, `ModifyColor`
81+ * objects, or nested theme objects containing the same .
82+ * @returns A new theme object mirroring the input shape, with every `ModifyColor`
83+ * collapsed to its final resolved color string.
6784 */
6885const applyColorModifiers = (
69- componentTheme : Record < string , string | ModifyColor > | undefined | null
70- ) => {
86+ componentTheme : Record < string , unknown > | undefined | null
87+ ) : Record < string , unknown > => {
7188 if ( componentTheme == null ) return { }
72- return Object . keys ( componentTheme ) . reduce < Record < string , string > > (
73- ( res , k ) => {
74- const entry = componentTheme [ k ]
75- if ( typeof entry === 'object' && entry !== null ) {
76- const { value , modify } = entry
77- if ( modify . type === 'darken' || modify . type === 'lighten' ) {
78- const { h , s , l , a } = colorToHsla ( value )
79- const newL = modifyLightness ( l , modify . value , modify . type )
80- return { ... res , [ k ] : formatHsla ( h , s , newL , a ) }
81- }
82- return { ... res , [ k ] : value }
83- }
84- return { ... res , [ k ] : entry }
85- } ,
86- { }
87- )
89+ const result : Record < string , unknown > = { }
90+ for ( const key of Object . keys ( componentTheme ) ) {
91+ const entry = componentTheme [ key ]
92+ if ( isModifyColor ( entry ) ) {
93+ result [ key ] = resolveModifyColor ( entry )
94+ } else if (
95+ typeof entry === 'object' &&
96+ entry !== null &&
97+ ! Array . isArray ( entry )
98+ ) {
99+ result [ key ] = applyColorModifiers ( entry as Record < string , unknown > )
100+ } else {
101+ result [ key ] = entry
102+ }
103+ }
104+ return result
88105}
89106
90107export default applyColorModifiers
0 commit comments