Skip to content
CSS in JS with support for static / atomic CSS extraction.
JavaScript HTML
Branch: master
Clone or download

Latest commit

Latest commit 0ff1cde May 19, 2020

Files

Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
.github Removed flow May 18, 2020
.vscode Removed flow May 18, 2020
example Fixes #11 May 18, 2020
packages v1.0.14 May 19, 2020
scripts Added basic flow types. May 15, 2020
.flowconfig Added basic flow types. May 15, 2020
.gitignore Removed coverage. May 15, 2020
.prettierrc Initial commit. May 13, 2020
CODE_OF_CONDUCT.md Create CODE_OF_CONDUCT.md May 14, 2020
LICENSE Create LICENSE May 14, 2020
README.md Update README.md May 19, 2020
babel.config.js Removed flow May 18, 2020
bundlesize.config.json Added a warning when the stylesheet could not be resolved. May 14, 2020
jest.config.js Added a warning when the stylesheet could not be resolved. May 14, 2020
package.json Oops.. May 19, 2020
yarn.lock Removed flow May 18, 2020

README.md

stylemug ⚛️

A fast css-in-js library that extracts atomic CSS rules to a .css file.

We try to limit the functionality of this package in order to do the following key features really well:

  • It generates Atomic CSS from the static stylesheets defined in your component.
  • The compiled rules are extracted to a .css file.
  • The stylesheet code in your bundle is replaced (no CSS in your JS) with a hash map for classnames lookup at runtime.

Heavily inspired by Facebook's internal stylex and the "Building the new Facebook" presentation, at ~28:00.

Getting started

Add the package as a dependency.

npm i stylemug

The example below uses stylemug.create to define your stylesheet and will provide a styles function used to resolve your class names.

import React from 'react';
import stylemug from 'stylemug';

const styles = stylemug.create({
  default: {
    backgroundColor: 'red',
    color: 'black',
  },
  large: {
    fontSize: '32px',
  },
});

function App() {
  const [large, toggleLarge] = useToggle(true);

  return (
    <div className={styles('default', large && 'large')}>
      <button onClick={toggleLarge}>Toggle me</button>
    </div>
  );
}

Add stylemug/compiler's babel and webpack plugin in your webpack.config.js config file.

const stylemugCompiler = require('stylemug/compiler');

module.exports = {
  ...
  module: {
    rules: [
      {
        test: /\.m?js$/,
        exclude: /(node_modules|bower_components)/,
        use: {
          loader: 'babel-loader',
          options: {
            // The babel plugin will collect each schematic from `stylemug.create`
            // and rewrite the schema to a lookup table.
            plugins: [stylemugCompiler.babel],
          },
        },
      },
    ],
  },
  plugins: [
    new stylemugCompiler.webpack({
      // The CSS rules extracted by babel will be written to
      // this file.
      name: 'bundle.css',
    }),
  ],
};

Features

Generic rules

A style rule can be shared from one file to another, aslong as the stylesheet itself remains static.

// foo.js
export default stylemug.create({
  container: {
    width: '720px',
  },
});

// bar.js
import foo from './foo';

const styles = stylemug.create({
  default: {
    color: 'green',
  },
});

const classNames = styles('default', foo.container);

Pseudo classes & media queries

const styles = stylemug.create({
  button: {
    ...

    // pseudo classes
    '&:hover': {
      color: 'green',
    },

    // media queries
    '@media (min-width: 720px)': {
      width: '100px',
    },
  },
});

How it works

When webpack bundles your code, babel-loader is used to parse your JS files through Babel and perform a variety of transformations (one of the most common ones is converting ES6 to ES5). One of these transformations is part of the Babel plugin in stylemug/compiler.

The plugin looks for the import statement:

import stylemug from 'stylemug';

and searches for occurances of stylemug.create.

const styles = stylemug.create({
  className1: {
    color: 'red',
    backgroundColor: 'blue',
  },
  className2: {
    color: 'yellow';
  },
});

Afterwards, it compiles the stylesheet by generating a classname for each rule. This concept is called atomic CSS, offering single-purpose units of style, but applied via classes.

.c8938 { color: 'red'; }
.e79cd { color: 'blue'; }
.aaddb { color: 'yellow'; }

Finally, the stylesheet is replaced with a hash map in your code. At this point, the stylesheet is gone from your JS bundle.

const styles = stylemug.create({
  className1: {
    color: 'c8938',
    backgroundColor: 'e79cd',
  },
  className2: {
    color: 'aaddb',
  },
});

Internally, the styles function uses Object.assign to avoid duplication of rules.

// 1.
const classes = styles('className1', 'className2');

// 2.
const classes = Object.assign(
  {},
  {
    color: 'c8938',
    backgroundColor: 'e79cd',
  },
  {
    color: 'aaddb',
  }
);

// 3.
const classes = ['e79cd', 'aaddb'];
You can’t perform that action at this time.