A utility for generating class name strings, with support for CSS Module scoping and BEM expansion.
npm i -S use-classy
Classy does a lot of things, but at it's simplest, it’s a utility method for generating normalized class name strings. It also supports CSS Modules and BEM expansions, and ships with a React-ready hook.
To get started, you can pass Classy any number of selectors (either strings or nested arrays of strings) and it will generate a normalized class string. Here's a contrived example:
import { classy } from 'use-classy';
classy('class1', [[false && 'class2'], [[['class3']]]], '.class4, class5')
Under the hood, this will flatten any arrays, filter out falsey values, normalize the strings, and more. This gives you a nice, simple, space-separated class string:
'class1 class3 class4 class5';
If you're importing a CSS module, you can pass the scoped classes as the first argument. Classy will automatically match and replace the "naked" selectors with their scoped counterparts!
import classes from './style.module.scss';
// assuming ^this stylesheet exports something like { someClass: "r2984fh9wnc" }
classy(classes, 'someClass'); // r2984fh9wnc
If you'd like to reuse the same scope in a bunch of places, you can construct an instance of classy for reuse, like so:
import classes from './style.module.scss';
const cn = new classy({ classes });
cn('someClass'); // r2984fh9wnc
Classy can auto-expand BEM "partial" selectors. Call it with the bem
namespace option to prefix any selector that starts with -
or a _
with the root class. Sass-style root selectors (&
) will also be replaced with the root namespace.
Say, for example, you had the following SCSS module…
.Block {
&--title {
}
&__modifier {
}
}
We can construct a new instance of classy, specifying a base class against which to expand partial BEM selectors. For the ultimate classy
-ness, we can also pass in our module classes, since BEM expansion works with auto-scoping!
import classes from './style.module.scss';
const bem = new classy({
bem: 'Block',
classes,
});
Now we can reuse a single classy instance throughout our component to generate markup structures against our BEM selectors on the fly!
bem(); // Block
bem('&'); // Block
bem('--element'); // Block-element
bem('__modifier'); // Block_modifier
(The above comments give the "naked" selectors for clarity; in reality this would actually output the scoped classnames.)
Classy comes with a React-ready wrapper around the main classy
method! You can call it like any other hook, and use the returned instance to generate selector structures for your className
props, like so:
import { useClassy } from 'use-classy';
import classes from './style.module.scss';
const MyElement = ({ title, className }) => {
const bem = useClassy('MyElement', classes);
return (
<header className={bem('&', className)}>
<h2 className={bem('-title')}>{title}</h2>
</header>
);
};
Given this JSX, you'd end up with the following HTML structure: (This example shows the "naked" class names for clarity; in reality it would actually render the scoped selectors!)
<header className="MyElement additional-classes">
<h2 className="MyElement-title">Some Title</h2>
</header>