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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@humaan/css-drift-lint

v0.2.1

Published

Lint drift between stylesheet class definitions and their references.

Readme

CSS Drift Lint

CI

Keep CSS Module class definitions and their JavaScript or TypeScript consumers in sync.

  • Reports class references that a module does not export.
  • Reports exported classes with no static consumers.
  • Supports CSS and Sass modules, import aliases, nesting, and configurable class export naming.
  • Runs all diagnostics through one ESLint command.

Install

pnpm add -D @humaan/css-drift-lint eslint

CSS Drift Lint requires Node 20.19 or newer and ESLint 9.15+ within 9.x, or ESLint 10.x. ESLint is the only peer dependency; CSS Drift Lint owns its CSS and SCSS parsers.

Setup

Add the CSS Modules preset to an ESLint flat config. ESLint 9.22 and newer support named plugin configs through extends:

import cssDriftLint from "@humaan/css-drift-lint/eslint";
import { defineConfig } from "eslint/config";

export default defineConfig({
	plugins: { "css-drift-lint": cssDriftLint },
	extends: ["css-drift-lint/css-modules"],
	settings: {
		cssDriftLint: {
			projectRoot: ".",
			aliases: { "@": "src" },
			sassAliases: { "@": "src" },
			localsConvention: "asIs",
			ignoreDirectories: [".turbo", ".vercel", "coverage"],
		},
	},
});

On ESLint 9.15 through 9.21, spread the same preset directly:

import cssDriftLint from "@humaan/css-drift-lint/eslint";

export default [
	...cssDriftLint.configs["css-modules"],
	{
		settings: {
			cssDriftLint: {
				projectRoot: ".",
				aliases: { "@": "src" },
				sassAliases: { "@": "src" },
				localsConvention: "asIs",
				ignoreDirectories: [".turbo", ".vercel", "coverage"],
			},
		},
	},
];

The preset applies the JavaScript and TypeScript rules to module importers, strict CSS parsing to *.module.css, and tolerant SCSS parsing to *.module.scss. It enables only CSS Drift Lint rules, all at warning severity; it does not enable or disable general @eslint/css rules. Global styles and side-effect stylesheet imports are outside the preset's scope.

Rules

css-drift-lint/no-undefined-class

Reports static class references that are not exported by the imported module. It also reports modules that cannot be resolved or analyzed, and warns when composes prevents complete analysis.

import styles from "./Card.module.scss";

styles.card;
styles["card--active"];
const { cardTitle } = styles;
styles.missing; // warning

css-drift-lint/no-unanalyzable-class-usage

Reports access that prevents the stylesheet rule from proving which classes are used. This includes computed keys, interpolated template literals, aliases, calls, spreads, returns, exports, and object rest destructuring.

styles[key]; // warning
consume(styles); // warning
const copy = { ...styles }; // warning

Use an explicit static map when runtime selection is needed:

const variants = {
	large: styles.large,
	small: styles.small,
};

css-drift-lint/no-unused-class

Reports module classes with no static reference in any JavaScript or TypeScript importer. A module with no importers receives one orphan-module warning.

Dynamic keys and escaped styles objects do not count as static usage, so importer and stylesheet diagnostics remain independent. A module containing composes receives an explicit warning instead of unused-class findings.

Options

Analyzer options are configured once in ESLint's settings.cssDriftLint.

| Option | Default | Description | | --- | --- | --- | | projectRoot | "." | Directory scanned for JS/TS importers. Resolved from ESLint's working directory. | | aliases | {} | Maps source import prefixes to directories relative to projectRoot, for example { "@": "src" }. Prefixes match either exactly or before /. | | sassAliases | {} | Maps Sass @use and @import prefixes to directories relative to projectRoot. Extensionless imports and Sass partial conventions are supported. | | localsConvention | "asIs" | Class export naming: "asIs", "camelCase", or "camelCaseOnly". Match this to the CSS Modules loader configuration. | | ignoreDirectories | [] | Additional directory names excluded anywhere in the project usage scan, for example [".turbo", ".vercel", "coverage"]. |

camelCase accepts both .card-title and cardTitle references. camelCaseOnly accepts only cardTitle. asIs accepts the selector name unchanged. Camel-case conversion uses the same camelcase implementation as css-loader, including underscores, leading separators, case boundaries, and consecutive uppercase characters.

Rule severity is controlled by ESLint ("off", "warn", or "error"). Standard ESLint disable comments work in JavaScript, TypeScript, CSS, and SCSS; CSS Drift Lint adds no custom ignore syntax and provides no autofixes.

When aliases overlap, the longest matching prefix wins. If that target does not exist, resolution continues through less-specific matches.

Analysis

Supported module consumption:

  • Default and namespace imports of *.module.css and *.module.scss.
  • Dot access, string bracket access, static template-literal access, and static object destructuring declarations or assignments, including computed string keys.
  • Transparent TypeScript wrappers such as casts, non-null assertions, satisfies, type assertions, and parentheses.
  • Lexical shadowing of imported style identifiers.
  • Relative imports and configured aliases.

Sass modules are compiled before selectors are collected. The stylesheet currently being linted uses ESLint's in-memory text and Dart Sass source maps for diagnostic locations. Imported Sass dependency modification times and unresolved alias candidates are tracked for importer-side analysis, so cached exports are invalidated when a partial changes or a preferred alias target is created. Directory aliases resolve _index.scss or index.scss rather than the directory itself. Project scans ignore common generated and dependency directories: .git, .local, .next, build, dist, node_modules, and out, plus configured ignoreDirectories. Cross-file usage scans use saved filesystem state.

Named and side-effect module imports are not statically interpreted and do not count as class usage. Type-only imports do not count as runtime consumers.

Current limitations:

  • CSS Modules composes is detected but composition edges are not analyzed.
  • Selector locations generated by complex Sass mixins or interpolation can be approximate.
  • Usage scans are process-cached but still inspect the project source tree for changes.
  • Named CSS Module imports, side-effect imports, global styles, and non-JavaScript consumers are not analyzed.

Architecture

The shipped feature set remains intentionally focused on CSS Modules consumed from JavaScript and TypeScript. Internally, drift evaluation is language-independent:

stylesheet analyzer  -> class definitions
consumer extractor   -> class references
association strategy -> drift groups
drift evaluator      -> findings
linter adapter        -> editor diagnostics

CSS Modules currently derive one drift group per resolved import. Future global CSS, HTML, Blade, PHP, Vue, or Svelte support can add analyzers, extractors, and association strategies without changing drift evaluation or the existing adapter.

These extension interfaces remain private while only one production ecosystem implements them. A public plugin/configuration API will be designed from a second real ecosystem rather than committing now to speculative parser lifecycle, grouping, caching, and source-mapping semantics.

Exports

| Entry point | Purpose | | --- | --- | | @humaan/css-drift-lint/eslint | ESLint flat-config plugin and css-modules preset. |

For VS Code or Cursor, install the ESLint extension and enable it for stylesheet languages:

{
	"eslint.useFlatConfig": true,
	"eslint.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact", "css", "scss"],
	"eslint.workingDirectories": [{ "mode": "auto" }]
}

Development

pnpm install
pnpm test
pnpm check:pack

check:pack builds a real tarball, installs it into clean ESLint 9.15 and ESLint 10 consumers, and runs the stylesheet rule through the exported preset.

Releases use npm trusted publishing through GitHub Actions. See the release guide for the release workflow.

License

MIT