supafast-ui · sub-skill: COLOR (roles semánticos + uso real)
El sistema es monocromo neutro (grises oklch) con acentos solo para significado: estado,
delta o dato. Nunca color decorativo ni de marca random. Esta ficha dice qué color usar para qué;
la lista cruda de tokens vive en tokens.md.
1. Base monocroma (el 90% de la UI)
Texto, fondos, bordes y superficies salen SIEMPRE de los tokens semánticos neutros, nunca de hex:
| Rol | Token / clase |
|---|---|
| Fondo de app | bg-background |
| Texto principal | text-foreground |
| Texto secundario / metadatos | text-muted-foreground |
| Superficie (card/panel) | bg-card + border border-border |
| Borde / separador | border-border (filas de tabla border-border/50, toolbars border-border/60) |
| Acción principal (botón, badge sólido, pill activo) | bg-primary text-primary-foreground |
| Estado activo suave (nav, selección) | bg-primary/10 text-primary o bg-muted/30÷/40 |
| Popover/dropdown | bg-popover text-popover-foreground |
| Anillo de foco | ring-ring/50 |
Regla: si el elemento no comunica un estado ni un dato, es neutro. Punto.
2. Acentos semánticos (uso medido, fondos tenues)
Cada acento tiene un rol fijo. El patrón es texto saturado (600/700) sobre fondo muy tenue (50), o solo texto. Evita fondos saturados salvo en charts/indicadores puntuales.
✅ Éxito / positivo → emerald
- Texto:
text-emerald-600/text-emerald-700(dark:dark:text-emerald-400). - Pill/badge:
bg-emerald-50 text-emerald-700(obg-emerald-50 px-2.5 py-1 text-xs font-semibold text-emerald-600). - Usos reales: deltas positivos, “completado”, confirmación de copiado (
copied && 'text-emerald-600'), variantesuccessde tarjetas (bg-emerald-50 text-emerald-700).
⚠️ Aviso / atención → amber
- Texto:
text-amber-600/text-amber-700. Fondo:bg-amber-50. - Suele ir con icono
AlertTrianglede lucide:<AlertTriangle className="size-4 text-amber-600" />. - Variante
warningde tarjetas:bg-amber-50 text-amber-700. Mensajes de aviso no bloqueantes.
⛔ Error / peligro (estado de UI) → destructive (token semántico)
- Es el token, NO un hex. Para validación, errores y acciones destructivas:
- Texto de error:
text-destructive(enFieldError). - Input inválido (automático con
aria-invalid):border-destructive ring-destructive/20. - Fondo/borde tenue:
bg-destructive/10,bg-destructive/5,border-destructive/30. - Botón
variant="destructive":bg-destructive text-white hover:bg-destructive/90. - Ítem de menú destructivo:
data-[variant=destructive]:text-destructive focus:bg-destructive/10.
- Texto de error:
🔻 Delta negativo / micro-acción de quitar → rose
- Distinto de
destructive:rosees para números/tendencias a la baja y micro-acciones (“quitar”, “−”) inline, no para errores de validación. - Texto:
text-rose-500conhover:text-rose-600(transition-colors); variantesrose-400/rose-600. - Ej.: enlaces/botones de quitar en editores, valores negativos en charts/tendencias.
ℹ️ Info → blue (uso esporádico)
text-blue-700/bg-blue-100puntual para etiquetas informativas. No abusar; preferir neutro.
3. Color de dato (charts) → --chart-series
Las series de gráficas usan el rojo de marca vía la variable --chart-series
(oklch(0.59 0.23 17)), no los acentos de estado. Se combina con el fondo con color-mix para
mapas de calor y degradados:
const SERIES_COLOR = 'var(--chart-series)'
// heatmap: mezcla con la card según intensidad
backgroundColor: `color-mix(in oklab, var(--chart-series) ${mixPct}%, var(--card))`
// gradiente de área en un chart
<stop offset="0%" stopColor="var(--chart-series)" stopOpacity="0.7" />
<stop offset="100%" stopColor="var(--chart-series)" stopOpacity="1" />
No metas emerald/amber/rose como color de serie por defecto; esos son estado, --chart-series es dato.
4. Tabla de decisión rápida
| Quiero comunicar… | Color | Clase típica |
|---|---|---|
| Nada (UI normal) | neutro | text-foreground / text-muted-foreground / border-border |
| Acción primaria | primary | bg-primary text-primary-foreground |
| Selección/activo | primary suave | bg-primary/10 text-primary |
| Éxito / subida | emerald | text-emerald-600, pill bg-emerald-50 text-emerald-700 |
| Aviso | amber | text-amber-600, bg-amber-50 + AlertTriangle |
| Error / validación / destructivo | destructive |
text-destructive, bg-destructive/10, aria-invalid |
| Bajada / quitar (inline) | rose | text-rose-500 hover:text-rose-600 |
| Info ligera | blue | text-blue-700 (esporádico) |
| Serie de gráfica / dato | --chart-series |
var(--chart-series) + color-mix |
Reglas
- Acento = significado. Si no hay estado/dato detrás, va en neutro. No colorees por decorar.
- Fondos tenues (
-50,/10) + texto saturado (-600/700,text-destructive). Evita bloques de color saturado salvo indicadores pequeños o charts. destructive(token) ≠rose(hex). Errores/peligro =destructive; deltas negativos y micro-acciones de quitar =rose.- Estado ≠ dato. Para series de gráficas usa
--chart-series, no los acentos de estado. - Para añadir un color nuevo hay que declararlo en
:root(vertokens.md); no inventes hex en el TSX. - Respeta el par texto/fondo (
*-foreground) para contraste; no mezclestext-primarysobrebg-primary.