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.
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',
}),
],
};
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);
const styles = stylemug.create({
button: {
...
// pseudo classes
'&:hover': {
color: 'green',
},
// media queries
'@media (min-width: 720px': {
width: '100px',
},
},
});
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'];