etchteam / storybook-addon-css-variables-theme

Change the CSS variable based design tokens in your storybook on the fly
MIT License
28 stars 12 forks source link

@storybook/builder-vite support #20

Open AlexMargk2112 opened 2 years ago

AlexMargk2112 commented 2 years ago

Hi,

i ve been using storybook till now with webpack builder. I tried switching to "Vite" which is significantly faster but not all the storybook plugins work as expected. The documentation of "@etchteam/storybook-addon-css-variables-theme" of suggests using loaders which are made for webpack. Will in future will be @storybook/builder-vite support for this plugin?

gavmck commented 2 years ago

Any idea how to do the lazy loading sass files with vite?

AlexanderMar21 commented 2 years ago

Hi! I am the same as @AlexMargk2112 . This is my personal github account. I dont know what lazy exactly would be but i kind of made a helper function that returns an object with 2 functions( use(), unuse() ) that are used by your addon. In the use() funtcion i am using the concept of dynamic import const file = import(filePath) The code it looks like this

import cssVariablesTheme from '@etchteam/storybook-addon-css-variables-theme';

export const decorators = [cssVariablesTheme];

const createStyleTags = (url) => {
  return {
    styleTag: null,
    innerStyles: '',
    use: async function () {
      if (!this.styleTag) {
        this.styleTag = document.createElement('style');
        this.styleTag.type = 'text/css';
        this.innerStyles = (await import(url)).default;
        this.styleTag.innerHTML = this.innerStyles;
        document.body.appendChild(this.styleTag);
        return;
      }
      this.styleTag.innerHTML = this.innerStyles;
    },
    unuse: function() {
      if (this.styleTag) {
        this.styleTag.innerHTML = '';
      }
    }
  }
}
const theme1 = createStyleTags('../src/assets/themes/theme1.scss?inline');
const theme2 = createStyleTags('../src/assets/themes/theme2.scss?inline');

export const parameters = {
  actions: { argTypesRegex: '^on[A-Z].*' },
  controls: {
    matchers: {
      color: /(background|color)$/i,
      date: /Date$/,
    },
  },
  cssVariables: {
    files: {
      'Theme ONE': theme1,
      'Theme TWO': theme2,
    },
  },
};

The code might not be perfect but this was my first idea to create something similar like the webpack loader.

I have also made a repo with a minimum Storybook - Vue - @storybook/builder-vite to reproduce the proof of concept. repo

I have noticed though an issue when changing the theme. Sometimes the current selected theme is not updated at the themes tab but after checking previous projects that are using Webpack i saw that the issue is also there so thats not vite relative issue.

Kind regards , Alexander

magersoft commented 1 year ago

@AlexanderMar21 Thank you for your solution, but it's not working for me correct. Theme doesn't switch after first change. I changed your code a bit and got a working solution

const cssVariablesViteHack = (url: string) => {
  return {
    styleTag: null as any,
    innerStyles: '',
    use: async function () {
      if (!this.styleTag?.innerHTML) {
        this.styleTag = document.createElement('style');
        this.styleTag.type = 'text/css';
        this.innerStyles = (await import(/* @vite-ignore */ url)).default;
        this.styleTag.innerHTML = this.innerStyles;
        document.body.appendChild(this.styleTag);
        return;
      }
      this.styleTag.innerHTML = this.innerStyles;
    },
    unuse: function () {
      if (this.styleTag) {
        this.styleTag.innerHTML = '';
      }
    }
  };
};
KokoWebDev commented 1 year ago

I kept running into issues with the above switching between stories. Vite also adds some <style /> in development builds it seems. This works like a charm on my end.

/**
 * Create and place <style /> in document
 */
const styleTag = document.createElement('style');
styleTag.type = 'text/css';
document.head.appendChild(styleTag);

/**
 * Utility method to inject and cleanup style sheets
 */
export const createStyleTags = (url: string) => {
  return {
    use: async function () {
      // Update innerHtml with stylesheet data
      if (styleTag.id !== url) {
        styleTag.id = url;
        styleTag.innerHTML = (await import(/* @vite-ignore */ url)).default;
      }
    },
    unuse: function () {
      /**
       * <style /> tag that vite loads into the document head
       * These are identified by the style tag containing data-vite-dev-id attribute
       */
      Array.from(document.head.querySelectorAll('style[data-vite-dev-id]')).forEach((styleTag) => styleTag.remove());
    },
  };
};
yormy commented 1 year ago

When I try to implement this workaround it works for some css files. However when I try to load a css from node _modules I get:

Failed to load module script: Expected a JavaScript module script but the server responded with a MIME type of "text/css". Strict MIME type checking is enforced for module scripts per HTML spec.

When I copy that file to my .storybook folder and load it from there it does work Any ideas?

john-melin commented 1 year ago

Hmm all of the solutions only works on first load for me. As soon as i change story or any of the controls that affect color, it dissapears

lennartbuit commented 1 year ago

I have similar problems. It seems to be that switching stories usees your current theme, and then also unusees that same theme. Workarounds like @magersoft present cause the style tag to be emptied by that unuse call.

A workaround I'm currently investigating is creating a single style tag, and only listening to use calls:

const makeCssFiles = themes => {
  const styleTag = document.createElement('style');
  document.body.appendChild(styleTag);

  const use = name => () => {
    const { [name]: styles } = themes;
    styleTag.innerHTML = styles;
  };

  return Object.fromEntries(
    Object.keys(themes).map(name => {
      return [name, { use: use(name), unuse: () => null }];
    }),
  );
};

With this usage:

import lightTheme from 'lightTheme.css?inline';
import dark from 'darkTheme.css?inline';

export const parameters = {
  cssVariables: {
    files: makeCssFiles({
      Light: lightTheme,
      Dark: darkTheme
    }),
    defaultTheme: 'Light'
  }
}

Let me know whether that works for you ^^.

simonhermann commented 1 year ago

@lennartbuit This works great locally, but unfortunately not with a static build, not sure why.

magersoft commented 1 year ago

@simonhermann yes, I have the same problem in static build. I'll try fix this problem, but nothing yet. If you find some decisions, please share it here.

WesselKroos commented 10 months ago

After following the click event with the debugger I got stuck at the cssVariablesChange event that is emitted but not received. I'm not an expert in storybooks manager api that handles these messages. Maybe someone else has any idea on how we can fix this?

The cssVariablesChange event is emitted from this line: emit('cssVariablesChange', { id: newValue }); https://github.com/etchteam/storybook-addon-css-variables-theme/blob/2b5505db0d47b184da78db6832587d9494c2eac4/src/register.tsx#L71

And should be received in this line: channel.on('cssVariablesChange', ({ id }: { id: string }) => https://github.com/etchteam/storybook-addon-css-variables-theme/blob/2b5505db0d47b184da78db6832587d9494c2eac4/src/index.ts#L75