@helal_aytah/classname_builder
v1.0.16
Published
Headless, zero-dependency TypeScript className builder for the web. Accepts strings, nested arrays and conditional object maps, preserves insertion order and returns a deduplicated space-separated class string. Exports both ESM and CJS builds.
Downloads
40
Maintainers
Readme
@helal_aytah/classname_builder
A headless, zero-dependency TypeScript className builder for the web. It accepts strings, nested arrays, and conditional object maps, returning a deduplicated, space-separated class string. It's built to be robust, lightweight, and easy to integrate into any modern web project.
This utility is perfect for component-based frameworks like React, Next.js, Vue, or Svelte, even vanilla javascript where dynamic and conditional class names are a common requirement.
Features
- Tiny & Zero-Dependency: Adds minimal overhead to your project.
- Fully Typed: Written in TypeScript for superior autocompletion and type safety.
- Flexible API: Effortlessly handles strings, objects, arrays, and any nested combination.
- Smart Deduplication: Automatically removes duplicate class names.
- Modern Module Support: Exports both ESM (
import) and CJS (require) builds. - Falsy Handling: Intelligently ignores
null,undefined, andfalseto simplify conditional logic.
Installation
Install the package using your favorite package manager:
# Using npm
npm install @helal_aytah/classname_builder
# Using yarn
yarn add @helal_aytah/classname_builder
# Using pnpm
pnpm add @helal_aytah/classname_builderUsage Examples
1. ES Modules (React, Vue, Svelte, etc.)
This is the most common use case in modern frontend development.
import { classNameBuilder } from "@helal_aytah/classname_builder";
const MyButton = ({ isPrimary, isActive, children }) => {
const classes = classNameBuilder("btn", isPrimary && "btn-primary", {
active: isActive,
disabled: !isActive,
});
// If isPrimary=true and isActive=true, classes will be: "btn btn-primary active"
// If isPrimary=false and isActive=false, classes will be: "btn disabled"
return <button className={classes}>{children}</button>;
};2. CommonJS (Node.js / Legacy Build Systems)
For environments that use the require syntax, the package works out of the
box.
// In a file like `server-component.js` or a script running in Node.js
const { classNameBuilder } = require("@helal_aytah/classname_builder");
// --- Example 1: Basic usage ---
const buttonClasses = classNameBuilder("btn", "btn-large", "btn-primary");
console.log(buttonClasses);
// Output: "btn btn-large btn-primary"
// --- Example 2: Conditional classes ---
const isLoggedIn = false;
const hasError = true;
const userStatusClasses = classNameBuilder({
"status-online": isLoggedIn,
"status-offline": !isLoggedIn,
"has-error": hasError,
});
console.log(userStatusClasses);
// Output: "status-offline has-error"
// --- Example 3: Mixed array ---
const cardClasses = classNameBuilder(
"card",
["card-body", { "shadow-lg": true, rounded: false }],
isLoggedIn ? "user-card" : null,
);
console.log(cardClasses);
// Output: "card card-body shadow-lg"3. Vanilla JS (Directly in the Browser)
You can use the library directly in an HTML file by including a UMD (Universal Module Definition) build. This is useful for demos, prototyping, or legacy projects.
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vanilla JS ES Module Example</title>
<style>
body {
font-family: sans-serif;
}
.btn {
padding: 10px 20px;
border: 1px solid #ccc;
border-radius: 5px;
cursor: pointer;
transition: all 0.2s ease-in-out;
}
.btn-primary {
background-color: #007bff;
color: white;
border-color: #007bff;
}
.active {
box-shadow: 0 0 8px rgba(0, 123, 255, 0.8);
transform: scale(1.05);
}
.disabled {
opacity: 0.5;
cursor: not-allowed;
}
</style>
</head>
<body>
<h1>Vanilla JS ClassName Builder (from CDN)</h1>
<button id="myButton">Click Me</button>
<p>Current classes: <code id="class-display"></code></p>
<!--
The script tag now has type="module".
This allows us to use the 'import' keyword directly in the browser.
-->
<script type="module">
// Import the function directly from the JSDelivr CDN URL
import { classNameBuilder } from "https://cdn.jsdelivr.net/npm/@helal_aytah/classname_builder/dist/index.js";
const button = document.getElementById("myButton");
const classDisplay = document.getElementById("class-display");
let isActive = true;
let isPrimary = true;
function updateButtonClasses() {
const classes = classNameBuilder("btn", {
"btn-primary": isPrimary,
active: isActive,
disabled: !isActive, // Add a disabled class when not active
});
// Apply the generated classes to the button element
button.className = classes;
// Display the current class string
classDisplay.textContent = `"${classes}"`;
}
// Set the initial state of the button
updateButtonClasses();
// Add a click event listener to toggle the button's state
button.addEventListener("click", () => {
isActive = !isActive; // Toggle the active state
updateButtonClasses();
});
</script>
</body>
</html>API Reference
The package exports a primary function classNameBuilder, a ClassNameBuilder
class, and the TClassNameValue type.
classNameBuilder(...args)
This is the main function you'll use. It's a convenience wrapper around
ClassNameBuilder.build.
- Parameters:
...args: TClassNameValue[]— A spread of values to be processed. - Returns:
string | undefined— A space-separated string of unique class names, orundefinedif no valid classes are generated.
The TClassNameValue type can be any of the following:
| Type | Description | Example |
| ------------------------------ | -------------------------------------------------------------------------------- | --------------------------------------------- |
| string | A single class or multiple space-separated classes. | 'btn btn-primary' |
| object | A map where keys are class names and truthy values cause the key to be included. | { active: true, 'text-red': hasError } |
| Array<TClassNameValue> | An array containing any valid TClassNameValue type. Arrays can be nested. | ['base', { conditional: true }, ['nested']] |
| null \| undefined \| boolean | Falsy values that are ignored, allowing for simple inline conditionals. | isLoggedIn && 'user-active' |
Development Scripts
This project uses pnpm as its package manager and includes several scripts for
development:
pnpm dev: Run Vitest in watch mode for continuous testing.pnpm test: Run the full test suite once.pnpm lint: Lint and format the codebase.pnpm bench: Run benchmark tests.pnpm coverage: Generate a test coverage report.pnpm build: Create a production-ready build in thedist/folder.pnpm ci: Run the full continuous integration pipeline (lint, test, build).
Contributing
Contributions are welcome! If you have a feature request, bug report, or want to improve the code, please follow these steps:
- Fork the repository on GitHub.
- Clone your fork to your local machine.
- Create a new branch for your changes:
git checkout -b my-feature-branch. - Make your changes and commit them with a clear message.
- Push your branch to your fork on GitHub.
- Open a Pull Request to the main repository.
Please ensure your code adheres to the existing style and that the pnpm ci
command passes successfully.
License
This project is licensed under the MIT License. See the LICENSE file for details.
Author
- Helal Aytah (@virusoo7)
