Variables CSS con Claude Code: tokens de tema practicos
Implementa variables CSS, var(), tokens de tema, modo oscuro y revisiones de UI con Claude Code.
Las variables CSS ordenan las decisiones de diseno
Las variables CSS, llamadas oficialmente propiedades personalizadas, permiten definir valores como --color-accent y usarlos con var(--color-accent). Dicho de forma sencilla: son espacios con nombre para guardar colores, espaciados, radios, sombras, tamanos de letra y variantes de estado.
Esto es importante cuando trabajas con Claude Code. Si pides “haz una tarjeta bonita”, puede generar colores escritos a mano en cada selector. Si primero pides tokens de tema, los cambios futuros son mas seguros: un modo oscuro, una prueba de color para el CTA o una marca secundaria se convierten en cambios de tokens, no en una busqueda por todo el CSS.
Para ampliar la base, combina este articulo con implementacion de modo oscuro, sistema de diseno y patrones Flexbox. El mapa mental es: tokens de paleta para valores crudos, tokens semanticos para significado de producto, tokens de componente para una pieza de UI y tokens de estado para dark, danger, compact o marcas concretas.
Pide los tokens antes del componente
Un prompt explicito evita que Claude Code mezcle valores sueltos con variables reutilizables.
{
"task": "Create CSS custom properties for an article UI and implement a copy-paste demo.",
"mustInclude": [
"palette tokens",
"semantic theme tokens",
"component-level tokens",
"var() fallbacks",
"dark mode with data-theme and prefers-color-scheme",
"vanilla JavaScript for theme switching"
],
"constraints": [
"Use custom properties only for values, not selectors or property names.",
"Keep contrast readable in light and dark modes.",
"Add comments for tokens that product designers may edit."
]
}
CSS listo para copiar
El segundo argumento de var(--surface, #ffffff) es el fallback. Si --surface no existe o no es valido, el navegador usa #ffffff. La referencia oficial de MDN sobre var() explica este comportamiento con detalle.
:root {
color-scheme: light;
/* Palette tokens: raw values */
--blue-50: #eff6ff;
--blue-400: #60a5fa;
--blue-600: #2563eb;
--blue-700: #1d4ed8;
--slate-50: #f8fafc;
--slate-100: #f1f5f9;
--slate-300: #cbd5e1;
--slate-700: #334155;
--slate-900: #0f172a;
--red-600: #dc2626;
/* Semantic theme tokens: values used by the app */
--surface: var(--slate-50);
--surface-raised: #ffffff;
--text: var(--slate-900);
--text-muted: var(--slate-700);
--border: var(--slate-300);
--color-accent: var(--blue-600);
--color-danger: var(--red-600);
/* Typography and spacing */
--font-body: Inter, "Noto Sans", system-ui, sans-serif;
--font-mono: "JetBrains Mono", Consolas, monospace;
--step-0: 1rem;
--step-1: 1.125rem;
--step-2: 1.5rem;
--space-2: 0.5rem;
--space-3: 0.75rem;
--space-4: 1rem;
--space-6: 1.5rem;
--radius-md: 0.5rem;
--radius-lg: 0.75rem;
--shadow-card: 0 12px 30px rgb(15 23 42 / 0.12);
}
[data-theme="dark"] {
color-scheme: dark;
--surface: var(--slate-900);
--surface-raised: #111827;
--text: var(--slate-50);
--text-muted: var(--slate-300);
--border: #334155;
--color-accent: var(--blue-400);
--shadow-card: 0 18px 42px rgb(0 0 0 / 0.35);
}
@media (prefers-color-scheme: dark) {
:root:not([data-theme="light"]) {
color-scheme: dark;
--surface: var(--slate-900);
--surface-raised: #111827;
--text: var(--slate-50);
--text-muted: var(--slate-300);
--border: #334155;
--color-accent: var(--blue-400);
}
}
body {
margin: 0;
font-family: var(--font-body, system-ui, sans-serif);
background: var(--surface, #ffffff);
color: var(--text, #111827);
}
.pricing-card {
--card-padding: var(--space-6);
--card-border: var(--border);
max-width: 34rem;
margin: var(--space-6) auto;
padding: var(--card-padding);
background: var(--surface-raised, #ffffff);
border: 1px solid var(--card-border, #d1d5db);
border-radius: var(--radius-lg, 0.75rem);
box-shadow: var(--shadow-card, none);
}
.pricing-card h2 {
margin: 0 0 var(--space-2);
font-size: var(--step-2, 1.5rem);
}
.pricing-card p {
color: var(--text-muted, #4b5563);
line-height: 1.75;
}
.button {
--button-bg: var(--color-accent);
--button-text: #ffffff;
--button-border: transparent;
display: inline-flex;
align-items: center;
justify-content: center;
min-height: 2.75rem;
padding: 0 var(--space-4);
border: 1px solid var(--button-border);
border-radius: var(--radius-md);
background: var(--button-bg, #2563eb);
color: var(--button-text, #ffffff);
font-weight: 700;
cursor: pointer;
}
.button[data-variant="secondary"] {
--button-bg: transparent;
--button-text: var(--color-accent);
--button-border: var(--color-accent);
}
Demo HTML y JavaScript
Este ejemplo funciona sin framework. data-theme guarda la eleccion manual y prefers-color-scheme toma la preferencia del sistema cuando no hay eleccion guardada.
<section class="pricing-card">
<h2>Claude Code UI Token Demo</h2>
<p>
This card uses CSS custom properties for surface, text, border,
shadow, and button colors.
</p>
<button class="button" type="button" data-theme-toggle>
Toggle theme
</button>
<button class="button" type="button" data-variant="secondary">
Secondary action
</button>
<label>
Accent color
<input type="color" value="#2563eb" data-accent-picker>
</label>
</section>
const root = document.documentElement;
const themeKey = "claude-code-css-variable-theme";
const savedTheme = localStorage.getItem(themeKey);
const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
function applyTheme(theme) {
root.setAttribute("data-theme", theme);
localStorage.setItem(themeKey, theme);
}
function toggleTheme() {
const current = root.getAttribute("data-theme") || "light";
applyTheme(current === "dark" ? "light" : "dark");
}
function setAccentColor(color) {
if (CSS.supports("color", color)) {
root.style.setProperty("--color-accent", color);
}
}
applyTheme(savedTheme || (prefersDark ? "dark" : "light"));
document.querySelector("[data-theme-toggle]")?.addEventListener("click", toggleTheme);
document.querySelector("[data-accent-picker]")?.addEventListener("input", (event) => {
setAccentColor(event.target.value);
});
MDN documenta prefers-color-scheme y color-scheme. Usa ambos: uno detecta la preferencia y el otro informa al navegador de los colores esperados para controles nativos.
Casos de uso practicos
Caso 1: CTAs en articulos y landing pages. Cambiar --color-accent permite probar una oferta, un Gumroad link o un boton principal sin tocar cada selector.
Caso 2: dashboards SaaS. Los tokens compartidos mantienen superficies, bordes, espaciado y estados de error aunque Claude Code genere pantallas en sesiones distintas.
Caso 3: marca blanca. Puedes declarar [data-brand="client-a"] y sobrescribir tokens semanticos sin duplicar componentes.
Caso 4: accesibilidad. Contraste alto, mas espacio y tamanos legibles pueden expresarse como tokens y revisarse con la guia de accesibilidad.
Errores comunes
No dejes var() sin fallback en paginas publicas. Prefiere color: var(--text, #111827);.
Los nombres de propiedades personalizadas distinguen mayusculas y minusculas. La referencia de MDN sobre custom properties lo confirma; usa minusculas y guiones.
No uses variables CSS en selectores, nombres de propiedades o condiciones de media query. Son para valores.
Valida los cambios desde JavaScript. Si una entrada del usuario llega a setProperty(), compruebala con CSS.supports().
Revisa sombras y bordes en modo oscuro. Cambiar solo fondo y texto deja muchos temas con aspecto inacabado.
CTA y revision
Usa este prompt de revision para detectar problemas antes de publicar:
{
"reviewTarget": "CSS custom properties and theme implementation",
"checks": [
"undefined custom properties without fallback",
"light and dark contrast issues",
"component variants that bypass tokens",
"duplicated raw colors outside :root",
"JavaScript setProperty calls without validation",
"mobile overflow caused by fixed spacing tokens"
],
"output": "List findings with file name, selector, risk, and suggested fix."
}
Para la referencia oficial de Claude Code, consulta la documentacion de Anthropic. Para trabajar mas rapido, empieza con la cheatsheet gratuita, usa 50 Prompt Templates si necesitas prompts reutilizables y el Setup Guide si quieres ordenar CLAUDE.md, permisos, hooks y revisiones de equipo.
Al probar este flujo en tarjetas de CTA, el mayor beneficio fue separar los colores crudos en :root y obligar a los componentes a consumir tokens semanticos. Los cambios de tema quedaron mas pequenos y las revisiones de Claude Code fueron mas faciles de leer.
PDF gratis: cheatsheet de Claude Code
Introduce tu email y descarga una hoja con comandos, hábitos de revisión y flujos seguros.
Cuidamos tus datos y no enviamos spam.
Sobre el autor
Masa
Ingeniero enfocado en workflows prácticos con Claude Code.
Artículos relacionados
Escalera de permisos de Claude Code para ampliar acceso sin perder control
Pasa de read-only a ediciones limitadas, comandos de prueba y checks de deploy con menos riesgo.
Claude Code Small PR Proof Pack: cambios pequeños que sí se pueden revisar
Un paquete de prueba para PRs de Claude Code: diff, checks, URL pública, CTA y rollback.
Gate de revisión antes del commit con Claude Code
Cómo revisar con Claude Code antes del commit: diff, build, URL pública, Gumroad, consultoría, tests y archivos ajenos.