postcss / postcss-custom-properties

Use Custom Properties in CSS
https://postcss.github.io/postcss-custom-properties
MIT License
596 stars 77 forks source link

Generating one separate stylesheet per theme #80

Closed manulitopetito closed 5 years ago

manulitopetito commented 6 years ago

Right now we are using a scss mixin ( https://github.com/zellwk/themify ) to provide different themes for our react components in our app.

It works great, but produces rather long selector chains in the compiled css file.

While we do have to provide the ability to change themes during runtime ( which is no hassle with the current solution, we only have to switch one classname on the body element ) the default usecase is, that the theme does not change.

So we thought about reducing the complexity and filesize of our stylesheets by splitting them up in separate files.

Example:

themes.scss:

$themes: (
  red: (
    mainColor: #aa3939,
    secondaryColor: #D46A6A
  ),
  blue: (
    mainColor: #2e4272,
    secondaryColor: #4f628e
  )
);

button.sccs

@import 'themes.scss';

.button {
  @include themify($themes) {
    color: themed('secondaryColor');  
    background-color: themed('mainColor');  
  }}
}

This becomes:

.theme-red .button {
  color: #D46A6A;
  background-color: #aa3939;
}

.theme-blue .button {
  color: #4f628e;
  background-color: #2e4272;
}

Now I want this to become:

theme-red.css:

.button {
  color: #D46A6A;
  background-color: #aa3939;
}

theme-blue.css:

.button {
  color: #4f628e;
  background-color: #2e4272;
}

How would I achieve this with postcss and custom properties?

Thanks a lot!

jonathantneal commented 6 years ago

I think this is doable. My OSS time is currently limited, so I’ve made a call out to community members to help you. Please ping me in a week if no one has helped you.

https://twitter.com/jon_neal/status/962453326183981058

bjankord commented 6 years ago

Our team is facing this same issue, wanting to generate multiple themed stylesheets from 1 entry file to provide different themes for our react components in our app.

Our code setup looks like the following. We have a default CSS file using CSS custom properties, this gets imported into app.js.

/* styles.css */
.button {
  color: var(--button-color, #f00); /* Falls back to #f00 if --button-color is not set */
}

This stylesheet is then imported into our app.js file.

// app.js
import ('./styles.css');

app.js is our main entry we run through webpack. This runs the imported css file through postcss-loader with the postcss-custom-properties and which outputs the following:

/* style.css gets output as app.css */
.button {
  color: #f00;
}

We have a separate CSS file which acts as our theme file which contains definitions for all of the CSS custom properties inside of a root selector. It looks something like this.

blue-theme.css

/* blue-theme.css */
:root {
  --button-color: #006;
}

If an app wanted to use this this theme, it would import blue-theme.css into it's app.js file which acts as the entry file for webpack.

// app.js
import ('./styles.css');
import ('./blue-theme.css');

When app.js is run through webpack and postcss-loader with the postcss-custom-properties, it outputs the following:

output

/* app.css */
.button {
  color: #006; /* value from blue-theme.css used via postcss-custom-properties */
}

:root {
  --button-color: #006;
}

What we'd much rather have is 2 generated CSS files: app.css and app-blue-theme.css

ideal app.css

/* app.css */
.button {
  color: #f00; /* default color */
}

ideal app-blue-theme.css

/* app-blue-theme.css */
.button {
  color: #006; /* themed color */
}

/* It would be nice to remove this :root selector after the trasformation of CSS custom properties to static values, but thats another issue */
:root {
  --button-color: #006;
}

PostCSS plugin ideas

I've thought a little bit about the interface for a plugin to handle this. I've envisioned a plugin which takes file paths for theme files and uses it to generate the additional theme stylesheets from the 1 entry based stylesheet.

// postcss.config.js
const ThemingPlugin = require('./theming-plugin');

module.exports = {
  plugins() {
    return [
      ThemingPlugin({
        "blue-theme": "./blue-theme.css",
      }),
    ];
  },
};

Where I start to get confused is how to go about splitting the css AST and what happens to it after it is split. Does splitting the CSS file into multiples prevent additional PostCSS modifications.

I've put together a prototype of what I'd like to achieve, multiple themed CSS files using CSS custom properties complied into static values, though it achieves this by using multiple webpack entries. I'd love to find a way to do this via 1 webpack entry and a PostCSS plugin.

Here a few links I've come across discussing this more on this topic:

niccai commented 6 years ago

This example repo I did doesn't utilize custom properties, but it does illustrate a simple approach on how to do theming in postcss. It probably answers the original question at least. Hope it helps.

https://github.com/niccai/postcss-themes-example