no-hardcoded-colors
Disallow hardcoded color values in Tailwind CSS classes
What this rule does
Flags Tailwind color utilities whose arbitrary value is a hardcoded color literal — anything you'd write with a paint chip in your hand: hex (bg-[#ff5733], text-[#000]), rgb()/rgba(), hsl()/ hsla(), plus the modern color spaces oklch(), oklab(), lab(), lch(), hwb(), and color(). The goal is the same as no-arbitrary-value but scoped exclusively to color, which is where design-system drift tends to start.
The rule checks only the color-bearing utility prefixes (bg-, text-, border-*, outline-, ring-*, shadow-, accent-, caret-, fill-, stroke-, decoration-, divide-, placeholder-, gradient stops from-/via-/to-). Non-color arbitraries like w-[200px] or tracking-[0.5em] are explicitly ignored, so this rule can be enabled even where you do want to allow arbitrary dimensions.
Values that reference a CSS variable are treated as design-system indirection and pass: bg-[var(--primary)], text-[hsl(var(--fg))], bg-[oklch(var(--bg))], even bg-[var(--primary,#fff)] (a fallback inside var() still counts as referencing a variable). The non-recursive var() check is shallow — bg-[linear-gradient(hsl(var(--a)),#fff)] is not flagged because at least one var() is present. This is documented behavior and tested.
DS-independent — no design system is loaded. No autofix: picking the right token is a human decision.
Options
allow
string[], default [].
Exact class strings to whitelist. Matches are literal (no prefix match, no regex) so you can pin specific allowed escapes without opening the door wider. Useful for the occasional brand-mandated hex in a single brand-asset component.
{
"tailwindcss/no-hardcoded-colors": ["error", {
"allow": ["bg-[#000]", "text-[#fff]"]
}]
}entryPoint
string, optional. Present in the schema for forward compatibility but currently unused by the rule — no-hardcoded-colors runs without a design system. You can ignore it.
Examples
✗ Incorrect
// Hex literals on color utilities
<div className="bg-[#ff5733] text-[#000]" />
// rgb/rgba/hsl/oklch are all the same answer
<div className="border-[rgba(0,0,0,0.5)] text-[hsl(120,100%,50%)]" />
<div className="bg-[oklch(0.5_0.2_240)]" />
// Variants and `!` modifier don't help
<div className="hover:bg-[#ff5733] !bg-[#ff5733]" />✓ Correct
// Named theme colors
<div className="bg-blue-500 text-white" />
// CSS variable indirection passes
<div className="bg-[var(--primary)] border-[hsl(var(--border))]" />
// Non-color arbitraries are not this rule's business
<div className="w-[200px] tracking-[0.5em]" />
// Allowlisted exact string
<div className="bg-[#000]" /> // with allow: ["bg-[#000]"]Interactions with other rules
no-arbitrary-value: superset. If you enable that rule, every hardcoded color is already flagged. Useno-hardcoded-colorsalone when you want the specific color message and tolerate other arbitrary values; use both for a clearer diagnostic on color drift.prefer-theme-tokens: complementary.prefer-theme-tokensasks the design system if a matching@themecolor exists and suggests it; this rule fires regardless of whether a token exists, so it catches drift earlier (before you've defined the token).no-unknown-classes: orthogonal. Hardcoded arbitraries are valid Tailwind syntax, sono-unknown-classeswon't flag them. Both should be on.
When to disable it
- Marketing/brand pages with handpicked hex that genuinely doesn't belong in the shared design system. Prefer
allowwith the specific class strings. - Generated content (e.g. inline avatars colored from a hash) where the color is determined at runtime and rendered as inline style anyway — those won't show up here, but if they do, disable the line.
- Component libraries shipping examples that intentionally demonstrate arbitrary-color usage in docs.