Sass with Polymer in production

Sass is an integral component to our workflow here at Toaster. We utilise Sass to automate and organise our code to make the sites we build maintainable and scalable. So when we started to play with Polymer, we noticed a gaping hole where Sass could be used in conjunction with Polymer to take advantage of both Polymer’s modularity and Sass to help organise our sites and ultimately make our lives easier when building Polymer projects. This article aims to showcase how we integrate Sass into Polymer projects.

There are quite a few steps to get Sass setup with Polymer, let’s start with project structure.

Project structure with Sass

Like most site builds with Sass, we have a set of files which define the typography, colour palette, Sass utility mixins / functions and a set of global variables. This is no different in our Polymer builds, we have a _base.scss file which imports the essential modules that we need in each of our Polymer components:

// All the imports below are going to be available to every Sass file.
@import 'vars';
@import 'colors';
@import 'utils';
@import 'media-queries';
@import 'type';

One of the main benefits of Polymer is to componentize everything, we didn’t want to compromise this by having all of our Sass compiled into one file. We would also run into cross-scoping problems with the shadow dom using this method. With this in mind, we include a Sass file in each of the element folders, which imports the base file, so we have access to utility methods and global variables etc. Everything is vulcanized in production, so having multiple CSS files is not a problem.

Compiling and importing

We have a gulp task for dealing with the element styles, this task does the following:

  • Search for Sass files within the elements folder
  • Lint
  • Compile Sass to CSS
  • Autoprefix
  • Wrap the CSS into a Polymer style module
  • Minify if building for production
  • Pipe the transformed content into an HTML file, named [element-name]-styles.html

Transforming the contents

In order to import the styles into our element as a style module, we need to wrap the CSS with a few HTML tags. The outputted file looks something like this:

<test></test>

<dom-module id="my-element-styles">
  <template>
    <style>
      :host {

      }
      /* Styles in here */
    </style>
  </template>
</dom-module>

Thankfully, there is a gulp plugin for that. The idea is you generate the Sass, then pipe it into the gulp-style-modules, which will wrap the CSS in a dom module.

Importing the styles

Once the gulp task has run, an HTML file is added to the elements folder, which contains the CSS needed by the element.

The styles can then be imported into your element like any other CSS import:

<link rel="import" href="my-element-styles.html">

<dom-module id="my-element">
  <template>
    <style include="my-element-styles"></style>

  </template>

  <script>
    Polymer({
      is: 'my-element'
    });
  </script>
</dom-module>

Dealing with CSS variables and Mixins

CSS variables and mixins are essential for working with Web Components, especially for enabling Cross-Scope Styling.

Unfortunately, at the time of writing, Sass does not support CSS mixins and will throw errors when using them. Therefore we have come up with a Sass mixin to escape CSS mixins:

@mixin css-mixin($name-as-a-string, $rule-as-a-string) {
  #{$name-as-a-string}: #{$rule-as-a-string};
}

And used like this:

@include css-mixin(‘--mixin-name’, ‘{
  display: block;
}’);

Although this is a rather ugly way of doing things, escaping is the only way we could get CSS mixins to compile with Sass. Hopefully Sass will support CSS Mixins soon.

Deprecation warnings were introduced in Sass 3.4.22 for CSS variables saying:

Sass 3.6 will change the way CSS variables are parsed. Instead of being parsed as normal properties, they will not allow any Sass-specific behavior other than #{}.
For forwards-compatibility, use #{}:

--variable: #{rgba(255, 255, 255, 0.65)};

So make sure your CSS variables are escaped from now on!

The following work in Sass and do not need escaping:

color: var(--variable-name);
@apply(--mixin-name);

Using Sass to generate an app theme and shared styles

The app theme and shared styles are useful for defining styles for your app, app theme is best for customising the app from a top level and the shared styles should be used to define styles shared across all components. To avoid duplication of colours and variables, we decided to generate the app theme and shared styles from Sass, utilising the configurations from the base.

This is a very similar concept to generating styles for each component. We create an app-theme.scss and shared-styles.scss files, which in turn are compiled and transformed to sit within style tags and imports.

The app theme requires slightly different html to wrap the CSS, which unfortunately is not supported by the gulp-style-modules plugin. So we have to write a very simple task in gulp to run this separately, which wraps the app theme CSS in the following markup:

<link rel="import" href="../bower_components/polymer/polymer.html">

<style is="custom-style">
  /* App theme CSS in here */
</style>

app-theme.scss:

@import 'base/base';

// Application theme

:root {
  --text-primary-color: palette(app, text-primary);
  --text-primary-light-color: palette(app, text-secondary);
  --text-tertiary-color: palette(app, text-tertiary);
  // … more variables and config
}

The compiled app-theme.html file:

<link rel="import" href="../bower_components/polymer/polymer.html">
<link rel="import" href="../bower_components/paper-styles/paper-styles.html">
<link rel="import" href="../bower_components/paper-styles/typography.html">

<style is="custom-style">
  :root {
    --text-primary-color: #1e2e5e;
    --text-primary-light-color: #fff;
    --text-tertiary-color: #00dacc;
  }
</style>

These are then saved out into a styles folder which can be imported into the Polymer app.

Conclusion

That’s it! Building Sass into our Polymer project workflow has improved our productivity and organisation. It’s allowed us to take advantage of modular web components but also make use of the Sass utilities and helpers.

Has your approach differed? Feel free to get in touch! It would be good to know how others have approached this problem.