Get your CSS classes scoped by component directory
In CSS Modules you have to manually import and assign classes
import styles from './button.styl';
const Button = () => (
<button className={styles.foo}>Press Me</button>
);
React Scoped Styles doesn't require to change the conventional styling workflow. You still assign your classes with plain strings.
import './button.styl';
const Button = () => (
<button className="foo">Press Me</button>
);
npm i react-scoped-styles
The module assumes that component file and its styles are in the same directory. This is sample for Stylus. The same applies for Sass and others.
+-- button
+-- Button.tsx
+-- button.styl
Button.tsx
import React from 'react';
import './button.styl';
const Button = () => (
<button className="foo">Press Me</button>
);
export default Button;
button.styl
.foo
border none
padding 10px 30px
color white
background-color darkslateblue
This will be rendered to
<button class="button-c65bae6565-foo">Press Me</button>
.button-c65bae6565-foo {
border: none;
padding: 10px 30px;
color: #fff;
background-color: #483d8b;
}
The module exposes two loaders both for componenets and styles.
Append the script-loader after it has been transpiled by TypeScript or Babel.
And style-loader should be after the preprocessor loader and before the css-loader.
webpack.config.js
module.exports = {
module: {
rules: [
// TypeScript
{
test: /\.tsx?$/,
use: [
'react-scoped-styles/script-loader',
'awesome-typescript-loader'
]
},
// Babel
{
test: /\.jsx?$/,
exclude: /node_modules/,
use: [
'react-scoped-styles/script-loader',
{
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env', '@babel/preset-react']
}
}
]
},
// Stylus
{
test: /\.styl$/,
use: [
'style-loader',
'css-loader',
'react-scoped-styles/style-loader',
'stylus-loader'
]
},
// Sass
{
test: /\.scss$/,
use: [
'style-loader',
'css-loader',
'react-scoped-styles/style-loader',
'sass-loader'
]
}
]
}
};
To use global styles you can pass globalsPrefix
options to both loaders and prefix your classes with it.
(app
is applied by default)
const scopedStylesOptions = {
globalsPrefix: 'app'
};
{
loader: 'react-scoped-styles/script-loader',
options: scopedStylesOptions
}
// ...
{
loader: 'react-scoped-styles/style-loader',
options: scopedStylesOptions
}
Thus classes with app-
prefix will be ignored.
const Button = () => (
<button className="foo app-global-class">Press Me</button>
);
.foo
border none
padding 10px 30px
color white
background-color darkslateblue
.app-global-class
background-color purple
Becomes
<button class="button-c65bae6565-foo app-global-class">Press Me</button>
.button-c65bae6565-foo {
border: none;
padding: 10px 30px;
color: #fff;
background-color: #483d8b;
}
.app-global-class {
background-color: #800080;
}
To use conditional classnames you can use the classes
function.
Note that the classnames should be inline
import React, { useState } from 'react';
import { classes } from 'react-scoped-styles';
import './sidebar.styl';
export const SideBar = () => {
const [open, setOpen] = useState(false);
return (
<div className={classes([open, 'open'], 'sidebar')}>
...
</div>
)
};
classes (
...([boolean, string] | string)[]
) => string;
classes
function accepts arrays of [condition, className]
pairs, and class-name
strings or default classes.
<div className={classes('default', [true, 'applied'], 'another-one', [false, 'not-applied'])} />
All classes should be INLINE, this won't work
const someClass = 'some';
const someCondition = true;
<div className={classes([someCondition, someClass])} />