stylex
stylex copied to clipboard
Allow variables in themes
Describe the feature request
When creating a theme, it would make sense to use the variables of the global design system, instead of hard-coding and repeating it (e.g. hex values of colors).
tokens.stylex.ts
// Design system colors
const colors = stylex.defineVars({
white: 'hsl(0, 0%, 100%)',
gray100: 'hsl(0, 0%, 78%)',
gray200: 'hsl(0, 0%, 67%)',
// ...
gray800: 'hsl(0, 0%, 11%)',
gray900: 'hsl(0, 0%, 7%)',
});
// System colors (these will be themed)
const systemColors = stylex.defineVars({
textBlack: colors.gray800,
textDark: colors.gray700,
textMuted: colors.gray400,
border: colors.gray100,
// ...
});
themes.ts
// This does not work. The values need to be copy-pasted from `tokens.stylex.ts` unfortunately.
const darkBackgroundTheme = stylex.createTheme(systemColors, {
textBlack: colors.white,
textDark: colors.gray100, // Instead I should copy-paste the value 'hsl(0, 0%, 78%)'.
textMuted: colors.gray200,
border: colors.gray600,
// ...
});
App.tsx
// Assuming the Text component has `color: systemColors.textDark`.
export const App = () => {
return (
<>
<div {...stylex.props(styles.boxWithWhiteBackground)}>
<Text>This text will be colors.gray800</Text>
</div>
{/* Applying the theme to flip all colors */}
<div {...stylex.props(styles.boxWithBlackBackground, darkBackgroundTheme)}>
<Text>This text will be colors.gray100</Text>
</div>
</>
);
};
The problem in my example is that tokens need to be duplicated, which is error prone when someone forgets to update a token in all places. Ideally, the original colors token can be used in the theme.
However, allowing tokens in theme definitions could cause a recursive loop, which does not make it obvious what the value should be. Although a developer might not write code like this, StyleX is unaware of the hierarchy of tokens.
// Example of a theme based on a var group `myTokens`, which references the same var group inside its definition.
const darkTheme = stylex.createTheme(myTokens, {
textDark: myTokens.textLight,
});
Perhaps using variables inside themes could be allowed, and it is up to the developer to prevent writing unlogical loops like this.
Alternative proposal
If it would be impossible to use variables in a theme because of the above recursion issue, I would suggest that there needs to be a way to define global variables without creating actual tokens.
In my original example, there are two types of variables:
colors: a collection of hsl valuessystemColors: variables that reference thecolors. These need to be themed.
The colors variables are static at the top of the token hierarchy. They don't need to be CSS variables, so they might as well be simple JavaScript strings. The only reason they are not is because StyleX doesn't allow referencing variables from other files. Another possible solution would be to be able to define constants so that StyleX supports importing them from other files. Then we could use these special constants in themes, style definitions, ... but in essence they are simple strings (or numbers?). This would also solve some requests about reusable media queries (I've seen the PoC about defining media queries in StyleX but that seems unnecessarily complicated. I think all we need is a way to share constants across files.)
// `colors` remains an object with string values. StyleX does not process it except for the fact that it allows importing/exporting.
const colors = stylex.letStylexKnowThatTheseCanBeSafelyImportedAcrossFiles({
white: 'hsl(0, 0%, 100%)',
gray100: 'hsl(0, 0%, 78%)',
gray200: 'hsl(0, 0%, 67%)',
// ...
gray800: 'hsl(0, 0%, 11%)',
gray900: 'hsl(0, 0%, 7%)',
});
assert.equal(colors.white, 'hsl(0, 0%, 100%)');
See #601
#601 is related but I think this is still a different issue
See #724
I believe you want to be able to define a set of global constants and then use them within your themes.
@nmn Thanks for linking to the defineConsts API, I believe this will solve my issue. Thanks!
One quick question, will I be able to use those consts as values directly?
export const colors = stylex.defineConsts({
white: stylex.types.color('#FFFFFF'),
});
export const styles = stylex.create({
base: {
color: colors.white,
},
});
One quick question, will I be able to use those consts as values directly?
Yes!
(Closing this issue, please follow the linked issue for updates on stylex.defineConsts