# no-hardcoded-colors

Regla de oxlint que reporta colores fijos en valores arbitrarios de Tailwind CSS — hex, `rgb()`,
`hsl()`, `oklch()` y más — para que salgan de tu theme.

## De un vistazo

| Autofix | Sugerencias en el editor | Design system                         | Opciones              |
| ------- | ------------------------ | ------------------------------------- | --------------------- |
| No      | No                       | Opcional — se usa si hay `entryPoint` | `entryPoint`, `allow` |

## Qué hace esta regla

Marca cualquier clase cuyo arbitrary value lleve un literal de color hardcodeado — cualquier cosa
que escribirías con un paint chip en la mano: hex (`bg-[#ff5733]`, `text-[#000]`), `rgb()`/`rgba()`,
`hsl()`/`hsla()`, más los espacios de color modernos `oklch()`, `oklab()`, `lab()`, `lch()`,
`hwb()`, y `color()`. La intención es la misma que `no-arbitrary-value` pero acotada exclusivamente
al color, que es donde suele empezar el drift del design system.

El valor se **escanea**, no se compara desde su primer carácter, y la utility de la que cuelga es
irrelevante. Eso importa más de lo que parece:

- un color en medio de un shorthand cuenta — `shadow-[0_1px_2px_#000]` es un negro hardcodeado;
- también las utilities que ninguna lista de prefijos mantuvo al día — `inset-ring-[#000]`,
  `inset-shadow-[#000]`;
- y también las propiedades arbitrarias, con las que ningún prefijo podría coincidir —
  `[color:#f00]`, `[--brand:#f00]`.

Hay dos exclusiones deliberadas, y son la razón de que esto sea un escáner y no un regex: un `#`
dentro de un **string entre comillas** es texto (`content-['#fff']`), y un `#` dentro de **`url()`**
es una referencia a un nodo SVG (`fill-[url(#gradient)]`) — el `#` no-color más común en una clase
de Tailwind. Los arbitrary no-color como `w-[200px]` o `tracking-[0.5em]` no llevan literal, así que
pasan como antes y puedes activar esta regla incluso donde _sí_ quieres permitir dimensiones
arbitrarias.

Los colores con nombre (`bg-[red]`) no están cubiertos: `currentColor`, `transparent` e `inherit`
hacen de eso un juicio de valor y no un escaneo.

Los valores que referencian una variable CSS se tratan como indirección del design system y pasan:
`bg-[var(--primary)]`, `text-[hsl(var(--fg))]`, `bg-[oklch(var(--bg))]`, incluso
`bg-[var(--primary,#fff)]` (un fallback adentro de `var()` sigue contando como referencia a una
variable). El chequeo de `var()` no es recursivo — `bg-[linear-gradient(hsl(var(--a)),#fff)]` _no_
se marca porque hay al menos un `var()` presente. Es comportamiento documentado y testeado.

DS-opcional — funciona sin nada configurado. Con `settings.tailwindcss.entryPoint`, el mensaje
además nombra los colores de tu theme, así tienes el reemplazo a la vista:

```text
"bg-[#ff6600]" uses a hardcoded color value. Use one of your theme colors instead: brand,
brand-light.
```

El design system se carga solo cuando hay un color que reportar, y un proyecto sin colores propios —
Tailwind tal cual — conserva el mensaje simple. No hay autofix: elegir el token correcto es una
decisión humana.

## Opciones

### `allow`

`string[]`, default `[]`.

Strings exactos de clases para incluir en la allowlist. Las coincidencias son literales (no hay
coincidencia por prefijo ni regex), así que puedes permitir escapes puntuales sin abrir más la
puerta. Útil para el hex ocasional mandado por brand en un único componente de assets.

```jsonc
{
  "tailwindcss/no-hardcoded-colors": ["error", {
    "allow": ["bg-[#000]", "text-[#fff]"]
  }]
}
```

### `entryPoint`

`string`, opcional. Override por regla de `settings.tailwindcss.entryPoint`, que solo se lee para
nombrar los colores de tu theme en el mensaje.

## Ejemplos

### ✗ Incorrecto

```tsx
// Hex literales sobre utilities de color
<div className="bg-[#ff5733] text-[#000]" />
// reports: Use one of your theme colors instead

// rgb/rgba/hsl/oklch dan la misma respuesta
<div className="border-[rgba(0,0,0,0.5)] text-[hsl(120,100%,50%)]" />
<div className="bg-[oklch(0.5_0.2_240)]" />

// Variants y el modificador `!` no salvan
<div className="hover:bg-[#ff5733] !bg-[#ff5733]" />

// El color no tiene que ser todo el valor
<div className="shadow-[0_1px_2px_#000]" />

// Utilities que una lista de prefijos no cubría
<div className="inset-ring-[#000] inset-shadow-[#111]" />

// Propiedades arbitrarias, incluida una custom property
<div className="[color:#f00] [--brand:#f00]" />
```

### ✓ Correcto

```tsx
// Colores del theme
<div className="bg-brand text-brand-light" />

// Indirección por variable CSS pasa
<div className="bg-[var(--primary)] border-[hsl(var(--border))]" />

// Los arbitrary no-color no son asunto de esta regla
<div className="w-[200px] tracking-[0.5em]" />

// Un `#` dentro de un string entre comillas es texto
<div className="content-['#fff']" />

// Un `#` dentro de url() referencia un nodo SVG
<svg className="fill-[url(#gradient)]" />

// String exacto allowlisteado
// options: { "allow": ["bg-[#000]"] }
<div className="bg-[#000]" />
```

## Interacciones con otras reglas

- **`no-arbitrary-value`**: superset. Si activas esa regla, cada color hardcodeado ya está marcado.
  Usa `no-hardcoded-colors` sola cuando quieres el mensaje específico de color y toleras otros
  arbitrary values; usa ambas para un diagnóstico más claro sobre el drift de color.
- **`no-default-palette`**: la otra mitad. Reporta los colores de la paleta por defecto de Tailwind
  (`bg-red-500`); esta regla reporta literales de color (`bg-[#f00]`). Juntas dejan solo los colores
  de tu theme.
- **`prefer-theme-tokens`**: complementaria. `prefer-theme-tokens` le pregunta al design system si
  existe un color `@theme` que coincida y lo sugiere; esta regla dispara igual exista o no el token,
  así que atrapa el drift más temprano (antes de definir el token).
- **`no-unknown-classes`**: ortogonal. Los arbitrary hardcodeados son sintaxis _válida_ de Tailwind,
  así que `no-unknown-classes` no los va a marcar. Deberían estar ambas activas.

## Cuándo desactivarla

- **Páginas de marketing/brand** con hex pinzados a mano que genuinamente no pertenecen al design
  system compartido. Prefiere `allow` con los strings de clase específicos.
- **Contenido generado** (e.g. avatares inline coloreados desde un hash) donde el color se determina
  en runtime y se renderiza como inline style igual — esos no van a aparecer aquí, pero si aparecen,
  desactiva la línea.
- **Librerías de componentes que publican ejemplos** que deliberadamente demuestran uso de colores
  arbitrarios en su doc.
