fuzetsu / zaftig

~2kB css in js: z`display flex` // .zjsdkk43-1

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

"theming" implementation

resolritter opened this issue · comments

First of all, what I mean by "theming" is:

  1. being able to define variables which influence the style
  2. allow components to be notified (for e.g. re-rendering) when those variables change
  3. when the active theme's variable change, this effect is propagated automatically to all the interested parties

So going by that concept, by reading the README, I've understood this library does not offer a theming solution.

I've tried to implement it in React

Interesting files

constants.ts

defines the variables for the theme [1st item].

theme.ts

useThemeVariable subscribes to themeChangedCallbacks [2nd item]; due to the fact that it's a hook leveraging useState, the 3rd item is also satisfied.

Button.tsx

Due to useThemeVariable, this components re-renders everytime the theme changes.

What motivated me to do this, initially, was wondering how I'd use Sass' helper functions (darken, lighten, saturate, etc) outside of SCSS files, since that's not directly available with CSS-in-JS. I later found there's the color package for that, so you'd just need to post-process the variable's value after every theme change.


If you feel interested in bringing similar ideas into the library somehow, let me know! And thank you for this project.

Thanks for sharing! I like your approach. Having programmatic access to the variables in a nice touch 👍
Do you do that because you need to perform some code based transformations on the theme variables? Like something color based?

I've personally implemented theming in my applications using a kind of similar method but maybe more stripped down, which led me to not feel a huge need to put it into Zaftig or into a separate library.

I usually do something like this:

const themes = {
  day: z.style`
    $main-bg white
    $main-fg black
  `,
  night: z.style`
    $main-bg black
    $main-fg white
  `
}

const setTheme = name => 
  document.rootElement.style.cssText = themes[name] || themes.day

setTheme('night')

Very simple approach that just swaps the style attribute on the <html> element. I save the styles onto an object keyed by the theme name and parsed using z.style

Pretty basic, but very lightweight and functional, you could still of course get the current value for the theme variable using:

const getThemeVariable = (name, ctx = document.rootElement) =>
  getComputedStyle(ctx).getProperyValue('--' + name)

getThemeVariable('main-bg') // white

Curious what you think 😄

Do you do that because you need to perform some code based transformations on the theme variables? Like something color based?

Most of the use-cases were like that. For instance, having a red color and shifting it to be 10% darker, as that's something you don't want to be doing by hand.

Sometimes it's also needed to override some padding, border width, or whatever other preset attribute from a third-party component using raw values instead of classes, so that'd be another use-case.

Sometimes I need to calculate the dimensions of one element based on another's, but I probably should be using CSS's calc instead of JS for that...

const themes = {
  day: z.style`
    $main-bg white
    $main-fg black
  `,
  night: z.style`
    $main-bg black
    $main-fg white
  `
}

const setTheme = name => 
  document.rootElement.style.cssText = themes[name] || themes.day

I'd intuit that has some disadvantage. Instead of only updating the CSS variables like in the project I showed, you're replacing the whole stylesheet's content with another, isn't that right? So I'd think the whole layout would need to be recomputed instead of only the places relying in the changed variables. I also suppose this "advantage" I'm imagining would not matter in general.

const getThemeVariable = (name, ctx = document.rootElement) =>
  getComputedStyle(ctx).getProperyValue('--' + name)

Similar to the the paragraph before, this has to call getComputedStyle for every component relying on the variable, which I'd assume is more expensive than simply getting the value which already exists in JS.

kind of similar method but maybe more stripped down

Yes, it looks quite less verbose. I think the style I showed is more suited for "TypeScript"-ish projects which tend to be more robust, because you get some variable safety without having to do typeof. I usually prefer verbosity over having many different ways of writing one thing which does not fulfill all my needs at once (this is similar to the feeling I brought up in #14 , so sorry for that 😅 ).