Components with multiple themes

I am building a web app (React) in which I need to support multiple themes. The file structure is as follows. Each component has it’s own directory containing all files that belong to the component.
Example:

ui-Avatar
|– index.jsx
|– theme-a.css
|– theme-b.css
|– ….

When using webpack-dev-server I can quite easily use a conditional require and pull in either theme-a or theme-b.

onComponentWillMount() {
if (this.context.theme === ‘theme-a’) {
require(‘./theme-a.css’);
else (this.context.theme === ‘theme-b’) {
require(‘./theme-b.css’);
}
}

BUT when I use web pack to package for the production server both theme-a.css and theme-b.css get pulled in and packaged into the same resulting css file. With the result that the style rules in the themes overwrite each other.

What I would like is to be able to produce separate files for the two themes with the collected theme snippets from all the components in the application.

Does anyone know how to set up webpack to solve my problem?

Author: Fantashit

4 thoughts on “Components with multiple themes

  1. You can use Local scoped CSS or “CSS Modules” (experimental) for that.

    Just give your component a theme property and use the styles from this.

    Example using CSS Modules:

    import baseStyle from "./baseStyle.css"
    class Component {
      render() {
        var { theme } = this.props;
        return <div className={theme.main + " " + baseStyle.main}>
          <div className={theme.inner + " " + baseStyle.main}></div>
        </div>;
      }
      // ...
    }
    /* themeA.css */
    .main {
      background: black;
    }
    .inner {
      color: white;
    }
    /* themeB.css */
    .main {
      background: white;
    }
    .inner {
      color: blue;
    }

    Useage:

    import themeA from "component/themeA.css";
    import themeB from "component/themeB.css";
    import customTheme from "./customThemeForComponent.css";
    import Component from "component";
    
    <Component theme={themeA} />
    <Component theme={themeB} />
    <Component theme={customTheme} />

    CSS Module mode: https://github.com/webpack/css-loader#module-mode

  2. I realize that this thread is fairly old, but it has the most information about this problem so I’m hoping someone here might be able to help.

    I’m working with a whitelabeled website that has multiple customer domains pointing to the same set of servers. The backend returns a different CSS bundle based on the domain with custom branding. I’d like to be able to do the following:

    • Define a different set of brand_1_theme.css, brand_2_theme.css, files with custom colors, etc.
    • Require a theme file in my CSS code. Seems like postcss-theme might be the best option here, but I’m open to anything.
    • Have ExtractTextPlugin output a different set of CSS files for each theme, and have ManifestPlugin output a manifest file with mappings for all themes.
    • It’d be great if we didn’t have to build the CSS files for all themes in one go, rather than having to run the whole build pipeline for every theme.

    Has anyone done anything similar to this? What has been there approach?

  3. import() works:

    if (this.context.theme === 'theme-a') {
        import('./theme-a.css').then(() => {
            /* ... */
        });
    else (this.context.theme === 'theme-b') {
        import('./theme-b.css').then(() => {
            /* ... */
        });
    }

    OR

    import(`./${this.context.theme}.css`).then(() => {
        /* ... */
    });
  4. I’m dealing with the same problem currently, I try to figure it out without any invasive of codes. Then I write a webpack plugin themes-switch to do that. Maybe it can help you.
    Now I just use variables to define different colors and dimensions, then webpack would generate themes for me, and the style formats can be less, postcss and sass. Then I can make a realtime-switch in my app.

    theme-a.less

    @color-main: #0A6EFA;
    @color-text: #000;
    

    theme-b.less

    @color-main: #222A42;
    @color-text: #FFF;
    

    default.less

    @import 'theme-a.less';
    

    main.less

    @import 'default.less';
    .main {
      background: @color-main;
    }
    

    In use

    changeTheme('theme-b', 'theme-b-123456.css')

Comments are closed.