Tema
Dashboard de administración
Panel de administración completo hecho con HTML, CSS y JavaScript puro, sin librerías: sidebar colapsable, tarjetas de KPIs, gráfica de barras y gráfica de dona en SVG con tooltips y animación, tabla de pedidos con búsqueda, filtro, orden por columna y paginación, y modo oscuro persistente.
Sirve como base para el panel de cualquier sistema que construyas para un cliente: tienda, CRM, escuela, inventario, reservas...
Vista previa
Prueba: cambia el periodo (7 días / 30 días / 12 meses), pasa el mouse sobre las barras, ordena la tabla haciendo clic en los encabezados, busca un cliente, colapsa el menú con ☰ y activa el modo oscuro.
Estructura del layout
txt
.app (grid: sidebar | principal)
├── aside.sidebar ← menú lateral (fijo en escritorio, deslizable en móvil)
└── .principal
├── header.topbar ← botón de menú, búsqueda, tema, notificaciones, usuario
└── main.contenido
├── .cabecera ← saludo + selector de periodo
├── .kpis ← 4 tarjetas de indicadores
├── .fila-graficas← barras (2/3) + dona (1/3)
└── tabla de pedidosEl sidebar colapsable se logra cambiando una sola variable CSS:
css
.app { display: grid; grid-template-columns: var(--ancho-sidebar) 1fr; transition: grid-template-columns .3s; }
.app.colapsado { --ancho-sidebar: 72px; }
.app.colapsado .texto-menu { opacity: 0; } /* sólo se ven los iconos */En pantallas pequeñas el sidebar sale de la pantalla (translateX(-100%)) y se abre encima del contenido con una capa oscura.
Gráficas sin librerías
Barras en SVG
pintarGrafica() calcula la escala a partir del valor máximo, dibuja la rejilla, las etiquetas de los ejes y dos barras por grupo (periodo actual y anterior). Las barras crecen con una transición CSS y muestran un tooltip con el valor exacto:
js
const max = Math.max(...actual, ...anterior) * 1.1; // 10 % de aire arriba
const altoBarra = (valor / max) * h; // regla de tres
const rect = svgEl('rect', { x, y: m.arriba + h, width: anchoBarra, height: 0, rx: 4, class: 'barra' });
requestAnimationFrame(() => { // anima de 0 a su altura
rect.style.transition = 'y .6s ease, height .6s ease';
rect.setAttribute('y', m.arriba + h - altoBarra);
rect.setAttribute('height', altoBarra);
});Los colores salen de variables CSS (fill: var(--primario)), así que la gráfica cambia sola con el modo oscuro.
Dona con stroke-dasharray
El truco: un círculo con radio 15.915 tiene una circunferencia de casi exactamente 100, así que cada porcentaje equivale a una unidad del trazo:
js
const r = 15.915;
svg.append(svgEl('circle', {
cx: 21, cy: 21, r, fill: 'none', stroke: c.color, 'stroke-width': 6,
'stroke-dasharray': `${pct} ${100 - pct}`, // tramo visible y tramo vacío
'stroke-dashoffset': 25 - acumulado, // empieza donde terminó el anterior
}));¿Cuándo usar una librería?
Para un panel con 2–3 gráficas simples, SVG a mano pesa 0 KB extra. Si el cliente pide gráficas de líneas con zoom, muchas series o exportar a imagen, usa Chart.js o ApexCharts desde un CDN.
Tabla con búsqueda, filtro, orden y paginación
Todo el comportamiento depende de un objeto de estado:
js
const estadoTabla = { texto: '', estado: '', orden: 'fecha', dir: -1, pagina: 1, porPagina: 8 };pintarTabla() aplica en orden: filtrar → ordenar → paginar → pintar. Los encabezados con data-orden cambian la columna y alternan la dirección; la búsqueda usa un pequeño debounce de 200 ms. Para conectar datos reales, sustituye PEDIDOS por la respuesta de tu API:
js
const res = await fetch('/api/pedidos?limite=100');
const { data } = await res.json();
PEDIDOS.splice(0, PEDIDOS.length, ...data.map((p) => ({ ...p, fecha: new Date(p.fecha) })));
pintarTabla();Con miles de registros, haz la búsqueda, el orden y la paginación en el servidor (paginación en Node o PHP) y envía ?q=&orden=&dir=&pagina= en cada cambio.
Conectar los KPIs a datos reales
Las consultas SQL que alimentan este tipo de panel están en Consultas útiles: ventas por día, ticket promedio, comparación contra el periodo anterior y ventas por categoría. Un endpoint típico devolvería:
json
{
"kpis": { "ventas": 186400, "ventasAnterior": 171200, "pedidos": 412, "ticketPromedio": 452.4 },
"serie": { "etiquetas": ["1 oct", "3 oct"], "actual": [6100, 7200], "anterior": [5400, 6800] },
"categorias": [{ "nombre": "Café", "valor": 48200 }]
}Personalización
css
:root {
--primario: #4f46e5; /* botones activos, barras, enlaces */
--sidebar: #111827; /* fondo del menú lateral */
--fondo: #f4f6fb;
--radio: 14px; /* redondeo de tarjetas */
}
[data-tema="oscuro"] { /* mismas variables, versión oscura */ }- Menú: edita los enlaces del
aside; el contador rojo es unspan class="badge". - KPIs: cambia el arreglo
datosdentro depintarKpis()(etiqueta, valor, variación, icono y color de fondo). - Estados de pedidos: cada estado tiene una clase
e-nombrecon su color.
Protegerlo
Un dashboard nunca debe ser público. Ponlo detrás de un login:
- PHP: registro, login y logout + protección por roles.
- Node: autenticación JWT con el middleware
autorizar('admin'). - El proyecto Blog/CMS en PHP tiene un panel admin completo con login que puedes combinar con este diseño.
Accesibilidad de las gráficas
Las gráficas SVG tienen role="img" y aria-label. Si los datos son críticos, ofrece también una tabla o un botón "Ver datos" para quienes usan lector de pantalla.