Cambios: - Agregar sección de Variables de Tipos de Café (--coffee-*) - Agregar sección de Variables de Estados (--status-*) - Agregar Ejemplo 7: Usar Colores de Tipos de Café - Agregar Ejemplo 8: Usar Colores de Estados - Actualizar Ejemplo 6 con todas las propiedades del tema - Actualizar Resumen de Comandos Rápidos con nuevas variables - Documentación completa para las 15 variables CSS del sistema La documentación ahora refleja el sistema extendido de temas.
18 KiB
Sistema de Temas - Analítica Núcleo
Documentación definitiva del sistema de temas personalizable Versión: 2.0 | Última actualización: 2025-10-30
Índice
- Introducción
- Arquitectura del Sistema
- Variables CSS Disponibles
- Reglas de Uso Obligatorias
- Clases Utilitarias
- Composable useTheme()
- Ejemplos de Uso
- Integración con Nuxt UI
- Casos Especiales
- Troubleshooting
Introducción
Analítica Núcleo cuenta con un sistema de temas completamente dinámico que permite personalizar todos los colores de la interfaz en tiempo real. Los cambios se aplican instantáneamente a todos los componentes de la aplicación.
Características principales:
- ✅ 8 variables de color personalizables
- ✅ 4 temas predefinidos (Café, Azul, Verde, Carbón)
- ✅ Persistencia en localStorage
- ✅ Vista previa en tiempo real
- ✅ Exportar/importar temas en JSON
- ✅ Integración completa con Nuxt UI
Arquitectura del Sistema
Flujo de Datos
Usuario modifica tema en /settings
↓
useTheme() composable
↓
Actualiza variables CSS (--brand-*)
↓
Variables --ui-color-neutral-* mapeadas automáticamente
↓
Todos los componentes se actualizan instantáneamente
Archivos Clave
| Archivo | Propósito |
|---|---|
app/assets/css/main.css |
Definición de variables CSS y clases utilitarias |
app/composables/useTheme.ts |
Lógica del sistema de temas |
app/pages/settings.vue |
Interfaz de configuración |
Variables CSS Disponibles
Variables Base (--brand-*)
Estas variables controlan la apariencia general de la aplicación:
| Variable | Uso | Ejemplo de valor |
|---|---|---|
--brand-bg |
Fondo principal de la app | #14100b |
--brand-surface |
Fondos de cards, modales, elementos elevados | #1f180f |
--brand-border |
Bordes de todos los elementos | #3a2a16 |
--brand-primary |
Color principal para enlaces, títulos destacados | #e0c080 |
--brand-primary-strong |
Variante más intensa del primario | #c08040 |
--brand-accent |
Highlights, elementos de énfasis | #ffe0a0 |
--brand-text |
Texto principal (alta legibilidad) | #fef9f0 |
--brand-text-muted |
Texto secundario, descripciones | #d8c7a6 |
Variables de Tipos de Café (--coffee-*)
Estas variables se usan para identificar visualmente los tipos de café en gráficas y tablas:
| Variable | Uso | Ejemplo de valor |
|---|---|---|
--coffee-uva |
Color de identificación para Café Uva | #a855f7 (Purple) |
--coffee-oreado |
Color de identificación para Café Oreado | #f97316 (Orange) |
--coffee-mojado |
Color de identificación para Café Mojado | #06b6d4 (Cyan) |
--coffee-verde |
Color de identificación para Café Verde | #22c55e (Green) |
Variables de Estados (--status-*)
Estas variables se usan para indicar estados de pago en tablas y badges:
| Variable | Uso | Ejemplo de valor |
|---|---|---|
--status-pendiente |
Estado de pago pendiente | #f59e0b (Amber) |
--status-pagado |
Estado pagado completamente | #10b981 (Emerald) |
--status-anulado |
Estado anulado o cancelado | #6b7280 (Gray) |
Variables de Nuxt UI (--ui-color-neutral-*)
⚠️ NO uses estas variables directamente. Están mapeadas automáticamente a --brand-* en main.css.
Mapeo actual (NO modificar):
--ui-color-neutral-50: var(--brand-text); /* Texto más claro */
--ui-color-neutral-100: var(--brand-text-muted); /* Texto secundario */
--ui-color-neutral-200: var(--brand-primary); /* Primario claro */
--ui-color-neutral-300: var(--brand-primary); /* Primario */
--ui-color-neutral-400: var(--brand-primary-strong); /* Primario fuerte */
--ui-color-neutral-500: var(--brand-border); /* Bordes */
--ui-color-neutral-600: var(--brand-border); /* Bordes */
--ui-color-neutral-700: var(--brand-surface); /* Surface */
--ui-color-neutral-800: var(--brand-surface); /* Surface */
--ui-color-neutral-900: var(--brand-bg); /* Fondo más oscuro */
--ui-color-neutral-950: var(--brand-bg); /* Fondo */
IMPORTANTE: Nuxt UI usa la escala neutral de forma invertida en dark mode:
neutral-50/100→ Colores claros (texto)neutral-900/950→ Colores oscuros (fondos)
Reglas de Uso Obligatorias
✅ SIEMPRE Hacer
-
Usar variables CSS en lugar de colores hardcoded:
<!-- ✅ CORRECTO --> <div class="bg-[var(--brand-surface)] text-[var(--brand-text)]"> Contenido </div> -
Usar clases utilitarias cuando existan:
<!-- ✅ CORRECTO --> <div class="brand-card"> <h2 class="brand-section-title">Título</h2> </div> -
Usar transparencias para hover states:
<!-- ✅ CORRECTO --> <button class="hover:bg-[var(--brand-primary)]/10"> Botón </button>
❌ NUNCA Hacer
-
NO usar colores hardcoded:
<!-- ❌ INCORRECTO --> <div class="bg-[#1f180f] text-[#fef9f0]"> Contenido </div> -
NO usar clases gray-scale genéricas:
<!-- ❌ INCORRECTO --> <div class="bg-gray-900 text-gray-400 border-gray-800"> Contenido </div> -
NO mezclar variables con hardcoded:
<!-- ❌ INCORRECTO --> <div class="bg-[#1f180f] text-[var(--brand-text)]"> Contenido </div>
Clases Utilitarias
.brand-shell
Contenedor principal con gradiente radial.
<div class="brand-shell">
<!-- Contenido de la página -->
</div>
.brand-card
Tarjetas con estilos consistentes (gradiente, borde, sombra).
<div class="brand-card">
<h3>Título</h3>
<p>Contenido</p>
</div>
.brand-divider
Divisor horizontal decorativo.
<div class="brand-divider"></div>
.brand-section-title
Títulos de sección con color primario.
<h2 class="brand-section-title">Mi Sección</h2>
.brand-chip / .brand-pill
Tags o badges pequeños.
<span class="brand-chip">Estado</span>
<span class="brand-pill">Badge</span>
.brand-badge
Badge con fondo primario sólido.
<span class="brand-badge">3</span>
.brand-table
Estilos para tablas.
<table class="brand-table">
<thead>...</thead>
<tbody>...</tbody>
</table>
Composable useTheme()
Importar
const {
theme,
applyTheme,
saveTheme,
resetTheme,
exportTheme,
importTheme
} = useTheme()
API
theme (Ref)
Estado reactivo del tema actual.
interface ThemeColors {
bg: string
surface: string
border: string
primary: string
primaryStrong: string
accent: string
text: string
textMuted: string
}
applyTheme(newTheme?: ThemeColors)
Aplica el tema a las variables CSS del DOM.
applyTheme() // Aplica theme.value actual
applyTheme(customTheme) // Aplica un tema personalizado
saveTheme()
Guarda el tema actual en localStorage.
const success = saveTheme()
if (success) {
console.log('Tema guardado')
}
resetTheme()
Resetea al tema por defecto (Café).
resetTheme()
exportTheme()
Exporta el tema actual como JSON string.
const jsonTheme = exportTheme()
await navigator.clipboard.writeText(jsonTheme)
importTheme(jsonString: string, autoSave: boolean = false)
Importa un tema desde JSON string.
const success = importTheme(jsonString, true) // true = guardar automáticamente
Ejemplos de Uso
Ejemplo 1: Card Básica
<template>
<UCard
:ui="{
base: 'bg-[var(--brand-surface)]',
ring: 'ring-1 ring-[var(--brand-border)]',
header: { base: 'border-b border-[var(--brand-border)]' }
}"
>
<template #header>
<h3 class="text-[var(--brand-text)]">Mi Card</h3>
</template>
<p class="text-[var(--brand-text-muted)]">
Descripción del contenido
</p>
</UCard>
</template>
Ejemplo 2: Botón con Hover
<template>
<button
class="px-4 py-2 rounded-lg bg-[var(--brand-surface)] border border-[var(--brand-border)] text-[var(--brand-text)] hover:bg-[var(--brand-primary)]/10 hover:border-[var(--brand-primary)] transition-colors"
>
Mi Botón
</button>
</template>
Ejemplo 3: Input Personalizado
<template>
<UInput
v-model="value"
placeholder="Ingresa un valor"
:ui="{
base: 'bg-[var(--brand-surface)] border-[var(--brand-border)] text-[var(--brand-text)]',
placeholder: 'placeholder:text-[var(--brand-text-muted)]'
}"
/>
</template>
Ejemplo 4: Link con Hover
<template>
<NuxtLink
to="/perfil"
class="text-[var(--brand-accent)] hover:text-[var(--brand-primary)] hover:underline transition-colors"
>
Ver perfil →
</NuxtLink>
</template>
Ejemplo 5: Iconos
<template>
<UIcon
name="i-lucide-star"
class="size-5 text-[var(--brand-primary)]"
/>
<UIcon
name="i-lucide-info"
class="size-4 text-[var(--brand-accent)]"
/>
</template>
Ejemplo 6: Manipular Tema Programáticamente
<script setup lang="ts">
const { theme, applyTheme, saveTheme } = useTheme()
// Modificar un color
const cambiarColorPrimario = () => {
theme.value.primary = '#60a5fa'
applyTheme()
saveTheme()
}
// Aplicar tema personalizado completo
const aplicarTemaAzul = () => {
theme.value = {
bg: '#0a0e1a',
surface: '#151a28',
border: '#2d3748',
primary: '#60a5fa',
primaryStrong: '#3b82f6',
accent: '#93c5fd',
text: '#f0f4f8',
textMuted: '#cbd5e1',
// Colores de café (mantener consistentes)
coffeeUva: '#a855f7',
coffeeOreado: '#f97316',
coffeeMojado: '#06b6d4',
coffeeVerde: '#22c55e',
// Colores de estados (mantener consistentes)
statusPendiente: '#f59e0b',
statusPagado: '#10b981',
statusAnulado: '#6b7280'
}
applyTheme()
saveTheme()
}
</script>
<template>
<div>
<button @click="cambiarColorPrimario">Cambiar Primary</button>
<button @click="aplicarTemaAzul">Aplicar Tema Azul</button>
</div>
</template>
Ejemplo 7: Usar Colores de Tipos de Café
<template>
<!-- Badge para tipo de café -->
<UBadge
:style="{ backgroundColor: 'var(--coffee-uva)', color: 'white' }"
size="sm"
>
Café Uva
</UBadge>
<!-- Indicador visual de tipo -->
<div class="flex items-center gap-2">
<div class="w-3 h-3 rounded-full" style="background: var(--coffee-oreado)"></div>
<span>Café Oreado</span>
</div>
<!-- En gráficas (ejemplo con Chart.js) -->
<script setup>
const chartData = {
datasets: [{
label: 'Uva',
backgroundColor: 'var(--coffee-uva)',
data: [10, 20, 30]
}, {
label: 'Oreado',
backgroundColor: 'var(--coffee-oreado)',
data: [15, 25, 35]
}]
}
</script>
</template>
Ejemplo 8: Usar Colores de Estados
<template>
<!-- Badge de estado pendiente -->
<UBadge
class="text-[var(--status-pendiente)] bg-[var(--status-pendiente)]/10 border border-[var(--status-pendiente)]/30"
size="sm"
>
Pendiente
</UBadge>
<!-- Badge de estado pagado -->
<UBadge
class="text-[var(--status-pagado)] bg-[var(--status-pagado)]/10 border border-[var(--status-pagado)]/30"
size="sm"
>
Pagado
</UBadge>
<!-- Badge de estado anulado -->
<UBadge
class="text-[var(--status-anulado)] bg-[var(--status-anulado)]/10 border border-[var(--status-anulado)]/30"
size="sm"
>
Anulado
</UBadge>
<!-- Indicador en tabla -->
<td>
<div class="flex items-center gap-2">
<div class="w-2 h-2 rounded-full" :style="{ backgroundColor: 'var(--status-pagado)' }"></div>
<span>Pagado</span>
</div>
</td>
</template>
Integración con Nuxt UI
Componentes que Respetan el Tema Automáticamente
Los siguientes componentes de Nuxt UI NO necesitan customización porque usan --ui-color-neutral-* internamente:
UButton(concolor="neutral")UBadge(concolor="neutral")UInput(parcialmente)UTextarea(parcialmente)USelect(parcialmente)- Componentes de navegación (sidebar, navbar)
Componentes que Necesitan :ui Prop
Para estos componentes, debes especificar colores explícitamente:
UCard
<UCard
:ui="{
base: 'bg-[var(--brand-surface)]',
ring: 'ring-1 ring-[var(--brand-border)]',
body: { padding: 'p-6' },
header: { base: 'border-b border-[var(--brand-border)]' }
}"
>
<!-- Contenido -->
</UCard>
UModal
<UModal
v-model="isOpen"
:ui="{
background: 'bg-[var(--brand-surface)]',
ring: 'ring-1 ring-[var(--brand-border)]',
header: { base: 'border-b border-[var(--brand-border)]' }
}"
>
<!-- Contenido -->
</UModal>
UDropdown
<UDropdown
:items="items"
:ui="{
background: 'bg-[var(--brand-surface)]',
ring: 'ring-1 ring-[var(--brand-border)]',
item: {
base: 'hover:bg-[var(--brand-primary)]/10'
}
}"
>
<!-- Contenido -->
</UDropdown>
Casos Especiales
Colores Semánticos (Success, Error, Warning)
Para estados semánticos, SÍ puedes usar colores fijos de Tailwind porque no forman parte del tema personalizable:
<template>
<!-- ✅ CORRECTO - Estados semánticos -->
<div class="text-green-400">Operación exitosa</div>
<div class="text-red-400">Error al procesar</div>
<div class="text-yellow-400">Advertencia</div>
<div class="text-blue-400">Información</div>
<!-- Pero el fondo y borde deben respetar el tema -->
<div class="bg-[var(--brand-surface)] border-green-600/30 text-green-400">
Éxito con tema
</div>
</template>
Imágenes y Avatares
<template>
<!-- Avatar con ring temático -->
<UAvatar
src="/avatar.jpg"
:ui="{
wrapper: 'ring-2 ring-[var(--brand-primary)]/40'
}"
/>
<!-- Imagen con borde -->
<img
src="/logo.png"
class="rounded-full border-2 border-[var(--brand-accent)]/40 shadow-lg shadow-[var(--brand-primary-strong)]/25"
/>
</template>
Gradientes
<template>
<!-- Gradiente usando variables -->
<div
class="bg-gradient-to-br"
:style="{
'--tw-gradient-from': 'var(--brand-primary)',
'--tw-gradient-to': 'var(--brand-accent)'
}"
>
Contenido con gradiente
</div>
</template>
Troubleshooting
Problema 1: Texto Invisible
Síntoma: El texto no se ve en algunos componentes.
Causa: Estás usando un color de fondo donde debería ir un color de texto.
Solución:
<!-- ❌ INCORRECTO -->
<div class="bg-[var(--brand-text)] text-[var(--brand-bg)]">
<!-- ✅ CORRECTO -->
<div class="bg-[var(--brand-surface)] text-[var(--brand-text)]">
Problema 2: Componentes No Cambian con el Tema
Síntoma: Algunos componentes mantienen colores fijos cuando cambias de tema.
Causa: Estás usando colores hardcoded o clases gray-scale.
Solución: Reemplaza todos los colores fijos con variables --brand-*.
Problema 3: UCard con Fondo Incorrecto
Síntoma: UCard tiene un fondo que no corresponde al tema.
Causa: Falta especificar la prop :ui.
Solución:
<UCard
:ui="{
base: 'bg-[var(--brand-surface)]',
ring: 'ring-1 ring-[var(--brand-border)]'
}"
>
<!-- Contenido -->
</UCard>
Problema 4: Hover No Se Ve
Síntoma: Los hover effects no son visibles.
Causa: Estás usando colores sólidos en lugar de transparencias.
Solución:
<!-- ❌ INCORRECTO -->
<button class="hover:bg-[var(--brand-primary)]">
<!-- ✅ CORRECTO -->
<button class="hover:bg-[var(--brand-primary)]/10">
Checklist Pre-Commit
Antes de hacer commit, verifica:
- No hay colores hexadecimales hardcoded (
#ffffff,#000000, etc.) - No hay clases gray-scale genéricas (
bg-gray-900,text-gray-500, etc.) - Todos los fondos usan
--brand-bgo--brand-surface - Todos los bordes usan
--brand-border - Todo el texto usa
--brand-texto--brand-text-muted - Los elementos interactivos usan
--brand-primaryo--brand-accent - Los hover states usan transparencias (
/10,/20, etc.) - Se usan clases utilitarias cuando sea posible
Resumen de Comandos Rápidos
<!-- VARIABLES BASE -->
<!-- Fondos -->
bg-[var(--brand-bg)] <!-- Fondo principal -->
bg-[var(--brand-surface)] <!-- Cards, modales -->
<!-- Bordes -->
border-[var(--brand-border)]
<!-- Texto -->
text-[var(--brand-text)] <!-- Texto principal -->
text-[var(--brand-text-muted)] <!-- Texto secundario -->
<!-- Colores de marca -->
text-[var(--brand-primary)] <!-- Links, títulos -->
text-[var(--brand-accent)] <!-- Highlights -->
bg-[var(--brand-primary-strong)] <!-- Backgrounds intensos -->
<!-- Hover (siempre con transparencia) -->
hover:bg-[var(--brand-primary)]/10
hover:border-[var(--brand-primary)]
hover:text-[var(--brand-accent)]
<!-- VARIABLES DE TIPOS DE CAFÉ -->
<!-- Colores de café -->
bg-[var(--coffee-uva)] <!-- Purple - Café Uva -->
bg-[var(--coffee-oreado)] <!-- Orange - Café Oreado -->
bg-[var(--coffee-mojado)] <!-- Cyan - Café Mojado -->
bg-[var(--coffee-verde)] <!-- Green - Café Verde -->
<!-- Ejemplo en badges -->
<UBadge :style="{ backgroundColor: 'var(--coffee-uva)', color: 'white' }">
Café Uva
</UBadge>
<!-- VARIABLES DE ESTADOS -->
<!-- Colores de estados -->
text-[var(--status-pendiente)] <!-- Amber - Pendiente -->
text-[var(--status-pagado)] <!-- Green - Pagado -->
text-[var(--status-anulado)] <!-- Gray - Anulado -->
<!-- Ejemplo en badges con fondo suave -->
<UBadge class="text-[var(--status-pagado)] bg-[var(--status-pagado)]/10">
Pagado
</UBadge>
Notas Finales
- Nunca modifiques el mapeo de
--ui-color-neutral-*enmain.csssin entender el sistema completamente - Siempre usa variables CSS para mantener la consistencia del tema
- Prueba todos los temas después de hacer cambios visuales (Café, Azul, Verde, Carbón)
- Usa el composable
useTheme()para manipulación programática - Consulta este documento ante cualquier duda sobre colores
Mantenido por: Claude Code para Núcleo Río Frío Versión del sistema: 2.0 Última revisión: 2025-10-30