Skip to content

Repository files navigation

UI Consistency logo

eslint-plugin-ui-consistency

UI consistency guardrails for shadcn/ui, Radix UI, and Tailwind CSS projects.

中文说明 · GitHub Pages · Rules Reference

ESLint plugin for enforcing UI consistency in shadcn/ui + Radix UI + Tailwind CSS projects.

Overview

This plugin helps maintain design system consistency by enforcing rules around component usage, styling patterns, and design token adherence. It's specifically designed for projects using shadcn/ui, Radix UI primitives, and Tailwind CSS.

Installation

pnpm add -D @aicode-nexus/eslint-plugin-ui-consistency

Quick Start

Using Preset Configurations

The easiest way to get started is using one of the preset configurations:

Recommended preset (balanced approach):

// eslint.config.js
import { recommended } from "@aicode-nexus/eslint-plugin-ui-consistency/presets";

export default [
  recommended,
  // ... your other config
];

Strict preset (all rules enabled):

// eslint.config.js
import { strict } from "@aicode-nexus/eslint-plugin-ui-consistency/presets";

export default [
  strict,
  // ... your other config
];

Manual Configuration

For custom rule configuration:

// eslint.config.js
import { uiConsistencyPlugin } from "@aicode-nexus/eslint-plugin-ui-consistency";

export default [
  {
    plugins: {
      "ui-consistency": uiConsistencyPlugin,
    },
    rules: {
      "ui-consistency/no-raw-interactive-elements": "error",
      "ui-consistency/no-raw-palette-utility": "error",
      // ... configure other rules as needed
    },
  },
];

Rules

This plugin provides 11 rules to enforce UI consistency:

1. no-raw-interactive-elements

Disallows raw HTML interactive elements (button, input, textarea, select, a, label, form, fieldset) in favor of UI library primitives.

Why: Ensures consistent styling, accessibility, and behavior across all interactive elements.

// ❌ Bad
<button onClick={handleClick}>Click me</button>

// ✅ Good
<Button onClick={handleClick}>Click me</Button>

2. no-primitive-classname

Disallows className overrides on governed UI components to maintain design system integrity.

Why: Prevents style drift by ensuring components use their built-in styling patterns.

3. no-shell-only-component-usage

Restricts shell-only components (like SidebarFooter, SidebarGroup) to layouts and patterns only.

Why: Prevents misuse of layout-specific components in business logic pages.

4. no-raw-palette-utility

Disallows raw Tailwind color palette utilities (e.g., bg-blue-500, text-red-600) in favor of design tokens.

Why: Enforces semantic color usage and ensures consistent theming.

// ❌ Bad
<div className="bg-blue-500 text-white">Content</div>

// ✅ Good
<div className="bg-primary text-primary-foreground">Content</div>

5. no-forbidden-ui-import-path

Prevents importing from legacy or internal UI paths that bypass official exports.

Why: Ensures stable imports and prevents breaking changes from internal refactoring.

6. no-button-icon-classname

Disallows unnecessary className on icons inside Button components.

Why: Button components already handle icon sizing and spacing through [&_svg] styles.

// ❌ Bad
<Button>
  <Icon className="h-4 w-4 mr-2" />
  Click me
</Button>

// ✅ Good
<Button>
  <Icon />
  Click me
</Button>

7. no-arbitrary-utility

Disallows arbitrary Tailwind utilities (e.g., bg-[#ff0000], text-[14px]) that bypass design tokens.

Why: Maintains design system consistency and prevents one-off values.

// ❌ Bad
<div className="bg-[#3b82f6] text-[14px]">Content</div>

// ✅ Good
<div className="bg-primary text-sm">Content</div>

8. no-hardcoded-z-index

Disallows arbitrary z-index values in favor of predefined layers.

Why: Prevents z-index conflicts and maintains a clear stacking context hierarchy.

// ❌ Bad
<div className="z-[9999]">Modal</div>

// ✅ Good
<div className="z-50">Modal</div>

9. no-dark-mode-hardcode

Disallows dark mode implementations using raw color palette utilities.

Why: Design tokens handle dark mode automatically through CSS variables.

// ❌ Bad
<div className="bg-white dark:bg-gray-900">Content</div>

// ✅ Good
<div className="bg-background">Content</div>

10. no-inconsistent-spacing

Disallows arbitrary spacing values that bypass the design system's spacing scale.

Why: Maintains consistent spacing throughout the application.

// ❌ Bad
<div className="p-[13px] m-[7px]">Content</div>

// ✅ Good
<div className="p-3 m-2">Content</div>

11. prefer-composition-import

Enforces importing composition components from the correct path.

Why: Maintains clear separation between primitive and composition components.

// ❌ Bad
import { DataTable } from "@your-org/ui/components/data-table";

// ✅ Good
import { DataTable } from "@your-org/ui/compositions/";

Preset Configurations

Recommended

Balanced configuration suitable for most projects:

  • no-raw-interactive-elements (error)
  • no-raw-palette-utility (error)
  • ⚠️ no-arbitrary-utility (warn)
  • no-button-icon-classname (error)
  • ⚠️ no-hardcoded-z-index (warn)

Strict

All rules enabled for maximum consistency:

  • All rules from recommended
  • no-primitive-classname (error)
  • no-shell-only-component-usage (error)
  • no-forbidden-ui-import-path (error)
  • no-arbitrary-utility (error - upgraded from warn)
  • no-hardcoded-z-index (error - upgraded from warn)
  • ⚠️ no-dark-mode-hardcode (warn)
  • ⚠️ no-inconsistent-spacing (warn)
  • prefer-composition-import (error)

Configuration Examples

Gradual Adoption

Start with warnings and gradually increase strictness:

import { uiConsistencyPlugin } from "@aicode-nexus/eslint-plugin-ui-consistency";

export default [
  {
    plugins: {
      "ui-consistency": uiConsistencyPlugin,
    },
    rules: {
      "ui-consistency/no-raw-interactive-elements": "warn",
      "ui-consistency/no-raw-palette-utility": "warn",
      "ui-consistency/no-arbitrary-utility": "warn",
    },
  },
];

Per-Directory Configuration

Apply stricter rules to new code:

import { recommended, strict } from "@aicode-nexus/eslint-plugin-ui-consistency/presets";

export default [
  recommended, // Default for all files
  {
    files: ["src/features/new-dashboard/**/*.tsx"],
    ...strict, // Stricter rules for new features
  },
];

Contributing

Contributions are welcome! Please feel free to submit issues or pull requests.

License

MIT

Related Projects

  • shadcn/ui - Re-usable components built with Radix UI and Tailwind CSS
  • Radix UI - Unstyled, accessible components
  • Tailwind CSS - Utility-first CSS framework

About

ESLint plugin for enforcing UI consistency in shadcn/ui + Radix UI + Tailwind CSS projects

Resources

Contributing

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages