Docs: Crear documentación definitiva del sistema de temas
All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 52s

- Crear THEME_SYSTEM.md como documento único y completo
- Eliminar DEVELOPER_GUIDE.md y THEME_CUSTOMIZATION.md (desactualizados)
- Actualizar README.md con referencia al nuevo documento
- Incluye arquitectura, variables, reglas, ejemplos y troubleshooting
- Documentación clara para usuarios y desarrolladores
This commit is contained in:
2025-10-30 17:18:32 -06:00
parent 5500c83f9f
commit bc02bc6bde
4 changed files with 672 additions and 798 deletions

View File

@@ -0,0 +1,655 @@
# 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](#introducción)
2. [Arquitectura del Sistema](#arquitectura-del-sistema)
3. [Variables CSS Disponibles](#variables-css-disponibles)
4. [Reglas de Uso Obligatorias](#reglas-de-uso-obligatorias)
5. [Clases Utilitarias](#clases-utilitarias)
6. [Composable useTheme()](#composable-usetheme)
7. [Ejemplos de Uso](#ejemplos-de-uso)
8. [Integración con Nuxt UI](#integración-con-nuxt-ui)
9. [Casos Especiales](#casos-especiales)
10. [Troubleshooting](#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 de Tema (`--brand-*`)
Estas son las **ÚNICAS** variables que debes usar en tu código:
| 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 Nuxt UI (`--ui-color-neutral-*`)
⚠️ **NO uses estas variables directamente**. Están mapeadas automáticamente a `--brand-*` en `main.css`.
**Mapeo actual (NO modificar):**
```css
--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:**
```vue
<!-- ✅ CORRECTO -->
<div class="bg-[var(--brand-surface)] text-[var(--brand-text)]">
Contenido
</div>
```
2. **Usar clases utilitarias cuando existan:**
```vue
<!-- ✅ CORRECTO -->
<div class="brand-card">
<h2 class="brand-section-title">Título</h2>
</div>
```
3. **Usar transparencias para hover states:**
```vue
<!-- ✅ CORRECTO -->
<button class="hover:bg-[var(--brand-primary)]/10">
Botón
</button>
```
### ❌ NUNCA Hacer
1. **NO usar colores hardcoded:**
```vue
<!-- ❌ INCORRECTO -->
<div class="bg-[#1f180f] text-[#fef9f0]">
Contenido
</div>
```
2. **NO usar clases gray-scale genéricas:**
```vue
<!-- ❌ INCORRECTO -->
<div class="bg-gray-900 text-gray-400 border-gray-800">
Contenido
</div>
```
3. **NO mezclar variables con hardcoded:**
```vue
<!-- ❌ INCORRECTO -->
<div class="bg-[#1f180f] text-[var(--brand-text)]">
Contenido
</div>
```
---
## Clases Utilitarias
### `.brand-shell`
Contenedor principal con gradiente radial.
```vue
<div class="brand-shell">
<!-- Contenido de la página -->
</div>
```
### `.brand-card`
Tarjetas con estilos consistentes (gradiente, borde, sombra).
```vue
<div class="brand-card">
<h3>Título</h3>
<p>Contenido</p>
</div>
```
### `.brand-divider`
Divisor horizontal decorativo.
```vue
<div class="brand-divider"></div>
```
### `.brand-section-title`
Títulos de sección con color primario.
```vue
<h2 class="brand-section-title">Mi Sección</h2>
```
### `.brand-chip` / `.brand-pill`
Tags o badges pequeños.
```vue
<span class="brand-chip">Estado</span>
<span class="brand-pill">Badge</span>
```
### `.brand-badge`
Badge con fondo primario sólido.
```vue
<span class="brand-badge">3</span>
```
### `.brand-table`
Estilos para tablas.
```vue
<table class="brand-table">
<thead>...</thead>
<tbody>...</tbody>
</table>
```
---
## Composable useTheme()
### Importar
```typescript
const {
theme,
applyTheme,
saveTheme,
resetTheme,
exportTheme,
importTheme
} = useTheme()
```
### API
#### `theme` (Ref<ThemeColors>)
Estado reactivo del tema actual.
```typescript
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.
```typescript
applyTheme() // Aplica theme.value actual
applyTheme(customTheme) // Aplica un tema personalizado
```
#### `saveTheme()`
Guarda el tema actual en localStorage.
```typescript
const success = saveTheme()
if (success) {
console.log('Tema guardado')
}
```
#### `resetTheme()`
Resetea al tema por defecto (Café).
```typescript
resetTheme()
```
#### `exportTheme()`
Exporta el tema actual como JSON string.
```typescript
const jsonTheme = exportTheme()
await navigator.clipboard.writeText(jsonTheme)
```
#### `importTheme(jsonString: string, autoSave: boolean = false)`
Importa un tema desde JSON string.
```typescript
const success = importTheme(jsonString, true) // true = guardar automáticamente
```
---
## Ejemplos de Uso
### Ejemplo 1: Card Básica
```vue
<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
```vue
<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
```vue
<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
```vue
<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
```vue
<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
```vue
<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'
}
applyTheme()
saveTheme()
}
</script>
<template>
<div>
<button @click="cambiarColorPrimario">Cambiar Primary</button>
<button @click="aplicarTemaAzul">Aplicar Tema Azul</button>
</div>
</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
```vue
<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
```vue
<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
```vue
<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:
```vue
<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
```vue
<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
```vue
<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:**
```vue
<!-- ❌ 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:**
```vue
<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:**
```vue
<!-- ❌ 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
```vue
<!-- 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)]
```
---
## 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