stylex icon indicating copy to clipboard operation
stylex copied to clipboard

Allow variables in themes

Open dd-jonas opened this issue 1 year ago • 2 comments

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 values
  • systemColors: variables that reference the colors. 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%)');

dd-jonas avatar Jul 24 '24 14:07 dd-jonas

See #601

necolas avatar Jul 24 '24 15:07 necolas

#601 is related but I think this is still a different issue

dd-jonas avatar Jul 30 '24 09:07 dd-jonas

See #724

I believe you want to be able to define a set of global constants and then use them within your themes.

nmn avatar Oct 09 '24 08:10 nmn

@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,
  },
});

dd-jonas avatar Oct 09 '24 08:10 dd-jonas

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

nmn avatar Oct 09 '24 17:10 nmn