Compile-check LESS/CSS classnames using TypeScript and Webpack

  Subscribe
6/1/2018 - Marc Dürst

As I am making myself familiar with modern frontend development based on React, TypeScript, Webpack and others, I learned something really cool. I like to write this down not only for you – dear reader – but also for my own reference.

The problem

Let’s say you have a trivial React component like this where you specify a classsName to tell what CSS class should be used:

const MyComponent = (props: MyComponentProps) => (
<MySubCompnent className='myclass'>
....
</MySubCompnent>
);

export default MyComponent;

The problem with this is that we don’t have any compiler-check to ensure this class myclass really exists in our LESS file. So if we have a Typo or we later change the LESS file, we cannot be sure all classes/selectors are still valid. Not even the browser will show that. It silently breaks. Bad thing!

A solution

Using Webpack and the LESS loader one can fix this by checking this at compile time. To do so, you can define the style and its classname in the LESS file and import it into .tsx files. The LESS loader for webpack will expose the following LESS variables to the build process where the TypeScript loader (used for the .tsx files) can pick it up.

MyComponent.less:

@my-class: ~':local(.myClass)';
 
@{my-class}{
  width: 100%;
  background-color: green;
}
...

Note the local() function supported by the LESS loader (see webpack config at the end) which scopes that class to a local scope.

The above LESS files can be typed and imported into the .TSX file like this:

MyComponent.tsx:

type TStyles = {
  myClass: string;
};
 
const styles: TStyles = require('./MyComponent.less');
 
const MyComponent = (props: MyComponentProps) => (
 <MySubCompnent className={{styles.myClass}}>
 ....
 </MySubCompnent>
);
 
export default MyComponent;

Then firing up your build, the .less file gets picked up using the require() function and checked against the TypeScript type TStyles. The property myClass will contain the LESS/CSS classname as defined in the .less file.

I then can use the styles.myClass instead of the string literal of the original code.

To get this working, ensure you have the LESS loader included in your webpack configuration (you probably already have it if your are already using LESS):

webpack.json:

module: {
  rules: [
  {
    test: /.tsx?$/,
    loader: "ts-loader"
  },
  {
    test: /.less$/,
    use: ExtractTextPlugin.extract({
    use: [
      {
        loader: "css-loader",
        options: {
          localIdentName: '[local]--[hash:5]',
          sourceMap: true
        }
      }, {
        loader: "less-loader",
        options: {
          sourceMap: true
        }
      }
    ],
    fallback: "style-loader",
    ...
  }),
  ...
},
...

Note: The samples use LESS stylesheets, but one can do the same with SCSS/SASS – I guess. You just have to use another loader for webpack and therefore the syntax supported by that loader.

No broken CSS classnames anymore – isn’t this cool? Let me know your feedback.

This is a cross-post from Marc's personal blog at https://marcduerst.com/2018/03/08/compile-check-less-css-classnames-using-typescript-and-webpack/

Sign up for our Newsletter