GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — GitHub - Infinitode/TailClass: An open-source, zero-dependency Tailwind CSS auto-classer for Node.js.

BranchesTags
Open more actions menu

Latest commit

 

History

19 Commits
19 Commits

Folders and files

NameName
Last commit message
Last commit date
.github
.github
 
 
bin
bin
 
 
examples
examples
 
 
schema
schema
 
 
scripts
scripts
 
 
src
src
 
 
test
test
 
 
types
types
 
 
.gitignore
.gitignore
 
 
CHANGELOG.md
CHANGELOG.md
 
 
CODE_OF_CONDUCT.md
CODE_OF_CONDUCT.md
 
 
CONTRIBUTING.md
CONTRIBUTING.md
 
 
LICENSE
LICENSE
 
 
README.md
README.md
 
 
SECURITY.md
SECURITY.md
 
 
package.json
package.json
 
 

Repository files navigation

TailClass

npm version npm provenance Node.js Version License: MIT Modified Dependencies Tests GitHub stars GitHub issues

An open-source, zero-dependency Tailwind CSS auto-classer for Node.js. TailClass scans an entire site or web application for repeated Tailwind utility layouts, assigns smart semantic names based on both what elements look like and where they live in the DOM (.navbar, .navbar-brand, .nav-link, .btn-primary, .btn-outline, .card, .grid-3col, .badge-success, .form-input, .modal-dialog, .site-footer, …), generates a centralized stylesheet (tailclass.css), and rewrites your templates so you can update styles across your entire site from one place.

Don't want automatic names on certain patterns? Run Class List Mode (tailclass list) to audit every utility and repeated layout across your codebase — complete with class counts, occurrence counts, and exact file/line locations — and type your own names into an auto-generated tailclass.map.json dictionary.

Note

TailClass understands Tailwind CSS v3 and v4 utilities, variant stacks (dark:md:hover:…), arbitrary values (grid-cols-[240px_1fr]), negative & !important modifiers, and canonicalizes token order — so "px-4 py-2 flex items-center" and "flex items-center py-2 px-4" are automatically recognized as the exact same layout. Non-@apply markers (group, peer, dark) and custom JS/BEM hooks (js-*) are preserved inline automatically so nothing breaks at build time.


Before & After

Before (index.html, pricing.html, contact.html):

<nav class="sticky top-0 z-50 flex items-center justify-between border-b border-slate-200 bg-white/90 px-6 py-4 backdrop-blur">
  <a href="/" class="text-xl font-bold tracking-tight text-slate-900">Infinitode</a>
  <ul class="hidden md:flex items-center gap-6">
    <li><a href="/" class="text-sm font-medium text-slate-600 transition hover:text-indigo-600">Home</a></li>
    <li><a href="/pricing" class="text-sm font-medium text-slate-600 transition hover:text-indigo-600">Pricing</a></li>
  </ul>
  <a href="/signup" class="inline-flex items-center justify-center rounded-lg bg-indigo-600 px-4 py-2.5 text-sm font-semibold text-white shadow-sm transition hover:bg-indigo-500">
    Get Started
  </a>
</nav>

<!-- Superset card with extra ring utilities on the featured plan -->
<div class="group js-plan rounded-xl border border-slate-200 bg-white p-6 shadow-sm ring-2 ring-indigo-600">
  ...
</div>

After running npx tailclass apply ./src:

<nav class="navbar">
  <a href="/" class="navbar-brand">Infinitode</a>
  <ul class="nav-menu">
    <li><a href="/" class="nav-link">Home</a></li>
    <li><a href="/pricing" class="nav-link">Pricing</a></li>
  </ul>
  <a href="/signup" class="btn-primary">
    Get Started
  </a>
</nav>

<!-- Shared `.card` extracted; `ring-2 ring-indigo-600` + inline `group` & `js-plan` hooks kept intact! -->
<div class="card ring-2 ring-indigo-600 group js-plan">
  ...
</div>

Centralized tailclass.css (edit once to restyle everywhere):

/* Generated by TailClass v1.0.2 — https://github.com/Infinitode/TailClass */
/* Centralized Tailwind component classes. Edit rules here to update styles site-wide. */

@layer components {
  /* === Navigation & Headers === */
  /* Navigation Bar — 3 occurrences across 3 files (<nav>) */
  .navbar {
    @apply flex sticky top-0 z-50 items-center justify-between px-6 py-4 bg-white/90 border-b border-slate-200 backdrop-blur;
  }

  /* Navigation Brand — 3 occurrences across 3 files (<a>) */
  .navbar-brand {
    @apply font-bold text-slate-900 text-xl tracking-tight;
  }

  /* Desktop Navigation Menu — 2 occurrences across 2 files (<ul>) */
  .nav-menu {
    @apply hidden gap-6 items-center md:flex;
  }

  /* Navigation Link — 6 occurrences across 2 files (<a>) */
  .nav-link {
    @apply font-medium text-slate-600 text-sm transition hover:text-indigo-600;
  }

  /* === Cards & Surface Panels === */
  /* Surface Card — 6 occurrences across 3 files (<article>, <div>) */
  .card {
    @apply p-6 bg-white border-slate-200 border rounded-xl shadow-sm;
  }

  /* === Buttons & Interactive Controls === */
  /* Primary Action Button — 6 occurrences across 4 files (<a>, <button>) */
  .btn-primary {
    @apply inline-flex items-center justify-center px-4 py-2.5 font-semibold text-sm text-white bg-indigo-600 rounded-lg shadow-sm transition hover:bg-indigo-500;
  }
}

Features

  • Smart Visual & Structural Naming — Combines element tags (<nav>, <header>, <aside>, <footer>, <button>, <input>, <dialog>, <table>), ARIA roles, parent/ancestor context, JSX component names, and visual CSS signals (flex/grid geometry, sticky/fixed positioning, spacing scale, border-radius, shadows, colour semantics) to name patterns accurately.
  • Order-Independent Canonicalization — Shuffled utility lists across different files resolve to the same canonical layout key automatically.
  • Smart Subset & Shared-Base Matching — Recognizes when an element uses a repeated component layout plus 1–2 extra contextual utilities (like mt-8 or ring-2 ring-indigo-600), replacing the shared base while keeping element-specific utilities inline. Disable anytime with --no-subset.
  • Class List Mode & Custom Dictionaries — tailclass list generates an editable tailclass.map.json (plus optional .md / .csv reports) detailing total classes, unique utilities, pattern class counts, occurrence counts, and exact file/line numbers. Fill in "customName" on any entry (or pass a simple flat JSON dictionary) to override auto-naming.
  • Includes, Excludes & Granular Rules — Filter files (include / exclude globs), keep specific utilities inline (excludeClasses: ["js-*", "animate-*"]), opt elements out (tc-ignore), filter tags (ignoreTags / onlyTags), tune thresholds (minOccurrences, minClasses, minFiles), and define custom heuristic rules in tailclass.config.json.
  • Multi-Framework Support — Scans HTML, JSX/TSX (including className={...}, template literals `...` without breaking ${...} interpolations, and clsx / cn / twMerge / cva calls), Vue, Svelte, Astro, Blade/PHP, ERB, EJS, Twig, Nunjucks, Liquid, and Jinja.
  • Safe, Incremental & Reversible — Preview diffs with --dry-run, create backups with --backup, re-run idempotently as new pages are added, and undo everything in one command with tailclass revert.
  • Zero Dependencies — Pure Node.js (>= 18), instant startup, dual CommonJS & ES Module exports, and bundled TypeScript .d.ts definitions.

Installation

Install globally or as a dev dependency via npm:

npm install -D tailclass
# or run directly with npx:
npx tailclass scan ./src

Or install directly from GitHub:

git clone https://github.com/Infinitode/TailClass.git
cd TailClass
npm link

Quickstart

1. Scan & Preview Detections (Read-Only)

npx tailclass scan ./src

2. Preview Centralized CSS & File Diffs (Dry Run)

npx tailclass apply ./src --dry-run

3. Extract Centralized CSS & Rewrite Templates

npx tailclass apply ./src -o src/styles/tailclass.css

Then import tailclass.css inside your main Tailwind stylesheet:

/* Tailwind v3 */
@tailwind base;
@tailwind components;
@tailwind utilities;
@import "./tailclass.css";
/* Tailwind v4 */
@import "tailwindcss";
@import "./tailclass.css";

Class List Mode & Custom Name Dictionaries

When you want complete visibility into your site's Tailwind usage — or want to customize class names before rewriting — run Class List Mode:

npx tailclass list ./src --report class-audit.md

This prints a full breakdown in your terminal and generates tailclass.map.json:

{
  "$schema": "https://raw.githubusercontent.com/Infinitode/TailClass/main/schema/tailclass.map.schema.json",
  "version": "1.0.2",
  "summary": {
    "filesScanned": 12,
    "filesWithClasses": 11,
    "totalClassAttributes": 148,
    "totalUtilityTokens": 892,
    "uniqueUtilityTokens": 114,
    "uniqueClassPatterns": 46,
    "extractablePatterns": 14,
    "totalReplacementsPossible": 68
  },
  "mappings": [
    {
      "id": "tc_9a41c20e",
      "autoName": "navbar",
      "customName": "",
      "enabled": true,
      "category": "navigation",
      "label": "Navigation Bar",
      "confidence": 0.96,
      "matchType": "exact",
      "classCount": 12,
      "classes": "flex sticky top-0 z-50 items-center justify-between px-6 py-4 bg-white/90 border-b border-slate-200 backdrop-blur",
      "timesFound": 4,
      "fileCount": 4,
      "elements": ["nav"],
      "files": [
        { "file": "index.html", "count": 1, "lines": [7] },
        { "file": "pricing.html", "count": 1, "lines": [8] }
      ]
    }
  ],
  "utilityInventory": [
    {
      "utility": "flex",
      "timesFound": 42,
      "fileCount": 10,
      "files": ["index.html", "pricing.html", "..."]
    }
  ]
}

Customizing Names

  1. Edit tailclass.map.json:

    • Set "customName": "main-topbar" on any entry to override "autoName".
    • Set "enabled": false on any entry you want left inline.
    • Pass --all (tailclass list ./src --all) to include 1-off patterns in tailclass.map.json — typing a "customName" on a 1-off pattern promotes and extracts it too!
    • Re-running tailclass list automatically merges and preserves your existing "customName" and "enabled" edits.
  2. Or supply a minimal hand-written flat JSON dictionary (custom-dict.json): Keys can be utility strings (in any order), auto-detected names, or pattern IDs:

    {
      "px-6 py-4 flex items-center justify-between bg-white": "site-topbar",
      "btn-primary": "brand-button",
      "nav-link": false
    }
  3. Apply your dictionary:

    npx tailclass apply ./src --dict tailclass.map.json

Tip

If tailclass.map.json exists in your target directory, tailclass apply loads your custom overrides from it automatically! Pass --no-dict if you want to bypass tailclass.map.json and force pure automatic naming.


Configuration, Includes, Excludes & Custom Rules

Scaffold a starter tailclass.config.json with:

npx tailclass init
{
  "$schema": "https://raw.githubusercontent.com/Infinitode/TailClass/main/schema/tailclass.config.schema.json",
  "include": [
    "src/**/*.{html,jsx,tsx,vue,svelte,astro}",
    "pages/**/*.{jsx,tsx}"
  ],
  "exclude": [
    "src/legacy/**",
    "**/*.stories.tsx"
  ],
  "outputCss": "src/styles/tailclass.css",
  "dictionaryFile": "tailclass.map.json",
  "cssFormat": "tailwind-v3",
  "minOccurrences": 2,
  "minClasses": 3,
  "minFiles": 1,
  "subsetMatching": true,
  "classPrefix": "",
  "namingStyle": "kebab",
  "tailwindPrefix": "",
  "excludeClasses": ["js-*", "qa-*"],
  "includeClasses": [],
  "excludePatterns": ["tc-ignore", "no-tailclass"],
  "ignoreTags": ["script", "style", "svg", "path"],
  "onlyTags": [],
  "rules": [
    {
      "name": "brand-pill-cta",
      "category": "controls",
      "matchTag": ["a", "button"],
      "mustInclude": ["rounded-full", "bg-indigo-*"],
      "priority": 100
    }
  ]
}

Configuration Safety

tailclass.config.json travels with the repository, so TailClass treats it as untrusted input:

  • outputCss, dictionaryFile and manifestFile must stay inside the project root when they come from the config file (use -o/--dict on the command line for deliberate out-of-root targets). This is a physical check: every existing path component is resolved with lstat/realpath against the real project root, so a directory symlink cannot redirect a write outside the project. Symlinked directories that resolve inside the root still work. apply additionally refuses to overwrite a scanned source file, or a stylesheet/manifest that it does not own, unless --force is used.
  • Behavior change in v1.0.2: an output path's final component must be a regular file, not a symlink — even when the link points to a file inside the project root, even with --force, and even for an explicit CLI path. 1.0.1 wrote through such links; 1.0.2 fails with Refusing to write ... the target is a symlink ..., and revert --clean never deletes a symlink. Replace the link with a regular file or point outputCss/dictionaryFile/manifestFile at the real file. Intermediate directory symlinks are unaffected.
  • revert --clean only deletes files TailClass can verify it generated; a foreign stylesheet, an unrelated JSON file, a scanned source file or a symlink is left untouched and reported as a warning. --force does not override this.
  • Invalid values fail loudly with the offending key (Invalid exclude[1]: expected a string, received null); unknown keys are reported as warnings on stderr.
  • Class tokens containing ;, {, } or comment terminators are never emitted into @apply — they simply stay inline on the element.
  • .git/, node_modules/ and .tailclass-backup/ are always excluded, even with replaceDefaultExcludes.

See SECURITY.md for the hardening guarantees and disclosure process.

Opting Out Individual Elements Inline

Add tc-ignore (or any token listed in excludePatterns) to keep an element untouched:

<div class="tc-ignore rounded-xl border border-slate-200 bg-white p-6 shadow-sm">
  Utilities on this element will never be replaced.
</div>

CLI Reference

tailclass <command> [directory] [options]
Command Description
tailclass scan [dir] Scan site files and preview auto-detected semantic classes (read-only).
tailclass list [dir] Class List Mode — print pattern & utility counts/locations and write tailclass.map.json.
tailclass apply [dir] Generate centralized CSS (tailclass.css) and rewrite matched utility classes across files.
tailclass revert [dir] Expand centralized TailClass names back into inline Tailwind utilities.
tailclass init [dir] Create a starter tailclass.config.json file.

Key Flags

Flag Description
-c, --config <path> Explicit path to tailclass.config.json.
-o, --output <path> Output path for centralized CSS (default: tailclass.css).
--dict <path> Path to custom mapping dictionary JSON.
--no-dict Ignore existing tailclass.map.json during apply.
--strict-dict Only extract patterns explicitly present in --dict.
-d, --dry-run Preview edits and generated CSS without writing to disk.
--backup Back up original modified files into .tailclass-backup/.
--force Overwrite a stylesheet/manifest that does not look like TailClass output on apply. Never permits --clean to delete foreign files.
-a, --all Include non-repeated / 1-off patterns in list mode.
--report <file> Export Markdown (.md) or CSV (.csv) audit report in list mode.
--format <mode> CSS format: tailwind-v3 (default), tailwind-v4, or plain.
--prefix <str> Prepend prefix to generated classes (e.g. ui- → .ui-navbar).
--style <mode> Naming style: kebab (btn-primary), bem (btn--primary), or camel (btnPrimary).
--tw-prefix <str> Custom Tailwind utility prefix (e.g. tw-).
--min-occurrences <n> Minimum site occurrences to extract a pattern (default: 2).
--min-classes <n> Minimum Tailwind utilities per pattern (default: 3).
--min-files <n> Minimum distinct files a pattern must appear in (default: 1).
--no-subset Disable subset / shared-base layout matching.
-i, --include <globs> Comma-separated file include globs.
-e, --exclude <globs> Comma-separated file exclude globs.
--exclude-classes <globs> Utility globs to keep inline (e.g. "js-*,animate-*").
--include-classes <globs> Restrict extraction to matching utility globs.
--ignore-tags <tags> Comma-separated HTML/JSX tags to ignore.
--only-tags <tags> Restrict auto-classing to specific HTML/JSX tags.
--clean After revert, remove only the stylesheet/manifest TailClass generated (foreign files are kept with a warning).
--json Output structured JSON for CI pipelines and editor integrations.

Programmatic Node.js API

TailClass supports both CommonJS (require) and ES Modules (import), with full TypeScript declarations included.

import {
  scanProject,
  generateClassList,
  applyAutoClasses,
  revertProject,
  rewriteSource,
  canonicalizeClasses,
} from "tailclass";

// 1. Scan & inspect patterns without touching disk
const { analysis } = scanProject("./src", { minOccurrences: 2 });
console.log(analysis.summary);

// 2. Generate Class List dictionary + Markdown report
const { dictionary } = generateClassList("./src", {
  dictionaryFile: "tailclass.map.json",
  reportFile: "docs/tailwind-audit.md",
});

// 3. Apply with custom overrides (or dry-run in memory)
const result = applyAutoClasses("./src", {
  outputCss: "src/styles/tailclass.css",
  cssFormat: "tailwind-v4",
  dictionary: {
    navbar: "app-topbar",
  },
  dryRun: false,
});
console.log(`Saved ${result.totalBytesSaved} bytes across ${result.totalFilesModified} files.`);

// 4. Pure in-memory string transformation (no filesystem needed)
const canonical = canonicalizeClasses("p-6 bg-white rounded-xl border border-slate-200 shadow-sm");
const singleFile = rewriteSource(
  '<div class="p-6 bg-white rounded-xl border border-slate-200 shadow-sm">Hello</div>',
  "Card.html",
  [{ id: "tc_demo", finalName: "card", canonicalKey: canonical }]
);
console.log(singleFile.output); // '<div class="card">Hello</div>'

Built-In Smart Naming Archetypes

TailClass combines DOM context (tag, parent/ancestors, ARIA attributes, input type, PascalCase JSX component names) with visual utility geometry to assign clean names out of the box:

Category Example Auto-Assigned Names Key Visual & Structural Signals
Navigation navbar, site-header, navbar-brand, nav-menu, mobile-menu, nav-link, breadcrumb, sidebar <nav>, <header>, <aside>, role="navigation", sticky top-0 z-50, justify-between, backdrop-blur, child <ul> / <a>
Page Layout page-shell, content-container, hero-section, section-block, site-footer, divider, grid-3col, flex-between, vstack min-h-screen flex-col, max-w-7xl mx-auto, py-24, <footer>, <hr>, grid-cols-*
Controls btn-primary, btn-outline, btn-danger, btn-soft, btn-ghost, icon-btn <button>, type="submit", button-padded <a>, solid brand/danger fills, borders/rings, square icon dimensions
Surfaces card, card-dark, card-elevated, surface-box <article>, <div>, rounded-xl/2xl, border, shadow-*, surface fills & padding
Forms form-layout, form-label, form-input, search-input, form-select, form-textarea, form-checkbox <form>, <label>, <input>, <select>, <textarea>, w-full, focus:ring-*
Data Display badge, badge-success, badge-warning, badge-danger, avatar, icon-box, data-table, table-header-cell, table-cell rounded-full px-2.5 py-0.5 text-xs, semantic colour tints, <img> square rounded-full, <table> / <th> / <td>
Overlays & Feedback modal-backdrop, modal-dialog, alert-warning, alert-danger, spinner, skeleton fixed inset-0 bg-black/50, <dialog>, role="dialog", role="alert", border-l-4, animate-spin, animate-pulse
Typography hero-title, section-title, card-title, eyebrow, lead-text, muted-text, inline-code, code-block, blockquote, text-link <h1>–<h4>, text-5xl font-extrabold, uppercase tracking-wider, <code>, <pre>

When multiple distinct patterns share an archetype, TailClass disambiguates by visual traits first (card vs card-dark vs card-elevated) before falling back to numeric suffixes.


Limitations & Important Notes

Important

  • Tailwind Build Step Required: Generated tailclass.css files use Tailwind's @apply directive. Ensure tailclass.css is imported into your main CSS entrypoint processed by Tailwind CLI, PostCSS, or @tailwindcss/vite.
  • Dynamic Runtime String Concatenation: Glued runtime interpolations like `bg-${color}-500` cannot be statically compiled into @apply; TailClass automatically leaves glued dynamic fragments untouched while still extracting any surrounding static utilities in the same template literal.

Contributing

Contributions, bug reports, and new UI naming heuristics are very welcome! Please read CONTRIBUTING.md and our Code of Conduct before submitting a pull request.

Security

Please review SECURITY.md for responsible disclosure instructions and the hardening guarantees TailClass upholds. Never report security vulnerabilities in public issues.

License

Released under the MIT License (Modified) — see LICENSE for full details. Substantial derivative works must be clearly distinguished from TailClass and distributed under a different name.


Developed and maintained by Infinitode Pty Ltd · Documentation · GitHub · Support on Ko-fi

About

An open-source, zero-dependency Tailwind CSS auto-classer for Node.js.

infinitode.netlify.app

Topics

csscssclassesjavascriptjavascript-librarynodenodejsnpmnpm-clinpm-packagenpmjstailwindcsstailwindcss-v3tailwindcss-v4typescripttypescript-library

Resources

Readme
License

Code of conduct

Code of conduct

Contributing

Contributing

Security policy

Security policy
Activity
Custom properties

Stars

0 stars

Watchers

0 watching

Forks

0 forks
Report repository

Releases

Sponsor this project

Packages

Contributors

Languages

You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.