@taktikal/classnames
v2.0.0
Published
Classname utility for css modules based on the BEM naming convention
Downloads
3
Readme
@taktikal/classnames
This package follows the BEM naming convention for css loosely with utility classes mixed in for convenience. This package was created to make writing BEM classNames a bit faster and cleaner.
Input:
import classNames from "@taktikal/classnames";
import styles from "scss/Button.scss";
const s = classNames(styles);
s("button", {
modifiers: {
primary: true,
accent: false,
disabled: true,
},
);
Output in development:
Button___button--[hash] Button___button--primary--[hash] Button___button--disabled--[hash]
Output in production:
[hash] [hash] [hash]
Configuration
Short version
Set localIdentName
to [name]___[local]--[hash:base64:5]
in dev and [hash:base64:5]
in prod.
Optional configuration:
// _app.tsx
import { config } from "@taktikal/classnames";
config({ /* ... */ });
The options object for config
:
interface {
utilityStyles?: Styles;
logWarnings?: boolean;
onWarn?: (warning: string) => void;
}
Long version
Before you can use this package, the localIdentName
for your css loader should look like this:
{
localIdentName: process.env.NODE_ENV === "production"
? "[hash:base64:5]"
: "[name]__[local]--[hash:base64:5]",
}
[name]
is the filename, [local]
is the classname and [hash]
is well... a hash.
An example for the file
/* Button.scss */
.btn { /* ... */ }
.btn--primary { /* ... */ }
The resulting classNames would look like
{
"btn": "Button___btn--[hash]",
"btn--primary": "Button___btn--primary--[hash]"
}
The next.config.js
with SCSS and TypeScript should look something like this:
const withSass = require("@zeit/next-sass");
const withTypescript = require("@zeit/next-typescript");
const ForkTsCheckerWebpackPlugin = require("fork-ts-checker-webpack-plugin");
module.exports = withTypescript(withSass({
cssModules: true,
cssLoaderOptions: {
importLoaders: 1,
localIdentName: process.env.NODE_ENV === "production"
? "[hash:base64:5]"
: "[name]___[local]--[hash:base64:5]",
},
webpack: (config, options) => {
// Add TypeScript type checking in terminal
if (options.isServer) {
config.plugins.push(new ForkTsCheckerWebpackPlugin());
}
// ...
},
}));
Usage
classNames
import classNames from "@taktikal/classnames";
import styles from "src/scss/.../File.scss";
const s = classNames(styles);
...
<button
className={s({
name: "btn",
modifiers: {
primary: this.props.primary,
accent: this.props.accent,
disabled: this.props.disabled,
},
})}
utils: ["f16", "mb-40"]
>
{children}
</button>
// Or
<button
className={s("btn", {
modifiers: {
primary: this.props.primary,
accent: this.props.accent,
disabled: this.props.disabled,
},
})}
utils: ["f16", "mb-40"]
>
{children}
</button>
If you are only using the className and modifiers with no utilities, you can use this shorthand.
<button className={s("btn", { primary: this.props.primary })}>
{children}
</button>
If there are no modifiers or utility classes being used, you can just pass a string like this
<h1 className={s("card__title")}>Title</h1>
utilityClass
If you want to use utility classes, they have to be created in the format .u-{className} { ... }
.u-colorPrimary {
color: $color-primary;
}
Then you will have to let @taktikal/classNames
know of your utility classes, you should do this in the entry to your app (e.g. _app.js
or index.js
).
import { config } from "@taktikal/classnames";
import utilStyles from "~scss/Utils.scss";
config({
utilityStyles: utilStyles,
// ...
});
The utilClass
function then takes in an array of utilities without the u-
part of the className.
import { utilClass } from "src/utils/classNames";
<h1 className={utilClass(["mb-30", "colorPrimary"])}>Title</h1>
If the class only uses one utility class, you can use the shorthand
utilClass("mb-30");
The utility classes get applied first, then the className, then the modifiers.
For example:
<button
className={s({
name: "btn",
modifiers: { primary: true },
utils: ["mb-0"]
})}
>
{children}
</button>
The className in the code above would be something like
Utils__u-mb-0--[hash] Button__button--[hash] Button__button--primary--[hash]
Utility classes should be used when adding one or two simple things like text color, margin or padding. Avoid using utility classes heavily with BEM classNames as they can create a lot of noise.
Warnings
This structure allows us to do things like runtime validation of classNames in development. Here are some examples of warnings:
Button.scss: ClassName not found.
The className: 'button' does not exist in file 'Button.scss'.
Did you mean: 'btn'?
Button.scss: Modifier not found
The modifier: 'disabled' does not exist on class 'btn'.
A component reload (HMR or manual) will be required if the CSS is updated.
Utility class not found
The utility class 'titleBorde' does not exist.
Did you mean: 'titleBorder'?
You can enable them in the config:
import { config } from "@taktikal/classnames";
config({
logWarnings: true,
// ...
});
If you want to do something with the warnings, you can pass in a callback function
import { config } from "@taktikal/classnames";
config({
logWarnings: true,
onWarn: warning => {
// Do stuff with warning
},
// ...
});