Resumen — qué hace
La consola de InfraGuard se entrega con dos temas: oscuro (predeterminado) y claro. El tema se cambia mediante un único atributo en la raíz del documento (<html>), data-theme, cuyo valor intercambia todo el conjunto de tokens de variable CSS --infr-* definidos en SCSS. Todos los componentes usan tokens var(--infr-*) en lugar de valores hexadecimales fijos, así que cambiar una sola palabra (dark ↔ light) vuelve a pintar al instante toda la interfaz.
El valor predeterminado es oscuro: al tratarse de una consola de NOC (centro de operaciones de red), usa un fondo azul-negro profundo con acentos de señal en verde azulado, de modo que los colores de gravedad (crit / warn / good) se lean con nitidez sobre un campo oscuro.
<html data-theme="dark"> ← predeterminado, oscuro de NOC
<html data-theme="light"> ← claro (entornos de oficina luminosos)
El tema es una preferencia puramente de front-end. Nunca se almacena en el servidor y no tiene nada que ver con su cuenta ni con la base de datos: vive únicamente en el
localStoragedel navegador. Sobrevive al cierre de sesión y, si otra persona inicia sesión con la misma cuenta en otro dispositivo, ese dispositivo sigue su propio ajuste.
Cuándo utilizarlo
- Operación nocturna / paneles murales de NOC → oscuro (menos deslumbramiento, mayor contraste de los colores de gravedad).
- Oficinas luminosas, proyectores, capturas para documentación → claro.
- Preferencia personal de contraste o de visión del color: se cambia por usuario.
Una vez elegido persiste en ese navegador, de modo que normalmente solo se configura una vez.
Disposición
Botón de conmutación en la cabecera
Tanto el armazón de la consola (layouts/app.blade.php) como la pantalla de acceso (layouts/simple.blade.php) colocan un botón de cambio de tema a la derecha de la barra superior.
| Elemento | Valor |
|---|---|
| Punto de anclaje en el marcado | <button data-theme-toggle> |
| Icono | mdi-theme-light-dark (media luna) |
| aria-label | msg.action.theme (localizado) |
| Acción | Al hacer clic alterna oscuro ↔ claro |
| Posición | Área de acciones de la barra superior (junto al selector de idioma, a la izquierda del menú de usuario) |
La pantalla previa al acceso (invitado) lleva el mismo botón, de modo que puede cambiar de tema sin autenticarse.
Grupos de tokens de color
resources/scss/_variables.scss define los mismos nombres de token con valores distintos en dos bloques: :root[data-theme="dark"] y :root[data-theme="light"]. Tokens representativos:
| Token | Finalidad | dark | light |
|---|---|---|---|
--infr-bg |
Fondo de la página | #0b1220 |
#f6f8fb |
--infr-surface |
Superficie de panel / tarjeta | #111b2c |
#ffffff |
--infr-fg |
Texto del cuerpo | #e9eff8 |
#111a28 |
--infr-fg-muted |
Texto secundario | #8493ac |
#63728b |
--infr-border |
Bordes | #1e2c43 |
#dbe2ec |
--infr-accent |
Acento (verde azulado) | #2dd4bf |
#0d9488 |
--infr-crit |
Gravedad crítica | #fb7185 |
#e11d48 |
--infr-warn |
Gravedad de advertencia | #fbbf24 |
#b45309 |
--infr-good |
Gravedad saludable | #34d399 |
#0f9d6a |
--infr-info |
Información | #38bdf8 |
#0369a1 |
Más allá de estos, cada tema define además --infr-surface-2/hi, --infr-border-2, --infr-accent-soft/ink, --infr-on-accent (texto de botón sobre el acento), colores de estado TCP (--infr-state-*), colores de gráfico (--infr-chart-2), sombra/resplandor (--infr-shadow / --infr-glow) y el cristal de la barra superior (--infr-topbar-*). Cada bloque declara también color-scheme: dark|light, de modo que los controles de formulario nativos y las barras de desplazamiento coincidan con el tema.
La paleta del terminal SSH es una excepción. Los tokens
--infr-term-*permanecen en un oscuro profundo e inmersivo con independencia dedata-theme. Incluso con el tema claro, la consola SSH mantiene un terminal oscuro.
Cómo utilizarlo
Cambiar de tema
- Haga clic en el icono de media luna (Cambiar tema) a la derecha de la barra superior.
- Alterna oscuro ↔ claro de inmediato y la elección se guarda automáticamente.
- En la siguiente visita o al recargar la página se restaura su última elección.
Bajo el capó (referencia para desarrolladores)
El front-end se conecta en resources/js/app.js. Lógica principal:
const THEME_KEY = 'infr-theme'; // clave de localStorage
const get_theme = () => localStorage.getItem(THEME_KEY) || 'dark'; // oscuro por defecto
const set_theme = (t) => {
document.documentElement.setAttribute('data-theme', t); // atributo de la raíz
localStorage.setItem(THEME_KEY, t); // persistir
window.dispatchEvent(new CustomEvent('infr:theme', { detail: t })); // repintar los gráficos
};
// conectar el botón de conmutación
document.querySelectorAll('[data-theme-toggle]')
.forEach(btn => btn.addEventListener('click',
() => set_theme(get_theme() === 'dark' ? 'light' : 'dark')));
Resumen del comportamiento:
| Paso | Qué |
|---|---|
| Clave de almacenamiento | localStorage['infr-theme'] = "dark" | "light" |
| Se aplica a | atributo data-theme en document.documentElement (el <html>) |
| Predeterminado | dark (cuando localStorage está vacío o no se puede leer) |
| Evento | Cada cambio dispara un CustomEvent infr:theme en window (detail = tema actual) |
| Sincronización entre pestañas | Escucha el evento storage → las demás pestañas del mismo navegador se sincronizan automáticamente |
Prevención del destello de contenido sin estilo (FOUC) — script previo al pintado
Un pequeño script en línea situado al principio de <head> (compartido por app.blade.php y simple.blade.php, antes de la carga de CSS con @vite) fija data-theme antes de que se aplique ninguna hoja de estilo. Esto elimina el "destello oscuro→claro" al recargar.
<script>
(function () {
try { document.documentElement.setAttribute('data-theme',
localStorage.getItem('infr-theme') || 'dark'); }
catch (e) { document.documentElement.setAttribute('data-theme', 'dark'); }
})();
</script>
Incluso donde
localStorageestá bloqueado (modo incógnito, almacenamiento de terceros bloqueado, etc.), elcatchrecurre de forma segura a oscuro.
Los gráficos y los editores siguen al tema
- ECharts (
<metric-chart>/ echarts-kit): lee los tokens--infr-*mediantegetComputedStylepara construir colores de gráfico, ejes e informaciones sobre herramientas, y repinta con el eventoinfr:theme. La comprobaciónis_dark()esdata-theme !== 'light'(oscuro es el valor predeterminado). Véase Series temporales de servidores. - CodeMirror (
<cm-editor>): la propiedad:darkalterna entreoneDark(oscuro) y un tema claro. Las páginas Vue calculanis_dark_mode = document.documentElement.dataset.theme === 'dark', lo pasan como:dark="is_dark_mode"y lo refrescan con el eventoinfr:theme. Véase Generador de scripts de copia de seguridad.
<cm-editor v-model="script.body" lang="bash" :dark="is_dark_mode"></cm-editor>
Fuentes de datos
El tema no tiene absolutamente ningún almacenamiento en base de datos ni en el servidor. Solo utiliza:
| Ubicación | Contenido |
|---|---|
localStorage['infr-theme'] |
Elección del usuario por navegador (dark/light) |
<html data-theme> |
Estado aplicado en tiempo de ejecución |
resources/scss/_variables.scss |
Valores reales de los tokens --infr-* para cada tema (compilados en el CSS) |
resources/js/app.js |
Conmutación / persistencia / conexión de eventos |
El apéndice C (tokens del tema claro/oscuro) de CLAUDE.md es la fuente de verdad del diseño de la paleta.
Acceso
El tema es una preferencia de interfaz ajena a la autenticación y a la autorización.
- Todos los usuarios, incluidos los invitados (antes del acceso), pueden cambiarlo libremente: no hay control por nivel.
- Se comporta de forma idéntica con demo=1 / staff=5 / admin=10.
- Como no se escribe nada en el servidor, nunca aparece en el registro de auditoría (
infr_audit_log).
Resolución de problemas y preguntas frecuentes
P. El tema se reinicia al recargar.
Si el navegador bloquea localStorage (modo incógnito, ajustes estrictos de privacidad, bloqueo del almacenamiento de terceros), la elección no puede guardarse y vuelve cada vez al valor predeterminado oscuro. Conceda acceso al almacenamiento para el sitio.
P. Lo cambié en una pestaña, pero otra pestaña no se actualizó.
El cambio se propaga a las demás pestañas del mismo navegador mediante el evento storage. Si no se refleja de inmediato, puede que esa pestaña todavía no haya cargado la conexión del escuchador (app.js): recárguela. Los dispositivos/navegadores distintos tienen almacenamientos separados y no se sincronizan (así está diseñado).
P. Cambié a claro, pero el terminal SSH sigue oscuro.
Es lo esperado. El terminal usa una paleta dedicada --infr-term-* que permanece muy oscura con independencia del tema de la aplicación (legibilidad / inmersión).
P. Un componente se ve mal (el tema no se le aplica).
Ese componente fijó valores hexadecimales en lugar de tokens. Por convención, los componentes deben usar únicamente tokens var(--infr-*). Busque valores hexadecimales directos en resources/scss/pages con el grep de regresión:
rg -n "#[0-9a-fA-F]{3,6}" resources/scss/pages
P. Quiero que siga el modo oscuro de mi sistema operativo.
La implementación actual no sigue la preferencia del sistema operativo (prefers-color-scheme). El valor inicial es siempre dark y solo recuerda lo que el usuario eligió explícitamente con el botón. La integración con el sistema operativo es un elemento de la hoja de ruta.
Relacionado
- Navegación y disposición — el área de acciones de la barra superior (tema / idioma / menú de usuario)
- Idioma · configuración regional — la otra preferencia de front-end junto al tema
- Buscar en este manual — búsqueda y estructura de /manual
- Series temporales de servidores y MetricChart — gráficos que se repintan a partir de los tokens del tema
- Generador de scripts de copia de seguridad — el enlace
:darkde CodeMirror - Consola SSH — un terminal fijo en oscuro, independiente del tema de la aplicación
