As of this writing, if you google “css modules in aurelia” the top two results are:

  • An NPM package that is deprecated.
  • A Medium article where the guy says “I haven’t beaten this one yet, but I’m getting close.” Then later on, “I’ll have to follow up with another post if I am ultimately able to get it all playing together nicely.” Which he never did.

If you dig into the comments of that Medium article though, you’ll find the solution by Sayan Pal, which I’ve confirmed in a working project. Essentially the solution lies with something like this:

// webpack.config.js
const cssRules = [
  {
    loader: 'css-loader',
    options: {
      modules: true,
      localIdentName: '[name]__[local]',
    }
  },
  // You can omit this loader if you're not wanting to support SASS.
  // But you should be wanting to support SASS. :)
  {
    loader: 'sass-loader',
  },
];

module.exports = {
  // Obviously your actual config file will have more than this in it.
  // This just demonstrates the modularized piece of it.
  module: {
    rules: [
      cssRules,
    ],
  },
};

In Sayan’s example, his localIdentName also includes a ___[hash:base64:5] piece which I personally don’t think you should ever need. You shouldn’t ever have two CSS files that are named the same thing, just like you shouldn’t ever have two view-models or views that are named the same thing, and CSS files have a one-to-one correlation to those if you’re going the modularized route. Hence, the double guarantee of uniqueness by appending a hash at the end of the classname merely serves to bloat the DOM and the resulting stylesheet, and it’s wasteful. But I digress.

Let’s say you create a hello-world.scss file that looks like so:

/* hello-world.scss */
.component {
  padding: 25px;
}

Next, per Sayan’s example, you’ll have a view-model that looks something like this:

// hello-world.js
const styles = require('./hello-world.scss');

export class HelloWorld {
  styles = styles;
}

Then in your view you’ll have something that looks like this:

<!-- hello-world.html -->
<template>
  <div class.one-time="styles.component">Hello world</div>
</template>

On Aurelia’s roadmap for 2018 is to get single-file component support. From the blog:

While Aurelia has focused primarily on optimizing UI workflows for teams working on the same components in parallel, we realize that smaller teams and individual developers have different workflows and needs. To address this, we’re hoping to enable a single-file component development story in 2018.

When that day comes, this solution should continue to work. Until then, you might wish you could import the CSS directly into the HTML files via a require tag, instead of importing it into in the JS files. After all, when the views can play musical chairs with the view-models, you’re allowed a level of dynamism that you can’t have with single-file components; you can mix and match different views with other view-models. Instead of a one-to-one relationship you can have a many-to-many relationship.

There are a couple of problems with importing modularized CSS into HTML files in Aurelia, however. First, you can’t extract CSS via the mini-css-extract-plugin when you’re using this sort of syntax in your HTML files:

<!-- hello-world.html -->
<template>
  <require from="./hello-world.scss" />
</template>

At least, if it’s possible, I haven’t seen how.

Moreover, even if you could do that, I’m don’t know how you would associate that CSS to its view-model instance. Clearly, modularized CSS in Aurelia can only work in the context a specific (i.e. non-dynamic) view-model. Unless of course you manually hand-write the compiled class names, like so:

<!-- hello-world.html -->
<template>
  <div class"hello-world__component">Hello world</div>
</template>

But nobody wants to do that.

The good news is that this will be a moot point once single-file components come around. We won’t be thinking in those categories at that point; we’ll have a one-to-one understanding of views and their view-models, which is (respectfully) how it should have been all along.1


  1. When it comes to Aurelia’s MVVM pattern, every single nifty, elegant thing you can do with a many-to-many relationship of views and view-models you can achieve with a one-to-one relationship. That’s a separate topic for another time. For now, suffice it to say that the secret is in having smart components and dumb components, i.e. HOC. ↩︎