Guía completa de instalación y personalización de temas en shadcn/ui (con variables CSS)

La primera vez que usé shadcn/ui, me desconcertó lo de «no es un paquete npm». ¿Copiar código al proyecto? Suena demasiado primitivo.
Después de usarlo unas cuantas veces entendí por qué es tan potente: tienes el código fuente de todos los componentes, lo modificas como quieras, sin conflictos de versión ni limitaciones del diseño impuesto por la biblioteca.
Hoy hablamos de la instalación, la configuración y la personalización de temas en shadcn/ui, con foco en cómo usar variables CSS para un sistema de diseño con identidad de marca. Al terminar este artículo deberías poder configurar lo básico en unos 5 minutos y dedicar una hora más a ajustar el tema a tu gusto.
1. Instalación rápida: dos enfoques
Enfoque 1: inicialización con CLI (recomendado)
En un proyecto nuevo, ejecuta directamente:
npx shadcn@latest init
Te hará varias preguntas: ¿TypeScript o JavaScript? ¿Qué estilo? ¿Tema por defecto? Todo es interactivo; sigue las indicaciones.
Tras la instalación, tu proyecto tendrá archivos nuevos:
components.json— archivo de configuraciónlib/utils.ts— funciones de utilidadcomponents/ui/— directorio de componentes
Añadir componentes también es sencillo. Por ejemplo, un botón:
npx shadcn@latest add button
El código se copia automáticamente a components/ui/button.tsx; solo tienes que importarlo y usarlo.
Aquí hay una trampa: si el proyecto ya lleva tiempo, tailwind.config.js y globals.css pueden tener mucha configuración propia. El comando init de shadcn sobrescribe esos archivos, así que conviene instalarlo al principio.
Un blogger lo resumió bien: trata shadcn/ui como una de las «primeras dependencias» del proyecto; no lo dejes para más tarde. Aprendí la lección a las duras.
Enfoque 2: instalación manual (proyectos existentes)
Si el proyecto ya está maduro y el riesgo de que la CLI sobrescriba la config es demasiado alto, instala manualmente.
Pasos:
Paso 1: asegúrate de tener Tailwind CSS
Los componentes de shadcn están escritos con Tailwind. Si aún no lo tienes, instálalo primero; la documentación oficial es clara.
Paso 2: instala dependencias
npm install class-variance-authority clsx tailwind-merge
npm install lucide-react
class-variance-authority (CVA) es muy útil; lo usarás al definir variantes de componentes.
Paso 3: configura alias de rutas
En tsconfig.json, añade:
{
"compilerOptions": {
"paths": {
"@/*": ["./*"]
}
}
}
Así puedes importar con @/components/ui/button en lugar de escribir ../../../.
Paso 4: crea components.json
En la raíz del proyecto:
{
"style": "new-york",
"rsc": true,
"tailwind": {
"config": "tailwind.config.ts",
"css": "app/globals.css",
"baseColor": "neutral",
"cssVariables": true
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils"
}
}
La línea cssVariables: true es clave: indica que usaremos variables CSS para el tema, no utility classes de Tailwind.
Paso 5: añade estilos
Incluye los estilos base de shadcn en globals.css; más adelante profundizamos en el tema.
2. Entender el sistema de temas: cómo funcionan las variables CSS
El sistema de temas de shadcn/ui se basa en una convención simple: cada color tiene variables background y foreground.
¿Qué significa? Un ejemplo:
:root {
--primary: 222.2 47.4% 11.2%;
--primary-foreground: 210 40% 98%;
}
--primary es el fondo del botón; --primary-foreground, el color del texto. Al emparejarlos, cambias una variable y todos los componentes relacionados se actualizan.
Lista de variables CSS
shadcn/ui define por defecto estas variables:
| Variable | Uso |
|---|---|
--background | Fondo de página |
--foreground | Texto de página |
--card | Fondo de tarjeta |
--card-foreground | Texto de tarjeta |
--popover | Fondo de capa emergente |
--popover-foreground | Texto de capa emergente |
--primary | Color principal (botones, enlaces) |
--primary-foreground | Texto sobre color principal |
--secondary | Color secundario |
--secondary-foreground | Texto sobre color secundario |
--muted | Fondo suave |
--muted-foreground | Texto suave |
--accent | Color de acento |
--accent-foreground | Texto sobre acento |
--destructive | Acciones peligrosas (botón eliminar) |
--destructive-foreground | Texto sobre acción destructiva |
--border | Bordes |
--input | Campos de entrada |
--ring | Anillo de foco |
Parecen muchas, pero si captas el patrón background/foreground, se memorizan con facilidad.
El secreto del formato HSL
Quizá notes que los valores de color de shadcn no usan HSL estándar:
/* ❌ HSL estándar */
--primary: hsl(222.2, 47.4%, 11.2%);
/* ✅ Formato shadcn */
--primary: 222.2 47.4% 11.2%;
¿Por qué en formato «desnudo»?
Porque Tailwind admite modificadores de opacidad, como bg-primary/50 para el 50% de transparencia. Con hsl() completo, esa función no funciona.
Con el formato desnudo, Tailwind añade hsl() y la opacidad por ti. Diseño inteligente.
3. Personalizar tu tema de marca
Método 1: modificar variables CSS directamente
Lo más simple: abre globals.css, localiza :root y cambia los valores de color.
Por ejemplo, pasar el primario del azul por defecto al violeta:
:root {
--primary: 270 60% 60%;
--primary-foreground: 0 0% 100%;
}
.dark {
--primary: 270 60% 70%;
--primary-foreground: 0 0% 0%;
}
Guarda y todos los botones y enlaces con bg-primary pasarán a violeta.
Método 2: espacio de color OKLCH (Tailwind v4)
Si usas Tailwind v4, considera OKLCH. Frente a HSL, la percepción del color se acerca más al ojo humano y las escalas resultan más uniformes.
:root {
--primary: oklch(0.6 0.2 270);
--primary-foreground: oklch(0.98 0 0);
}
Los tres parámetros de oklch(0.6 0.2 270) son:
0.6— luminosidad (0-1)0.2— croma (aprox. 0-0.4)270— ángulo de matiz (0-360)
Método 3: herramientas online
¿Te resulta tedioso combinar colores? Usa herramientas online.
Recomiendo: Shadcn Theme Generator
Elige un color primario y la herramienta genera el conjunto completo de variables CSS, en claro y oscuro. Copia y pega en globals.css.
4. Configuración del modo oscuro
Cambio de tema con next-themes
shadcn/ui no trae cambio de tema integrado, pero puedes usar next-themes.
Instálalo:
npm install next-themes
Configura en layout.tsx:
import { ThemeProvider } from "next-themes"
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="es" suppressHydrationWarning>
<body>
<ThemeProvider
attribute="class"
defaultTheme="system"
enableSystem
>
{children}
</ThemeProvider>
</body>
</html>
)
}
Puntos clave:
suppressHydrationWarninges obligatorio; si no, verás avisos de hidrataciónattribute="class"indica que el tema se cambia con clasesdefaultTheme="system"sigue el sistema por defectoenableSystemactiva la detección del tema del sistema
Botón para cambiar de tema
Con el hook useTheme obtienes el tema actual y la función de cambio:
import { useTheme } from "next-themes"
import { Moon, Sun } from "lucide-react"
export function ThemeToggle() {
const { theme, setTheme } = useTheme()
return (
<button
onClick={() => setTheme(theme === "dark" ? "light" : "dark")}
className="p-2 rounded-md hover:bg-accent"
>
{theme === "dark" ? <Sun size={20} /> : <Moon size={20} />}
</button>
)
}
Modo oscuro por defecto
Si quieres que el sitio arranque en oscuro, hay dos formas:
Opción 1: clase dark fija
<html lang="es" className="dark">
El tema queda fijado en oscuro; no hay cambio.
Opción 2: tema por defecto
<ThemeProvider
attribute="class"
defaultTheme="dark" // Oscuro por defecto
enableSystem={false} // Desactiva detección del sistema
>
El usuario puede cambiar manualmente, pero el estado inicial es oscuro.
5. Personalización avanzada: variantes de componentes
Variantes personalizadas con CVA
A veces necesitas varios estilos de botón: «peligro», «éxito», «degradado». CVA facilita definir esas variantes.
import { cva, type VariantProps } from "class-variance-authority"
const buttonVariants = cva(
"inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground hover:bg-primary/90",
destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
default: "h-10 px-4 py-2",
sm: "h-9 rounded-md px-3",
lg: "h-11 rounded-md px-8",
icon: "h-10 w-10",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
)
export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {}
Uso en componentes:
<button className={buttonVariants({ variant: "destructive", size: "lg" })}>
Eliminar
</button>
No modifiques directamente el código fuente de shadcn
Es una cuestión de mejores prácticas.
El código de shadcn está en tu proyecto y puedes tocarlo, pero conviene no alterar los archivos originales: crea componentes wrapper.
¿Por qué? shadcn actualiza componentes con frecuencia. Si cambias los originales, al actualizar tendrás que hacer merge manual. Es un dolor de cabeza.
Enfoque mejor:
// components/brand-button.tsx
import { Button } from "@/components/ui/button"
import { cva } from "class-variance-authority"
const brandButtonVariants = cva("...", {
variants: {
brand: {
primary: "bg-brand-primary text-white",
secondary: "bg-brand-secondary text-black",
},
},
})
export function BrandButton({ brand, ...props }) {
return <Button className={brandButtonVariants({ brand })} {...props} />
}
El Button original queda intacto; tienes tu BrandButton y las actualizaciones de shadcn no rompen tu personalización.
6. Problemas frecuentes y trampas
Problema 1: los estilos no se aplican tras instalar
Revisa:
- ¿Está importado
globals.cssenlayout.tsx? - ¿La config
contentde Tailwind incluyecomponents/**/*? - ¿Las rutas en
components.jsonson correctas?
Problema 2: parpadeo al cambiar de tema
Suele ser desajuste de hidratación. Asegúrate de:
suppressHydrationWarningen la etiqueta<html>- ThemeProvider envolviendo toda la app
- No leer
themeen renderizado servidor (será undefined)
Problema 3: las variables CSS no funcionan
Posibles causas:
- Nombre mal escrito (
--primary-foreground, no--primaryForeground) - Falta el bloque
.darkcorrespondiente - Formato incorrecto (usa HSL desnudo u OKLCH)
Problema 4: conflictos de estilos entre componentes
Si el proyecto ya tiene un sistema de estilos, puede chocar con shadcn. Soluciones:
- Namespace en componentes shadcn (p. ej.
shadcn-button) - Ajustar prioridades de capas en Tailwind
- Variantes propias con CVA, sin depender del estilo por defecto
7. Resumen
Instalar y configurar shadcn/ui es sencillo; lo importante es entender la filosofía de «copiar código, no instalar paquete». Ganas control total; pagas manteniendo el código en cada proyecto.
En temas, el sistema de variables CSS es elegante: cambias unos valores y toda la app se recolorea. Con next-themes, claro/oscuro son unas pocas líneas.
Recomendaciones finales:
- En proyectos nuevos, usa la CLI y evita configuración manual
- Variables semánticas (primary, secondary), no nombres de color concretos
- Prueba contraste en claro y oscuro para legibilidad
- Componentes wrapper, no parches al fuente — facilita actualizaciones
La próxima vez que necesites una UI con tema rápido, prueba shadcn/ui. La satisfacción del copy-paste se entiende usándolo.
Referencias
- shadcn/ui Official Docs - Installation
- shadcn/ui Official Docs - Theming
- shadcn/ui Official Docs - Dark Mode
- Generate Custom shadcn/ui Themes
- Theming in shadcn UI: CSS Variables
Instalación y personalización de temas en shadcn/ui
Instala shadcn/ui desde cero, configura el sistema de temas y construye un diseño con identidad de marca
⏱️ Estimated time: 30 min
- 1
Step 1: Inicialización rápida con CLI
En un proyecto nuevo, ejecuta el comando de instalación:
• npx shadcn@latest init
• Elige TypeScript/estilo New York/tema por defecto
• Espera a que la CLI termine la configuración - 2
Step 2: Modificar el color primario de marca
Edita las variables CSS en globals.css:
• Abre app/globals.css
• Localiza la variable --primary bajo :root
• Cámbiala por tu color de marca (formato HSL u OKLCH)
• Ajusta también --primary-foreground para mantener el contraste - 3
Step 3: Configurar el modo oscuro
Instala y configura next-themes:
• npm install next-themes
• Añade ThemeProvider en layout.tsx
• Usa suppressHydrationWarning para evitar avisos de hidratación
• Crea un componente para cambiar de tema - 4
Step 4: Crear variantes de componentes
Usa CVA para definir estilos personalizados:
• Instala class-variance-authority
• Define variants y defaultVariants
• Aplica buttonVariants() en los componentes
• Mantén intactos los componentes originales de shadcn
FAQ
¿En qué se diferencia shadcn/ui de las bibliotecas UI tradicionales?
¿Por qué conviene instalar shadcn/ui al inicializar un proyecto nuevo?
¿Por qué las variables CSS usan formato «desnudo» en lugar de HSL estándar?
¿Cómo cambio el color primario de marca?
¿Por qué parpadea el modo oscuro?
¿Debo modificar directamente el código fuente de los componentes shadcn?
8 min de lectura · Publicado el: 26 mar 2026 · Actualizado el: 21 ago 2026
Tailwind y shadcn/ui en práctica
Si llegaste desde búsqueda, lo más rápido es ir al artículo anterior o siguiente de esta misma serie.
Anterior
¿Qué es shadcn/ui? Guía de comparación con MUI, Chakra y otras bibliotecas
Comparación profunda de shadcn/ui, Material-UI, Chakra UI y Ant Design en siete dimensiones: tamaño del bundle, flexibilidad, experiencia de desarrollo y accesibilidad, para elegir la mejor opción
Parte 3 de 14
Siguiente
Esqueleto de panel con shadcn/ui: mejores prácticas de Sidebar + Layout
Integra Sidebar de shadcn/ui con Layout de Next.js: arquitectura de componentes, diseño responsive y control de permisos. Esqueleto de administración extensible con ejemplos completos
Parte 5 de 14



Comentarios
Inicia sesión con GitHub para dejar un comentario