Search code examples
sasscompass-sasscompass

Iterate over theme-variable files in SCSS


I want to create different css-themes for a WordPress theme by using theme setup files. The setup (simplified) would be as following:

/themes/_theme1.scss
/themes/_theme2.scss
/components/_file1.scss
/components/_file2.scss
/theme.scss

The idea is to enable easy theming by adding a class to the body of the document like .theme-theme1 or .theme-theme2. In the files _theme#.scss I want to define variables like text colour, font sizes and so on. In _file#.scss the actual styles are defined.

My question now is, how to iterate over the theme setup files while filling up the files.scss.

Sample idea, Background colour:

body {

###foreach themefile###
&.theme# {
    background-color: $background-color;
}
###/foreach###

}

I know how to do this with only one theme available in the resulting CSS file, but I want to make ALL themes available in the resulting CSS. Feel free to ask more details as I am not sure if I explain me right.

Is there a way to create this stylesheet via some kind of foreach loops through variables in theme files or does it have to be done with extra scss-rules per theme file?


Solution

  • This is somewhat possible using a combo of @import with a @mixin to generate the styles. This method should produce minimal repeated code.

    Here's how we'll setup the files.

    - scss
      - themes
        - _theme1.scss
        - _theme2.scss
      - _theme.scss
      - styles.scss
    

    The _ prefix on some of the files prevent them from being compiled into CSS to keep our build nice and clean. Now let's go through the contents of the files:

    _theme1.scss

    $theme-name: 'theme1';
    
    $primary-color: red;
    $primary-font-size: 24px; 
    

    _theme2.scss

    $theme-name: 'theme2';
    
    $primary-color: blue;
    $primary-font-size: 12px;
    

    This is an oversimplified example but should give the basic idea. Each theme file will contain only variables.

    _theme.scss

    @mixin themestyle() {
      body.#{$theme-name} {
        p {
          color: $primary-color;
          font-size: $primary-font-size;
        }
    
        .bordered {
          border: 3px solid $primary-color;
        }
      }
    }
    

    The themestyle mixin will contain all the styles for each theme, using the variables from the /themes/_theme*.scss files. The body.#{$theme-name} will create a selector like body.theme1 or body.theme2, depending on the current value of the $theme-name variable.

    In this demo I'm styling on a p tag but this could easily be extended to all elements/selectors for your site. The important thing to remember is all styles need to be inside the body.#{$theme-name} selector.

    Now the final, and least DRY part. The styles.scss file will import each theme file then call the themestyle mixin to generate the styles for each theme.

    styles.scss

    @import 'themes/theme';
    
    /* Theme 1 Styles */
    @import 'themes/theme1';
    @include themestyles();
    
    /* Theme 2 Styles */
    @import 'themes/theme2';
    @include themestyles();
    

    The repeated @import/@include is required because it's not possible to @import within a loop or mixin, or this could be optimized a bit more.

    Once styles.scss is compiled the output will be:

    /* Theme 1 Styles */
    body.theme1 p {
      color: red;
      font-size: 24px; }
    body.theme1 .bordered {
      border: 3px solid red; }
    
    /* Theme 2 Styles */
    body.theme2 p {
      color: blue;
      font-size: 12px; }
    body.theme2 .bordered {
      border: 3px solid blue; }
    

    These themes can now be implemented by adding a class to the body tag, like <body class="theme1"> or <body class="theme1">.

    Here's a Cloud9 project showing the setup.