Herramientas

Generador de CSS Custom Properties

Estructurá tus variables CSS con nombres predecibles y escalables. Desde --color-primary-500 hasta --spacing-fluid-md, seguí convenciones que sobreviven al crecimiento del design system.

Instantáneo🔒En tu navegadorSin registro
En vivo
    Ver como texto

    Por qué las variables CSS genéricas terminan siendo deuda técnica

    El antipatrón clásico: empezás con --blue, --dark-blue, --lighter-blue. Funciona en el MVP. Seis meses después tenés --blue-for-buttons, --blue-but-darker, --new-blue y --blue-final-v2. Sin convención de nombres escalable, cada variable nueva es una decisión ad-hoc que el próximo dev interpretará diferente.

    Los mejores design systems usan dos capas de variables: primitivas y semánticas. Primitivas son los valores raw: --color-blue-500: #3b82f6. Semánticas mapean intención: --color-primary: var(--color-blue-500). Esto permite cambiar el azul de toda la app modificando una línea. Pero acá está el truco: las primitivas deben seguir escalas numéricas (50-900 estilo Tailwind) o t-shirt sizing (xs-xl), nunca --color-blue-kinda-light.

    Un equipo de design ops de Shopify documentó que el 70% de los bugs visuales después de rebrand venían de colores hardcoded en vez de usar variables semánticas. Alguien ponía color: #3b82f6 directo en CSS en vez de color: var(--color-primary). Cuando cambiaron el branding, tuvieron que hacer grep por 847 archivos buscando ese hex específico. Solución: linter que bloquee valores raw en favor de custom properties.

    Escalas de spacing que no rompen en responsive sin media queries

    El error más común con spacing: definir --spacing-small: 8px, --spacing-medium: 16px, --spacing-large: 24px y después sorprenderse que se ve mal en mobile. Spacing fijo no escala. Los mejores sistemas usan clamp() para spacing fluido: --spacing-md: clamp(1rem, 2vw, 1.5rem). Crece con el viewport sin media queries.

    La escala numérica (4px, 8px, 12px, 16px, 24px, 32px...) es predecible pero rígida. T-shirt sizing (xs, sm, md, lg, xl) es más semántico pero requiere consenso: ¿tu 'md' es 16px o 20px? Equipos grandes combinan ambas: --spacing-4 (primitiva) + --spacing-md (semántica que mapea a --spacing-4). Así podés refactorear la escala sin romper componentes.

    Un gotcha de spacing: no uses las mismas variables para padding y gap. Gap de flexbox/grid se comporta diferente que padding (colapsa en bordes). Mejor tener --gap-sm separado de --spacing-sm, o al menos documentar claramente cuál es para qué. Y nunca, NUNCA definas --spacing-default sin número — 'default' no comunica escala ni predecibilidad.

    Typography tokens que sobreviven al agregado de nuevas fonts

    El antipatrón: --font-heading, --font-body, --font-small. ¿Qué pasa cuando querés agregar una font display para hero sections, o una mono para code blocks? Terminás con --font-heading-v2 o --font-hero-special. Mejor separar family, size y weight en variables independientes: --font-family-sans, --font-size-lg, --font-weight-bold. Combinás en el uso: font: var(--font-weight-bold) var(--font-size-lg) / var(--line-height-tight) var(--font-family-sans).

    Para font-size, la escala numérica falla en comunicar intención. ¿--font-size-5 es grande o chico? Mejor: --font-size-sm, --font-size-base, --font-size-lg, --font-size-xl, --font-size-2xl... hasta --font-size-5xl para hero headings. Y agregá variantes fluid: --font-size-fluid-lg: clamp(1.25rem, 2vw + 1rem, 2rem). Se adapta solo de mobile a desktop.

    Line-height es donde más fallan los sistemas. Definir --line-height: 1.5 global no funciona — headings necesitan tighter (1.2), body text necesita relaxed (1.6), buttons necesitan 1. Mejor: --line-height-tight, --line-height-normal, --line-height-relaxed. Y nunca uses unidades en line-height (ej 24px) — siempre unitless (1.5) para que escale proporcionalmente al font-size.

    Errores comunes que rompen la herencia de custom properties

    Custom properties heredan por el cascade de CSS, pero muchos devs las usan como si fueran variables de preprocesador. Ejemplo común: definís --color-text: black en :root, pero después en un componente dark hacés background: black; color: white hardcoded. El texto dentro hereda --color-text: black y se vuelve invisible. Solución: siempre redefine las variables en el contexto: .dark { --color-text: white; --color-bg: black; }.

    Otro gotcha: custom properties no funcionan en media queries. No podés hacer @media (min-width: var(--breakpoint-md)). Las variables solo funcionan en valores de propiedades, no en at-rules. Tenés que definir los breakpoints como constantes aparte o usar PostCSS custom media queries. Un workaround común es tener --breakpoint-md como documentación pero usarlo hardcoded en el @media.

    El error más sutil: usar fallbacks incorrectos. var(--color-primary, blue) parece seguro, pero si --color-primary está definida como invalid o un valor que el browser no entiende, el fallback NO se usa — el browser usa el valor de herencia o initial. Mejor práctica: siempre definí todas las variables custom en :root aunque sea con placeholder, nunca dependas del fallback de var() para valores críticos. Y usá @supports para detectar si custom properties están disponibles antes de depender de ellas.

    Preguntas frecuentes

    ¿Debería usar nombres descriptivos (--color-ocean-blue) o abstractos (--color-primary)?

    Abstractos para tokens semánticos (--color-primary), descriptivos para primitivas (--color-blue-500). Así podés cambiar 'ocean blue' a 'forest green' solo modificando qué primitiva mapea --color-primary.

    ¿Cuántas variables CSS custom properties son demasiadas?

    No hay límite técnico, pero más de 200 primitivas indica falta de sistema. Mejor: 50-100 primitivas + 50-100 semánticas. Si tenés --spacing-17 o --color-blue-547, estás inventando valores sin escala coherente.

    ¿Qué pasa con custom properties y especificidad en cascade?

    Las custom properties respetan cascade normal — el selector más específico gana. Útil: podés hacer .theme-dark { --color-bg: black; } y todos los componentes que usen var(--color-bg) cambian automáticamente.

    ¿Debería poner todas las variables en :root o definirlas por componente?

    Primitivas globales en :root, tokens específicos de componente en el componente mismo. Ejemplo: :root tiene --spacing-md, pero .button puede tener --button-padding: var(--spacing-md) para override local sin contaminar el scope global.

    ¿Te sirvió este generador?