npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

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