Nocturna Eléctrica
Sistema de diseño dark-first, enérgico y de alto contraste. El fondo casi negro es el escenario; el celeste eléctrico es el reflector.
Principios
- Contraste con propósito — el acento solo aparece donde hay acción o dato clave.
- Profundidad por luz — la elevación aclara superficies, no oscurece bordes.
- Energía contenida — paleta vibrante, espaciado disciplinado: nunca ruidosa.
- Datos protagonistas — cifras y gráficas brillan; el chrome se mantiene discreto.
- Movimiento con propósito — la animación explica cambios de estado, nunca decora sola.
Voz y contenido
Español, técnico pero cercano. Tuteo implícito. Frases cortas y declarativas, con energía contenida.
Sentence case en headings y botones: «Crear proyecto», no «Crear Proyecto». Overlines en uppercase con tracking +0.08em.
Verbo + objeto, corto: «Guardar cambios», «Ver informe».
Concretos y accionables: dicen qué pasó y qué hacer.
Siempre en JetBrains Mono o tabular-nums; grandes en dashboards, con count-up al montar.
No se usan. El estado se comunica con color + icono + texto.
Colores
Haz clic en cualquier swatch para copiar su variable CSS.
Acento — Celeste Eléctrico
Secundario — Índigo Profundo
Neutrales — Escala Nocturna
Semánticos
Regla 60-30-10
60% fondos nocturnos · 30% texto y estructura (índigo como fondo de marca) · 10% acento eléctrico y semánticos. El celeste es el único color que grita: un CTA primario por pantalla. Texto masivo en night-100, nunca blanco puro.
Tipografía
Escala (ratio 1.25, base 16px)
Espaciado
Escala base 4px. Aliases de uso: --card-padding: 24px · --section-gap: 64px · --form-gap: 20px · --touch-target: 44px.
Bordes, radios y focus
Borde por defecto 1px night-500; énfasis night-600. Focus ring global de teclado: 2px accent-400 con offset 2px — pruébalo navegando con Tab.
Elevación
Sobre negro las sombras solas no bastan: la elevación combina superficie más clara + sombra + borde sutil.
night-950
night-900 + shadow-1
night-800 + shadow-2
night-800 + borde night-600 + shadow-3
Glow de acento
máximo uno por pantalla
Iconografía
Set sustituto: Lucide — trazo 2px, esquinas redondeadas, fill: none, tamaño default 20px, color currentColor. Tamaños: 20px en botones · 18px en inputs · 16px en chevrons. Sin emoji, sin unicode-como-icono.
Marca / wordmark
No hay logo dibujado: el wordmark es tipográfico — Space Grotesk 700, tracking -0.02em, con el punto separador en accent-400.
Botones
Alturas 36 / 44 / 52px. El primario lleva texto oscuro (contraste ≈ 8.5:1) — nunca blanco. Un solo primario por pantalla.
Ver código
<button class="btn btn--primary">Guardar cambios</button>
<button class="btn btn--secondary">Ver informe</button>
<button class="btn btn--outline">Exportar</button>
<button class="btn btn--ghost">Cancelar</button>
<button class="btn btn--danger">Eliminar cuenta</button>
<!-- Tamaños: .btn--sm (36px) · default (44px) · .btn--lg (52px) -->
<!-- Loading: fijar min-width y sustituir el texto por .spinner -->IconButton
Botón cuadrado solo-icono. Siempre con aria-label.
Ver código
<button class="icon-btn icon-btn--outline" aria-label="Buscar">
<svg>…</svg>
</button>
<!-- Variantes: --outline · --ghost · --accent | Tamaños: --sm (36) · default (44) · --lg (52) -->Inputs y Textarea
Fondo --surface-2, label siempre visible (nunca placeholder-como-label).
Ver código
<div class="field">
<label class="field__label" for="email">Correo</label>
<input class="input" id="email" type="email" placeholder="tu@correo.com">
<span class="field__help">Texto de ayuda.</span>
</div>
<!-- Estados: .input--error (+ .field__error) · .input--success · disabled -->
<!-- Icono leading: envolver en .input-wrap con un svg antes del input -->Select y Menú
Ver código
<div class="select">
<select><option>Opción</option></select>
</div>
<div class="menu">
<button class="menu__item">Duplicar</button>
<button class="menu__item is-selected">Seleccionado</button>
</div>Checkbox, Radio y Switch
Ver código
<label class="check-row"><input type="checkbox" class="checkbox"> Etiqueta</label>
<label class="check-row"><input type="radio" name="g" class="radio"> Opción</label>
<label class="check-row"><input type="checkbox" class="switch"> Activar</label>Badges y Tags
Ver código
<span class="badge badge--success">Activo</span>
<span class="tag">diseño <button class="tag__x" aria-label="Quitar">×</button></span>Avatar
Iniciales en Space Grotesk sobre índigo; punto de estado opcional.
Ver código
<span class="avatar">HS<span class="avatar__dot avatar__dot--online"></span></span>
<!-- Tamaños: --xs 24 · --sm 32 · default 40 · --lg 48 · --xl 64 -->
<!-- Con imagen: <span class="avatar"><img src="…" alt="Nombre"></span> -->Stat
Cifra de dashboard con count-up de 600ms al entrar en pantalla (respeta prefers-reduced-motion).
Ver código
<div class="stat">
<span class="stat__label">Ingresos del mes</span>
<span class="stat__value">$128 450</span>
<span class="stat__delta stat__delta--up">▲ +12,4 %</span>
</div>Card
Tarjeta en reposo
Superficie 1, borde sutil, sombra nivel 1.
Tarjeta interactiva
Al pasar el cursor sube de nivel: −2px + superficie más clara.
Ver código
<div class="card">…</div>
<div class="card card--interactive">…</div>
<!-- .card--glow: firma de marca — MÁXIMO UNO POR PANTALLA -->Tooltip
Delay de 400ms; máximo una línea. Pasa el cursor:
Ver código
<span class="has-tooltip">
<button class="btn btn--outline">Pasa el cursor</button>
<span class="tooltip" role="tooltip">Exportar como CSV</span>
</span>Toast
Top-right, auto-dismiss a los 5s (pausable con hover), máximo 3 apilados.
Ver código
<div class="toast-stack">
<div class="toast toast--success" role="status">
<svg>…</svg>
<div>
<div class="toast__title">Cambios guardados</div>
<div class="toast__body">El proyecto se actualizó correctamente.</div>
</div>
</div>
</div>Modal
Overlay con blur 4px; se cierra con Esc o clic fuera (excepto flujos destructivos).
Ver código
<div class="modal-overlay">
<div class="modal" role="dialog" aria-modal="true" aria-labelledby="mTitle">
<div class="modal__header">
<h4 class="modal__title" id="mTitle">Título</h4>
<button class="icon-btn icon-btn--ghost" aria-label="Cerrar">×</button>
</div>
<p>Contenido…</p>
<div class="modal__footer">
<button class="btn btn--outline">Cancelar</button>
<button class="btn btn--primary">Confirmar</button>
</div>
</div>
</div>Tabla
Ver código
<table class="table">
<thead><tr><th>Proyecto</th><th class="num">Ingresos</th></tr></thead>
<tbody>
<tr><td>Panel analítico</td><td class="num">$48 210</td></tr>
<tr class="is-selected">…</tr>
</tbody>
</table>Accordion
Ver código
<div class="accordion">
<div class="accordion__item">
<button class="accordion__header" aria-expanded="false">
Pregunta <svg>chevron</svg>
</button>
<div class="accordion__panel"><div class="accordion__inner">
<div class="accordion__content">Respuesta…</div>
</div></div>
</div>
</div>
<!-- JS: alternar aria-expanded en el header -->ProgressBar
Ver código
<div class="progress__head">
<span class="progress__label">Subiendo…</span>
<span class="progress__value">64 %</span>
</div>
<div class="progress" role="progressbar" aria-valuenow="64" aria-valuemin="0" aria-valuemax="100">
<div class="progress__bar" style="width:64%"></div>
</div>
<!-- Variantes: .progress--indeterminate · .progress--no-glow -->Link y Code
Ver código
<a class="link" href="…">Link con subrayado animado</a>
<span class="code">npm i nocturna</span>Skeleton y estados vacíos
Ver código
<span class="skeleton" style="height:12px;width:70%"></span>
<span class="skeleton skeleton--circle" style="width:40px;height:40px"></span>
<div class="empty-state">
<svg>…</svg>
<span class="empty-state__title">Aún no hay proyectos</span>
<span class="empty-state__desc">Crea tu primer proyecto…</span>
<button class="btn btn--primary btn--sm">Crear proyecto</button>
</div>Spinner
Trazo 2px, rotación 0.8s. Si la carga dura menos de 300ms, no mostrar spinner.
Ver código
<span class="spinner"></span>
<!-- Hereda currentColor; default accent-400 -->Motion
Solo transform y opacity. Salida ~20% más corta que la entrada. Distancias de 4–8px. Stagger 30ms/ítem (máximo 6).
Firma de marca: los elementos con acento entran con fade + glow — solo en el elemento hero/CTA. prefers-reduced-motion colapsa todo a 0.01ms.
Accesibilidad
- Mínimos WCAG AA: 4.5:1 texto normal, 3:1 texto grande y componentes UI.
- Focus visible siempre (:focus-visible global); estado nunca solo con color — siempre color + icono + texto.
- Touch targets de 44×44px. IconButton siempre con aria-label.
- role="progressbar" en Progress, aria-expanded en Accordion, aria-current en navegación.
Tokens y descarga
El sistema se distribuye en dist/nocturna-electrica/: nocturna-electrica.css (framework completo), tokens.css (solo variables), tailwind.css (bloque @theme para Tailwind v4) y tokens.json (formato estándar W3C DTCG 2025.10, interoperable con Figma / Tokens Studio / Style Dictionary).
Variables CSS (núcleo)
tokens :root (aliases semánticos)
:root {
/* Superficies */
--bg: #010203;
--surface-1: #0b0d12; --surface-2: #14171f; --surface-3: #1e222d;
--border: #3d4354; --border-strong: #2a2f3d;
/* Texto */
--text-primary: #e8eaef; --text-secondary: #c5c9d4;
--text-muted: #9aa0b0; --text-disabled: #6b7183;
--text-heading: #f6f7f9; --text-on-accent: #010203;
/* Acento e índigo */
--accent-400: #52baed; --accent-500: #2fa3dd; --accent-600: #1f87bb;
--indigo-500: #353d7c; --indigo-700: #22284f; --indigo-800: #1a1e3b;
--accent-ghost-hover: rgba(82,186,237,.08);
--accent-selected: rgba(82,186,237,.12);
/* Semánticos */
--success: #4ade80; --warning: #fbbf24; --danger: #f87171; --info: #52baed;
/* Tipografía */
--font-display: "Space Grotesk", system-ui, sans-serif;
--font-body: "Manrope", system-ui, sans-serif;
--font-mono: "JetBrains Mono", monospace;
/* Radios y elevación */
--radius-sm: 8px; --radius-md: 12px; --radius-lg: 16px; --radius-full: 9999px;
--shadow-1: 0 1px 2px rgba(0,0,0,.4);
--shadow-2: 0 4px 12px rgba(0,0,0,.5);
--shadow-3: 0 12px 32px rgba(0,0,0,.6);
--glow-accent: 0 0 0 1px rgba(82,186,237,.35), 0 4px 20px rgba(82,186,237,.18);
--ring: 0 0 0 3px rgba(82,186,237,.2);
/* Motion */
--duration-fast: 150ms; --duration-base: 220ms; --duration-slow: 250ms;
--ease-out: cubic-bezier(0.2, 0, 0, 1);
--ease-spring: cubic-bezier(0.34, 1.4, 0.64, 1);
}Tailwind CSS v4 — @theme
tailwind.css
@import "tailwindcss";
@theme {
--color-night-950: #010203; --color-night-900: #0b0d12;
--color-night-800: #14171f; --color-night-700: #1e222d;
--color-night-600: #2a2f3d; --color-night-500: #3d4354;
--color-night-400: #6b7183; --color-night-300: #9aa0b0;
--color-night-200: #c5c9d4; --color-night-100: #e8eaef;
--color-night-50: #f6f7f9;
--color-accent-100: #d9f0fb; --color-accent-200: #a8ddf6;
--color-accent-300: #7dccf1; --color-accent-400: #52baed;
--color-accent-500: #2fa3dd; --color-accent-600: #1f87bb;
--color-accent-700: #186a94;
--color-indigo-300: #7a83c4; --color-indigo-400: #565fa3;
--color-indigo-500: #353d7c; --color-indigo-600: #2b3264;
--color-indigo-700: #22284f; --color-indigo-800: #1a1e3b;
--color-success: #4ade80; --color-warning: #fbbf24;
--color-danger: #f87171; --color-info: #52baed;
--font-display: "Space Grotesk", system-ui, sans-serif;
--font-body: "Manrope", system-ui, sans-serif;
--font-mono: "JetBrains Mono", monospace;
--radius-sm: 8px; --radius-md: 12px; --radius-lg: 16px; --radius-xl: 24px;
--shadow-1: 0 1px 2px rgba(0,0,0,.4);
--shadow-2: 0 4px 12px rgba(0,0,0,.5);
--shadow-3: 0 12px 32px rgba(0,0,0,.6);
--shadow-glow: 0 0 0 1px rgba(82,186,237,.35), 0 4px 20px rgba(82,186,237,.18);
--ease-out: cubic-bezier(0.2, 0, 0, 1);
--ease-in: cubic-bezier(0.4, 0, 1, 1);
--ease-spring: cubic-bezier(0.34, 1.4, 0.64, 1);
}