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

@vizlint/eslint-plugin

v0.1.1

Published

The design quality gate for AI-generated frontend code

Readme

@vizlint/eslint-plugin

ESLint catches code bugs. Vizlint catches design bugs.

npm version License: MIT

14 ESLint rules that catch design quality violations in AI-generated frontend code — arbitrary colors, inconsistent spacing, missing responsive breakpoints, accessibility gaps, and more. Auto-fix support for 11 rules. Works with React, Vue, Svelte, Angular, and plain HTML.

Validated on 7 real-world projects (4,061 files) with 0% false positive rate and 0 crashes.

Renamed in v0.1.1 — this package was previously published as eslint-plugin-vizlint. The old name is deprecated; install @vizlint/eslint-plugin instead. See the migration note.

Installation

npm install -D @vizlint/eslint-plugin
# or
pnpm add -D @vizlint/eslint-plugin

Requirements: ESLint v10+, Node.js v20+

Optional framework parsers:

# Vue / Nuxt
pnpm add -D vue-eslint-parser
# Angular
pnpm add -D @angular-eslint/template-parser
# Svelte
pnpm add -D svelte-eslint-parser

Quick Start

// eslint.config.js (flat config only — no legacy .eslintrc)
import vizlint from '@vizlint/eslint-plugin';

export default [
  vizlint.configs.recommended,  // all rules at 'warn', dark-mode-coverage off
  // or
  vizlint.configs.strict,       // all rules at 'error'
];

Rules (14)

| Rule | Category | Description | Fixable | Default | |------|----------|-------------|:-------:|---------| | no-arbitrary-spacing | Spacing | Disallow arbitrary spacing values | Yes | warn | | no-arbitrary-colors | Colors | Disallow arbitrary color values | Yes | warn | | no-arbitrary-typography | Typography | Disallow arbitrary font/leading/tracking values | Yes | warn | | no-arbitrary-zindex | Consistency | Disallow arbitrary z-index values | Yes | warn | | no-inline-styles | Design System | Disallow inline style attributes | Yes | warn | | no-magic-numbers-layout | Layout | Disallow arbitrary numbers in grid/flex | Yes | warn | | consistent-component-spacing | Consistency | Detect spacing divergence across components | Yes | warn | | consistent-border-radius | Consistency | Detect mixed border-radius values | Yes | warn | | responsive-required | Responsive | Require breakpoints on fixed-width containers | No | warn | | missing-states | Consistency | Flag missing hover/focus/disabled states | Yes | warn | | dark-mode-coverage | Dark Mode | Flag missing dark mode variants | No | off | | a11y-color-contrast | Accessibility | Check WCAG AA contrast ratios | Yes | warn | | image-alt-text | Accessibility | Flag missing or meaningless alt text | Yes | warn | | max-component-lines | Code Quality | Flag overly large components | No | warn |


no-arbitrary-spacing

Detects arbitrary spacing values and auto-fixes to the nearest Tailwind spacing scale entry. Covers padding, margin, gap, positioning, and sizing utilities.

Detects: p-[13px] m-[7px] gap-[20px] w-[200px] h-[48px]

// Bad
<div className="p-[13px] m-[7px] gap-[20px]" />
// Good (auto-fixed)
<div className="p-3 m-2 gap-5" />

Options: allowlist, customScale


no-arbitrary-colors

Detects hex, rgb/rgba, hsl/hsla arbitrary colors in Tailwind classes. Suggests the nearest design token and auto-fixes. CSS variable references (var(--...)) are allowed by default.

Detects: bg-[#FF0000] text-[rgb(59,130,246)] border-[hsl(220,90%,56%)]

// Bad
<div className="bg-[#1a5276] text-[#fff]" />
// Good (auto-fixed)
<div className="bg-primary text-white" />

Options: allowlist, customTokens, allowCssVariables (default: true)


no-arbitrary-typography

Detects arbitrary font-size, font-weight, line-height, and letter-spacing values. Auto-fixes to nearest Tailwind type scale entry.

Detects: text-[17px] font-[450] leading-[24px] tracking-[0.05em]

// Bad
<div className="text-[17px] font-[450] leading-[24px]" />
// Good (auto-fixed)
<div className="text-base font-normal leading-6" />

Options: allowlist, customScale (fontSize, fontWeight, leading, tracking)


no-arbitrary-zindex

Detects arbitrary z-index values. Auto-fixes to the nearest Tailwind z-index scale entry.

Detects: z-[999] z-[100] z-[1]

// Bad
<div className="z-[999]" />
// Good (auto-fixed)
<div className="z-50" />

Options: allowlist, customScale


no-inline-styles

Flags style={{}} attributes. Inline styles bypass design system tokens and break consistency. Dynamic template literals are allowed by default.

Detects: style={{ color: 'red' }} style={{ padding: '10px' }}

// Bad
<div style={{ color: 'red', padding: '10px' }} />
// Good
<div className="text-red-500 p-2.5" />

Options: allowDynamic (default: true), allowlist


no-magic-numbers-layout

Flags arbitrary numbers in grid and flex layout properties. Auto-fixes to Tailwind utilities. Skips CSS functions (minmax, repeat, fit-content).

Detects: grid-cols-[200px_1fr] basis-[200px] order-[3]

Options: allowlist


consistent-component-spacing

Detects spacing divergence across components in the same file. If most components use p-4, flags the one using p-6.

Options: threshold (minimum occurrences to establish pattern)


consistent-border-radius

Detects mixed rounded-* values across same-type components.

Options: threshold


responsive-required

Flags fixed-width layout containers (w-[Npx], max-w-[Npx], min-w-[Npx]) that lack responsive breakpoint variants. Not auto-fixable — adding responsive variants requires design decisions.

// Bad
<div className="w-[800px]" />
// Good
<div className="w-[800px] sm:w-full md:w-auto" />

Options: requiredBreakpoints (default: ['sm', 'md']), iconSizeThreshold (default: 64), ignoredPrefixes


missing-states

Flags interactive elements (buttons, links, inputs) missing hover, focus, or disabled state styling.

Detects: <button className="bg-blue-500"> without hover:, focus:, or disabled: variants


dark-mode-coverage

Flags elements with color/background utilities that lack corresponding dark: variants. Disabled by default in recommended config — enable for projects using dark mode.


a11y-color-contrast

Checks WCAG AA color contrast ratios between text and background colors. Works with Tailwind color utilities.


image-alt-text

Flags <img> elements without alt attribute or with meaningless alt text like "image", "photo", "picture".

// Bad
<img src="hero.jpg" />
<img src="hero.jpg" alt="image" />
// Good
<img src="hero.jpg" alt="Team working together in the office" />

max-component-lines

Flags components exceeding a configurable line count (default: 300). Large components are harder to maintain.

Options: maxLines (default: 300)


Custom Configuration

// eslint.config.js
import vizlint from '@vizlint/eslint-plugin';

export default [
  {
    plugins: { vizlint },
    rules: {
      'vizlint/no-arbitrary-colors': ['error', {
        customTokens: { '#1A5276': 'primary' },
      }],
      'vizlint/no-arbitrary-spacing': ['warn', {
        allowlist: ['p-[18px]'],
      }],
      'vizlint/no-arbitrary-typography': 'warn',
      'vizlint/responsive-required': ['warn', {
        requiredBreakpoints: ['sm', 'md'],
      }],
      'vizlint/dark-mode-coverage': 'warn',  // enable for dark mode projects
      'vizlint/max-component-lines': ['warn', { maxLines: 250 }],
    },
  },
];

Design System Configuration

Create .vizlintrc.json to define your design system tokens:

{
  "designSystem": {
    "colors": {
      "primary": "#1A5276",
      "secondary": "#27AE60",
      "accent": "#F39C12"
    },
    "spacing": {},
    "fonts": {
      "body": "Inter",
      "heading": "DM Sans",
      "code": "JetBrains Mono"
    }
  },
  "tailwind": {
    "autoImport": true
  }
}

Framework Support

| Framework | Parsing | Rules | Auto-fix | Validated | |-----------|:-------:|:-----:|:--------:|:---------:| | React / Next.js | Yes | All 14 | Yes | 4 projects | | Vue / Nuxt | Yes | All 14 | Yes | 1 project | | Svelte | Yes | All 14 | Yes | Parser ready | | Angular | Yes | 7/14* | No** | 1 project | | Plain HTML | Yes | All 14 | Yes | Not yet |

* JSX-specific rules (a11y-color-contrast, missing-states, consistent-component-spacing, max-component-lines, responsive-required) produce 0 violations on Angular templates — they require JSX AST patterns.

** Angular template parser nodes lack range property. Violations are reported but auto-fix is skipped.

Validation Results

Tested on 7 real-world open-source projects:

| Project | Framework | Files | Violations | False Positives | |---------|-----------|------:|----------:|-----------:| | Cal.com | Next.js, Tailwind | 1,700 | 1,222 | 0 | | Dub.co | Next.js 15, shadcn/ui | 1,838 | 1,932 | 0 | | Elk | Vue 3, Nuxt | 259 | 0 | 0 | | Vintor | Angular 21, Tailwind v4 | 74 | 3 | 0 | | saas-starter | Next.js 15, shadcn/ui | 23 | 51 | 0 | | taxonomy | Next.js 13, shadcn/ui | 94 | 71 | 0 |

Cumulative: 4,061 files, 3,395 violations, 0 false positives, 0 crashes.

Migration from eslint-plugin-vizlint

In v0.1.1, this package was renamed from eslint-plugin-vizlint to @vizlint/eslint-plugin to align with the rest of the @vizlint/* workspace (@vizlint/cli, @vizlint/mcp, @vizlint/shared). Same code, same rules, same config — only the package name and import specifier changed.

Migration steps:

# 1. Uninstall the old package
npm uninstall eslint-plugin-vizlint

# 2. Install the new one
npm install -D @vizlint/eslint-plugin
// 3. Update your eslint.config.js import
- import vizlint from 'eslint-plugin-vizlint';
+ import vizlint from '@vizlint/eslint-plugin';

Nothing else changes:

  • The plugin shorthand stays vizlint (e.g. 'vizlint/no-arbitrary-colors')
  • All rule names, options, and presets are identical
  • Auto-fix output is identical
  • .vizlintrc.json schema is unchanged

The old [email protected] will be deprecated on npm with a redirect message pointing here.

License

MIT