Apps internas — SiteFlow
El shell que comparten los sistemas internos de Grupo Mecsa: sidebar plegable, topbar con launcher, ayuda contextual y modo claro/oscuro. Si vas a construir una app nueva, esta es la sección que tenés que leer.
Quién lo usa hoy
Todas estas apps corren el mismo shell. Cualquier cambio acá se propaga copiando
los archivos de starter/components/.
| Sistema | Dominio | Carpeta local |
|---|---|---|
| Proyectos | proyectos.grupomecsa.net | GrupoMecsaProyectos/ |
| Operaciones | ops.grupomecsa.net | MecsaOPS/ |
| Talento Humano | rh.grupomecsa.net | MecsaRH/ |
| Documentos | mecsadocs.grupomecsa.net | MecsaDocs/ |
| SICOP | sicop.grupomecsa.net | sicop/ |
| Universidad | universidad.grupomecsa.net | UniversidadMecsa/ |
| Pulpería | pulperia.grupomecsa.net | PulperiaAsomecsa/ |
Vista en vivo
Este es el starter real corriendo. Probá el menú waffle, el botón «?» y el interruptor de modo oscuro (menú del avatar).
Tokens
Todo el color sale de variables CSS. Nunca escribas un HEX dentro de un
componente: si usás el token, el modo oscuro sale gratis. La fuente canónica es
styles/tokens.css.
| Token | Claro | Oscuro | Para qué |
|---|---|---|---|
--brand | #013483 | #5b8def | Color de acción, links, estado activo |
--accent | #f7941d | Acentos, viñetas, tips | |
--bg-app | #f8f9fa | #0b1120 | Fondo de la página |
--bg-surface | #ffffff | #131c2e | Tarjetas, modales, dropdowns |
--bg-surface-2 | #f8f9fa | #1a2436 | Cabeceras de tarjeta, hovers |
--text | #111827 | #e8edf7 | Texto principal |
--text-2 | #4b5563 | #b6c2d6 | Texto secundario |
--text-muted | #9ca3af | #7f8ea8 | Metadatos, placeholders |
--border | #eaeaea | #263248 | Bordes y separadores |
--sidebar-* | 14 variables | Fondo, texto, activo y borde de la sidebar | |
--topbar-* | 3 variables | Degradado y borde del topbar | |
<!-- 1. tokens primero, shell después -->
<link rel="stylesheet" href="styles/tokens.css">
<link rel="stylesheet" href="styles/app-shell.css">
<!-- 2. el tema se marca en el <html> -->
<html data-theme="light"> <!-- o data-theme="dark" -->/* ✅ así */
.mi-tarjeta {
background: var(--bg-surface);
color: var(--text);
border: 1px solid var(--border);
}
.mi-tarjeta .titulo { color: var(--brand); }
/* ❌ así no: se rompe en modo oscuro */
.mi-tarjeta { background: #fff; color: #111; border: 1px solid #eaeaea; }Esqueleto de página
Todas las pantallas de todas las apps usan esta estructura. El orden importa:
sidebar.php va primero porque trae el script que aplica el tema
antes de pintar (si se deja para el final se ve un destello blanco al entrar en oscuro).
<body class="admin-page-premium">
<?php include 'components/sidebar.php'; ?> <!-- 1º: aplica el tema -->
<div class="cms-main-content">
<?php include 'components/header.php'; ?> <!-- topbar + launcher + ayuda -->
<main class="page-content">
... el contenido de la pantalla ...
</main>
<?php include 'components/footer.php'; ?>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
<script src="js/animations.js" defer></script>
</body>Topbar y breadcrumb
60px de alto, tres zonas: izquierda (hamburguesa + breadcrumb), centro (búsqueda global, opcional) y derecha (launcher, ayuda, campana, avatar).
El breadcrumb se declara por pantalla, antes de incluir el header:
$GM_BREADCRUMB = ['Comercial', 'Ofertas']; // App › Comercial › Ofertas
$GM_BREADCRUMB = ['Dashboard']; // App › DashboardClases: .cms-topbar, .topbar-left/-search/-right,
.topbar-breadcrumb (.bc-link, .bc-sep, .bc-current),
.topbar-icon-btn, .topbar-avatar-btn, .notif-dropdown,
.user-dropdown.
GrupoMecsaProyectos/components/header.php — copiala y cambiale el endpoint.
Launcher de sistemas canónico
El menú waffle que salta entre sistemas. Es autocontenido (HTML + CSS + JS en un
archivo), marca solo el sistema actual comparando HTTP_HOST y se incluye dentro de
.topbar-right.
<div class="topbar-right">
<?php include __DIR__ . '/gm_launcher.php'; ?>
...
</div>Sistemas en el menú
$GM_SISTEMAS en
starter/components/gm_launcher.php (esta es la fuente canónica) y copiá el archivo
a Proyectos, MecsaDocs, Universidad y SICOP. En OPS y RH la lista está embebida dentro de
components/header.php.
Modo claro / oscuro
Tres reglas y funciona en toda la app:
- El tema vive en
<html data-theme="light|dark">. - Se aplica antes de pintar (script al inicio de
sidebar.php), si no se ve un destello blanco. - Si el usuario nunca eligió, manda el sistema operativo — y se sigue en vivo.
<script>
(function () {
var t;
try { t = localStorage.getItem('theme'); } catch (e) {}
if (t !== 'dark' && t !== 'light') {
t = (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches)
? 'dark' : 'light';
}
document.documentElement.setAttribute('data-theme', t);
})();
</script>El interruptor vive en el menú del avatar (#themeToggle) y guarda
la elección en localStorage.theme. El dropdown lleva
data-bs-auto-close="outside" para que no se cierre al cambiar de tema.
Ayuda contextual y manual
El botón «?» del topbar abre un panel lateral con la ayuda de esa pantalla.
Se incluye una sola vez desde header.php, así que aparece en todas las páginas sin
tocar cada vista. Documentar una pantalla nueva es agregar una entrada:
// components/help.php
$helpContent = [
'ofertas.php' => [
'titulo' => 'Ofertas',
'resumen' => 'Cotizaciones a clientes: cabecera, líneas y consecutivo.',
'puntos' => [
'El código se genera solo al guardar.',
'El IVA depende del ámbito del servicio.',
],
'tip' => 'Una oferta enviada a revisión queda congelada.',
],
];El manual completo es una página aparte
(pages/documentacion.php) con índice y scroll-spy. Está implementado en Proyectos,
OPS y MecsaDocs — copialo de ahí.
Piezas de UI
Tarjeta de contenido
<div class="content-card">
<div class="content-card-header">
<h2 class="content-card-title mb-0">Últimos movimientos</h2>
<button class="btn btn-gm-primary btn-sm"><i class="fas fa-plus me-1"></i> Nuevo</button>
</div>
<div class="content-card-body">
...
</div>
</div>Indicador (KPI)
<div class="content-card h-100">
<div class="content-card-body d-flex align-items-center gap-3">
<span class="bg-primary-soft rounded-3 d-flex align-items-center justify-content-center"
style="width:46px;height:46px;">
<i class="fas fa-diagram-project text-primary"></i>
</span>
<div>
<div class="fs-4 fw-bold gm-counter">24</div>
<div class="small text-muted">Proyectos activos</div>
</div>
</div>
</div>Botones y estados
Botones: .btn-gm-primary, .btn-gm-secondary,
.btn-gm-success, .btn-gm-danger, .btn-gm-add.
Fondos suaves para estados: .bg-primary-soft, .bg-success-soft,
.bg-warning-soft, .bg-info-soft — combinados con
.badge-gm para las etiquetas de estado.
<span class="badge-gm bg-success-soft text-success">Completado</span>
<span class="badge-gm bg-warning-soft text-warning">En proceso</span>
<span class="badge-gm bg-primary-soft text-primary">Programado</span>Animaciones
js/animations.js se encarga de tres cosas, sin configuración:
contadores que suben al entrar en pantalla (.gm-counter), aparición al hacer scroll
y ripple en los botones. Las clases utilitarias de entrada están en el shell:
.gm-fade-in, .gm-fade-in-up, -down, -left,
-right.
prefers-reduced-motion: no agregues animaciones nuevas sin el guard.
Arrancar una app nueva
- Copiá
starter/a tu proyecto. - Editá
config.php: nombre, badge, usuario, links y$GM_NAV. - Definí
$GM_BREADCRUMBen cada pantalla. - Documentá las pantallas en
components/help.php. - Conectá la sesión real en lugar de
$GM_APP['user'].