Skip to content

Commit 732d946

Browse files
committed
build(ui-scripts,emotion): fix build:themes and extend the capabilities with adding color modifiers
1 parent 68faa9c commit 732d946

4 files changed

Lines changed: 113 additions & 11 deletions

File tree

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
/*
2+
* The MIT License (MIT)
3+
*
4+
* Copyright (c) 2015 - present Instructure, Inc.
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in all
14+
* copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
* SOFTWARE.
23+
*/
24+
import { colorToHsla } from '@instructure/ui-color-utils'
25+
26+
type ModifyColor = {
27+
value: string
28+
modify: {
29+
type: 'lighten' | 'darken'
30+
value: number
31+
}
32+
}
33+
34+
// Matches Tokens Studio's HSL modifier math: move L by `amount` (0–1) of the
35+
// remaining distance toward the endpoint, so the step shrinks as it approaches
36+
// black/white and never overshoots.
37+
const modifyLightness = (
38+
l: number,
39+
amount: number,
40+
type: 'darken' | 'lighten'
41+
): number =>
42+
type === 'darken'
43+
? Math.max(0, l - l * amount)
44+
: Math.min(1, l + (1 - l) * amount)
45+
46+
const formatHsla = (h: number, s: number, l: number, a: number): string => {
47+
const hh = Math.round(h)
48+
const ss = +(s * 100).toFixed(2)
49+
const ll = +(l * 100).toFixed(2)
50+
return a < 1
51+
? `hsla(${hh}, ${ss}%, ${ll}%, ${a})`
52+
: `hsl(${hh}, ${ss}%, ${ll}%)`
53+
}
54+
55+
/**
56+
* Resolves a component theme object by applying color modifiers to any entries
57+
* shaped as `{ value, modify: { type, value } }`. Entries with `modify.type` of
58+
* `'darken'` or `'lighten'` are transformed in HSL space using the same math
59+
* 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.
62+
*
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.
67+
*/
68+
const applyColorModifiers = (
69+
componentTheme: Record<string, string | ModifyColor> | undefined | null
70+
) => {
71+
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+
)
88+
}
89+
90+
export default applyColorModifiers
91+
export { applyColorModifiers }

packages/emotion/src/useStyleNew.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
import { useTheme } from './useTheme'
2626
import { mergeDeep } from '@instructure/ui-utils'
27+
import { applyColorModifiers } from './styleUtils/applyColorModifiers'
2728
import type {
2829
NewComponentTypes,
2930
SharedTokens,
@@ -121,9 +122,11 @@ const useStyleNew = <
121122
sharedTokensOverrides as Record<string, unknown>
122123
)
123124

124-
const baseComponentTheme = generateComponentTheme
125-
? generateComponentTheme({ primitives, semantics, sharedTokens })
126-
: theme.newTheme.components[componentWithTokensId]?.(semantics)
125+
const baseComponentTheme = applyColorModifiers(
126+
generateComponentTheme
127+
? generateComponentTheme({ primitives, semantics, sharedTokens })
128+
: theme.newTheme.components[componentWithTokensId]?.(semantics)
129+
)
127130

128131
const componentThemeFromSettingsProvider = mergeDeep(
129132
baseComponentTheme,

packages/emotion/src/withStyleNew.tsx

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import { warn } from '@instructure/console'
3636
import { decorator } from '@instructure/ui-decorator'
3737

3838
import { useTheme } from './useTheme'
39+
import { applyColorModifiers } from './styleUtils/applyColorModifiers'
3940

4041
import type { ComponentTheme, InstUIComponent } from '@instructure/shared-types'
4142
import type {
@@ -233,14 +234,15 @@ const withStyleNew = decorator(
233234
sharedTokensOverrides as Record<string, unknown>
234235
) as SharedTokens
235236
// Note: Some components do not have a theme, e.g., FormFieldMessages
236-
const baseComponentTheme = generateComponentTheme
237-
? generateComponentTheme({
238-
primitives,
239-
semantics,
240-
sharedTokens
241-
})
242-
: theme.newTheme.components[componentId]?.(semantics)
243-
237+
const baseComponentTheme = applyColorModifiers(
238+
generateComponentTheme
239+
? generateComponentTheme({
240+
primitives,
241+
semantics,
242+
sharedTokens
243+
})
244+
: theme.newTheme.components[componentId]?.(semantics)
245+
)
244246
const componentThemeFromSettingsProvider = mergeDeep(
245247
baseComponentTheme,
246248
componentOverridesFromSettingsProvider as Record<string, unknown>

packages/ui-scripts/lib/build/buildThemes/generateComponents.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,12 @@ const formatComponent = (collection, key) => {
3232
return { ...acc, [key]: formatComponent(value, key) }
3333
}, {})
3434
}
35+
if (value['$extensions']) {
36+
return {
37+
value: value.value,
38+
modify: value['$extensions']['studio.tokens'].modify
39+
}
40+
}
3541
return value.value
3642
}
3743

0 commit comments

Comments
 (0)