Meet npm Pro: unlimited public & private packages + package-based permissions.Learn more »

@taktikal/classnames

2.0.0 • Public • Published

@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
  },
  // ...
});

Keywords

none

Install

npm i @taktikal/classnames

DownloadsWeekly Downloads

14

Version

2.0.0

License

ISC

Unpacked Size

31.1 kB

Total Files

20

Last publish

Collaborators

  • avatar
  • avatar