Skip to content

RFC: Theming #18

Closed
Closed
@itsdouges

Description

@itsdouges

Fresh eyes needed. We should also take inspiration from https://theme-ui.com/

We can even create a TS language service to tell us what themes are available with intellisense!

Global theming

Define your tokens in your options:

{
  base: {},
  default: {},
  [optionalotherthemes]: {} // every other theme needs to have the same keys from default
}

Then use them in your CSS sourced from default:

css`
  color: primary; // or
  color: theme(primary);
`

Transforms to:

color: #fff;
color: var(--var-123abc);

Hardcodes and use the variable so it works with/without a parent theme.

Prepare a theme provider for consumers (you should only ever create one of these):

import { createThemeProvider } from '@compiled/css-in-js';

export const ThemeProvider = createThemeProvider();

// Optional "runtime" themes.
export const ThemeProvider = createThemeProvider({ ... });

Transforms to:

import { _TP } from '@compiled/css-in-js';

const theme = { base: { ... }, default: { '--var-abc123': '#000', ... }, ... };
// optional runtime themes would be merged in and converted to css variables

export const ThemeProvider = (props) => (
  <_TP theme={props.theme}>
   {props.children(theme[props.mode])
  <_TP>
);

And then your consumers use like:

import { ThemeProvider } from 'your-design-system';

const App = () => (
  <ThemeProvider theme="dark">
     {style => <div style={style}>...</div>
  </ThemeProvider>
);

Component theming

Would be the same as above with one key difference - the theme isn't sourced from the config. It's sourced inline.

import { createVariants } from '@compiled/css-in-js';

const useVariants = createVariants<'primary' | 'danger'>({
  default: { default: {}, [optionalotherthemes]: {} },
  [optionalotherthemes]: {},
});

<button style={useVariants('primary')} css={{ border: 'border', color: 'color' }}>
  blah
</button>
import { useMode } from '@compiled/style;

// transformed to css variables
const variants = { default: {}, ... };

const useVariants = (variant: string) => {
  const mode = useMode();
  const defaultVariant = variants.default;

  return { 
    ...defaultVariant.default,
    ...defaultVariant[mode],
    ...variants[variant][mode],
  };
};

The type safety aspect is missing a little. Perhaps instead of using string literals theme(primary) and variant(color) we could import them?

import { themeRefs, createVariants } from '@compiled/css-in-js'; 

const { useVariants, variant } = createVariants({});
const theme = themeRefs();

<div
  style={useVariants('primary')}
  css={{
    backgroundColor: variant('backgroundColor'),
    color: theme('primary'),
  }}
/>

???

Goals

  • Minimal use of react context
  • Css variables for passing style values around
  • Don't force specific markup to consumers
  • Ensure bleeding of css variables doesn't affect things it shouldn't

Lingering thoughts

  • What about runtime themes (i.e. they aren't known until runtime)?

Metadata

Metadata

Assignees

No one assigned

    Labels

    rfc 💬Request for comments

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions