Skip to content

new-AF/simple-merge-class-names

Repository files navigation

simple-merge-class-names

A straightforward utility for merging CSS class names in React (JSX) and other JavaScript projects.

For Production look into https://www.npmjs.com/package/clsx

Table of Contents

The Genocidal Occupation Is Starving Gaza

Palestine is about fundamental non-negotiable human rights. End all financial and diplomatic ties with i*rael.

Installation

pnpm add simple-merge-class-names
yarn add simple-merge-class-names
npm install simple-merge-class-names

Recommended If Using VSCode: Install Prettier Extension

It will nicely format your code especially when you have lots of classes, and will significantly improve your visual experience.

If you have a different IDE use an equivalent auto code formatter tool/extension

Usage

Function Prints console warnings Activates debugger
mergeClassNames
mergeClassNamesDebugger

Example:

import { mergeClassNames } from "simple-merge-class-names";

const Component = ({ condition }) => {
    return (
        <div
            className={mergeClassNames(
                "app",
                condition ? "min-h-dvh" : false,
                "grid",
                "grid-rows-[auto_1fr_auto]",
                "outline"
            )}
        >
            Hello, world!
        </div>
    );
};

TypeScript Definitions

export declare const mergeClassNames: (
    ...args: (string | false)[]
) => string | false;

export declare const mergeClassNamesDebugger: (
    ...args: (string | false)[]
) => string | false;

Valid Arguments

Only 2:

  1. Valid strings, not whitespace, of length >= 1

    (As long as you have content in the string you're OK)

  2. false

Example, This is OK:

mergeClassNames(
    "mx-auto",
    "min-dvh    ",
    "   flex",
    "      grid      ",
    "italic     font-bold   ",
    `
        gap-y-4
    `,
    false,
    condition ? "daisy-btn-active" : false
);

Invalid Arguments

Each below argument will be ignored, and cause a Dev Console warning to be printed to alert you:

  • Empty strings: (e.g. "")
  • Whitespace any consecutive combination of the following:
    • new lines,
    • spaces,
    • tabs
    • (e.g. " ", "\n ", " \t \n ", etc.)
  • true
  • undefined
  • null
  • Objects
  • Numbers
  • Big Int
  • Symbols
// Example: These arguments will be **ignored**, and a console.warn will be printed

const someVariable = "";

mergeClassNames(
    someVariable, // empty string
    "   ", // whitespace
    "\n ", // whitespace
    "  \t  \n ", // whitespace
    `           // whitespace
        \n
    `,
    true, // true
    undefined, // undefined
    null, // null
    {
        // object
        name: "value",
        email: "email@example.com",
    },
    123, // number
    123.45 // number
);

screenshot of console.warn warnings because invalid arguments were provided and ignored, so no silent failing

Reason for warnings

  • To avoid silent failures, because you will be pulling your hair asking why a Tailwind class isn't working only to figure out you passed an object, array or an empty string instead of a valid string. (It could also be because of an unsupported class name or typo but this is beyond the scope of this package)

Conditional class inclusion

Use this pattern:

  • condition ? "class-name" : false with false serving as the valid fallback.
  • or condition === true ? "class-name" : false if you want to be specific.

Note: Avoid using the short-circuit implicit syntax like this:

  • condition && "class-name", beside less readable code, it can produce falsy values which will be ignored (e.g. 0, "", undefined, and null).
import { mergeClassNames } from "simple-merge-class-names";

const Component = () => {
    return (
        <div
            className={mergeClassNames(
                "app",
                condition ? "min-h-dvh" : false,
                "grid",
                "grid-rows-[auto_1fr_auto]",
                "outline"
            )}
        >
            Hello, world!
        </div>
    );
};

Return Result

Either:

  1. Valid string, never whitespace, always length >= 1

  2. or false (if all input arguments were invalid)

Chaining

Because of safe return types you can chain calls safely without worrying about warnings or arguments being ignored.

So this is OK:

mergeClassNames(condition ? "disabled" : mergeClassNames(...) )

Usage of Browser Debugger

Once you see warnings in the console, the next step is to use mergeClassNamesDebugger

  1. Enable Debugger
    • For chromium-based browsers it's On by default and you don't need to do anything AFAIK.
    • For Firefox: Open Developer Tools:
      • Make Sure Debugger (tab) -> Pause on debugger statement is ticked.
      • Keep Dev Tools open. screenshot of Firefox Debugger section with Pause on debugger statement ticked on
  • Use import {mergeClassNamesDebugger as mergeClassNames} to debug the entire file, and keep the rest intact.

    import { mergeClassNamesDebugger as mergeClassNames } from "simple-merge-class-names";
    
    const Component = ({ condition }) => {
        return (
            <div
                className={mergeClassNames(
                    "app",
                    condition ? "min-h-dvh" : false,
                    "grid",
                    "grid-rows-[auto_1fr_auto]",
                    "outline"
                )}
            >
                Hello, world!
            </div>
        );
    };
  • or call mergeClassNamesDebugger directly.

    import { mergeClassNamesDebugger } from "simple-merge-class-names";
    
    const Component = ({ condition }) => {
        return (
            <div
                className={mergeClassNamesDebugger(
                    "app",
                    condition ? "min-h-dvh" : false,
                    "grid",
                    "grid-rows-[auto_1fr_auto]",
                    "outline"
                )}
            >
                Hello, world!
            </div>
        );
    };
  • Refresh the page, the debugger should connect:

    • Navigate to the Call stack
    • Click the function/component right before mergeClassNamesDebugger

    screenshot of Firefox debugger active because of undefined invalid class name argument

  • Hover over the arguments, one or several should be invalid: screenshot of Firefox debugger active because of undefined invalid class name argument

VSCode Workflow To Minimize Typing Strain

Use single quotes around class names, and activate Prettier which will neatly format and arrange the classes.

  • Install Prettier

  • Enable Editor: Word Wrap:

    • Open Settings (UI)Editor: Word Wrapon
    • or User Settings (JSON) and add this entry "editor.wordWrap": "on"
  • Use single quotes (') around class names

  • Save the file

Before and after:

Screenshot of code before Prettier neatly formats code

Testing Source Code

This project uses Vitest as the test runner for fast and modern testing.

Run Once

git clone https://github.com/new-AF/simple-merge-class-names
cd simple-merge-class-names
pnpm test

Run Watch Mode

pnpm test:watch

License

This project is licensed under the AGPL-3.0 License. See LICENSE.txt for full details.


Enjoy 😉

About

Yet another CSS classes merger, but with more readable source code

Resources

License

Stars

Watchers

Forks