Late last year, I was working on a project that required some elements of the page to be "theme-able" to match different live events being represented. Some background colors on containers needed to be changed and icons in certain contexts should be changeable as well.

This could get out of hand quickly, as I needed to create a number of different themes. Sass is my weapon of choice, so I dug through the docs to find an efficient way to make my code work for me.

Basic Theming

Theming elements can be a lot of work:

  • Repetitive css
  • In-depth knowledge of structure
  • A lot of overrides
  • Not very easy to add or read through.

Typically, with css, this would look something like:

  .post__title, 
.post__subtitle {
    color: black;
}

.theme--forest .post__title {
    color: forestgreen;
}

.theme--forest .post__subtitle {
    color: green;
}

We've got to include a parent class to override the defaults, usually included on the site's main wrapping element or body tag. We can't get around this. Any tool we use, eventually has to create this pattern. Sass makes it a little less repetitive.

  .post__title, 
.post__subtitle {
    color: black;
}

.theme--forest {
  .post__title {
    color: forestgreen;
  }

  .post__subtitle {
    color: green;
  }
}

This is much better than before but it still requires you to retype the classes over and over for each theme.

Placeholders To The Rescue

So the first thought is to use a placeholder. This will allow us to divvy out a set of styles to each element. This would require a specific placeholder for each theme we wanted to create, which still doesn't quite do what we want.

A placeholder, however, generates the style where it's declared, not where it's extended. For example:

  .theme--forest {
  %theme--post__title {
    color: forestgreen;
  }
}

.theme--ocean {
  %theme--post__title {
    color: navy;
  }
}

.post__title {
  @extend %theme--post__title;
  color: black;
}

Generates the following css:

  .theme--forest .post__title {
  color: forestgreen;
}

.theme--ocean .post__title {
  color: navy;
}

.post__title {
  color: black;
}

By declaring these placeholders within a different context with the same name, we're able to extend the placeholder one time in the stylesheet and give it a different style based on context. The extend pulls the name of the class and implements it where the placeholder is declared. Awesome!

We still don't want to manually write those placeholders over and over. Time to make a mixin!

Generating Placeholders

Let's think about this backwards. I want to implement this mixin by being able to declare a theme, and then give the mixin the variables I want to change. The mixin should know, based on the variables, what I want.

  .theme--forest {
  @include theme-generator(
    forestgreen,
    green
  );
}

That looks nice and clean. Before diving into the mixin, we should declare the theme-able elements throughout the stylesheet first.

  .post__title {
  @extend %theme--post__title;
  color: black;
}

.post__subtitle {
  @extend %theme--post__subtitle;
  color: black;
}

Now that we've done that, the mixin needs to know what these placeholders are, so declare them in the mixin.

  @mixin theme-generator(
  $post__title,
  $post__subtitle
) {

  %theme--post__title {
    color: $post__title;
  }

  %theme--post__subtitle {
    color: $post__subtitle;
  }
}

Now when the mixin is called, the placeholders will be generated based on the variables given! The only issue with this is that if I only want to override the title OR the subtitle, the mixin doesn't allow for it.

Give It Some Brains

If we give the variables a default value, we can check for it to generate the placeholder selectively.

  @mixin theme-generator(
  $post__title: "",
  $post__subtitle: ""
) {

  @if $post__title != "" {
    %theme--post__title {
      color: $post__title;
    }
  }

  @if $post__subtitle != "" {
    %theme--post__subtitle {
      color: $post__subtitle;
    }
  }

}

Go back through your stylesheet and add the !optional flag to your @extend properties. This will allow sass to skip any extends that don't exist, preventing a build error.

  .post__title {
  @extend %theme--post__title !optional;
  color: black;
}

.post__subtitle {
  @extend %theme--post__subtitle !optional;
  color: black;
}

Now including the mixin will read better and generate much cleaner css. We can tell what is being overridden by the theme.

  .theme--forest {
  @include theme-generator(
    $post__title: forestgreen,
    $post__subtitle: green
  );
}

.theme--ocean {
  @include theme-generator(
    $post__subtitle: navy
  );
}

  .theme--forest .post__title {
  color: forestgreen;
}

.theme--forest .post__subtitle {
  color: green;
}

.theme--ocean .post__subtitle {
  color: navy;
}

Here's a Sassmeister link that shows everything working together.

Final Thoughts

The benefit of this approach means you don't have to remember where the themeable element is in your stylesheet, nor what the structure is. Another developer can easily look at previously implemented themes and create a new one in just a few minutes, increasing ease of use and maintainability.

Be lazy. Make your code work for you.


600 0 2