Files
analiticaNucleo/nuxt4-app/docs/THEME_SYSTEM.md
josedario87 0beb01c03c
All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 48s
Docs: Actualizar THEME_SYSTEM.md con nuevas variables
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.
2025-10-30 17:58:58 -06:00

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

  1. Introducción
  2. Arquitectura del Sistema
  3. Variables CSS Disponibles
  4. Reglas de Uso Obligatorias
  5. Clases Utilitarias
  6. Composable useTheme()
  7. Ejemplos de Uso
  8. Integración con Nuxt UI
  9. Casos Especiales
  10. 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

  1. Usar variables CSS en lugar de colores hardcoded:

    <!--  CORRECTO -->
    <div class="bg-[var(--brand-surface)] text-[var(--brand-text)]">
      Contenido
    </div>
    
  2. Usar clases utilitarias cuando existan:

    <!--  CORRECTO -->
    <div class="brand-card">
      <h2 class="brand-section-title">Título</h2>
    </div>
    
  3. Usar transparencias para hover states:

    <!--  CORRECTO -->
    <button class="hover:bg-[var(--brand-primary)]/10">
      Botón
    </button>
    

NUNCA Hacer

  1. NO usar colores hardcoded:

    <!--  INCORRECTO -->
    <div class="bg-[#1f180f] text-[#fef9f0]">
      Contenido
    </div>
    
  2. NO usar clases gray-scale genéricas:

    <!--  INCORRECTO -->
    <div class="bg-gray-900 text-gray-400 border-gray-800">
      Contenido
    </div>
    
  3. 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>
<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 (con color="neutral")
  • UBadge (con color="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-bg o --brand-surface
  • Todos los bordes usan --brand-border
  • Todo el texto usa --brand-text o --brand-text-muted
  • Los elementos interactivos usan --brand-primary o --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-* en main.css sin 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