byronwade/ui

Tooling · Lint

Stay on-system.Every commit.

The on-system lint enforces tokens, primitives, and house utilities in consumer codebases, the same engine that grades the design system itself. One plugin, one CLI, zero raw colors.

01, ESLint plugin

@byronwade/eslint-plugin-ui ships a flat-config-ready recommended preset that enables all five detectors. Works with ESLint 9+ and the flat-config format. It pairs with the AI rule — the rule keeps agents generating on-system code, the lint catches anything that still drifts.

install

eslint.config.js

Options

optiondefaultdescription
maxColorDistance10Maximum ΔE distance before a raw color is flagged as "too far from any token". Lower values are stricter.
offSystemComponents["button", "input", "select"]Native HTML elements for which a design-system primitive exists. Override to expand or reduce the set.

02, CLI

byronwade-lint runs the same detectors outside of ESLint, useful in CI, pre-commit hooks, and editors that don't speak ESLint flat config yet. The --fix flag auto-applies safe codemods where a unique nearest token can be found.

check

auto-fix

03, What it catches

Five detectors cover the most common ways consumer code drifts off-system. Each maps a violation to a specific token or house utility from the foundation scale so the fix is always clear.

  • Raw color

    Hex, rgb(), hsl(), and named colors are replaced with their nearest semantic token (e.g. text-foreground, bg-brand, bg-destructive). The maximum allowed distance is configurable via maxColorDistance.

  • Off-token arbitrary spacing & radius

    Arbitrary Tailwind values like p-[14px] or rounded-[6px] are flagged. Use the spacing and radius steps from the foundation scale instead.

  • Hand-rolled gradients, grids, and glows

    Custom gradient or grid CSS that duplicates a house utility (glow-brand, bg-grid, text-gradient-brand, mask-fade-x) triggers a warning to use the utility instead.

  • Raw native elements where a primitive exists

    Using a bare <button>, <input>, or <select> where a design-system primitive covers the same role produces a warning. Controlled via the offSystemComponents option.

  • Bold weight on headings

    font-semibold and font-bold on h1–h6 elements (or heading-role components) are flagged, the editorial-typography DNA requires font-medium or font-normal.

04, Before → after

A typical off-system snippet leans on hardcoded colors, arbitrary spacing, and a bare native element. The lint rewrites each line to the nearest token, foundation step, and primitive.

before · off-system

after · on-system

bg-[#16a34a] resolves to bg-brand (baked into the primitive), the hardcoded white maps to the primitive's text-primary-foreground, and the arbitrary radius and padding snap to the radius and spacing steps.

05, What autofix can & can't do

--fix only applies a codemod when the correct replacement is unambiguous. Anything that needs a human judgment call is reported but left untouched.

Auto-applied

  • Raw colors within maxColorDistance of exactly one token
  • Arbitrary spacing and radius that map cleanly to a foundation step
  • Hand-rolled CSS that has a one-to-one house-utility equivalent (glow-brand, bg-grid)

Reported only

  • Colors equidistant from two tokens — the intent is ambiguous
  • Bare <button>/<input> swaps that need a primitive import and prop mapping
  • Bold headings — the fix depends on the intended visual hierarchy

06, CI & editor

Run the lint as a gate in CI so off-system code never lands, and wire the ESLint plugin into your editor for inline feedback while you type. Both reuse the same registry install and config you already have.

.github/workflows/lint.yml

.vscode/settings.json

With the ESLint VS Code extension installed, off-system code is underlined inline and Fix all auto-fixable problems applies the same codemods as --fix.