Sistema completo de inversión semántica de tokens para interfaces oscuras. Mapeo de colores, elevación, componentes y accesibilidad.
El dark mode de Flow no es una simple inversión de colores. Es una reinterpretación consciente del sistema visual que mantiene la jerarquía, accesibilidad y personalidad de marca.
En lugar de invertir colores literalmente (blanco → negro), Flow invierte los roles semánticos de los tokens. Un token llamado surface.primary siempre significa "superficie principal" — en light mode es crema, en dark mode es charcoal. El nombre nunca cambia, solo su valor subyacente.
1. Conservar la jerarquía visual. La relación entre niveles de superficie (primary → secondary → tertiary) se mantiene en ambos modos. La distinción entre capas nunca se pierde.
2. El verde sigue siendo protagonista. El acento verde (#3beb64 / #72f18b) es el elemento de continuidad entre ambos modos. Se ajusta ligeramente para mantener contraste, pero nunca pierde su rol.
3. Nunca negro puro. Las superficies dark usan charcoal (#0f2c24) y darkgreen (#14382e), nunca #000000. El negro puro genera fatiga visual y rompe la calidez de marca.
4. Elevación por luminosidad, no por sombra. En dark mode, las sombras son menos visibles. La percepción de profundidad se logra con superficies ligeramente más claras en capas superiores.
El 70/20/10 de Flow se mantiene en dark mode: 70% superficies oscuras neutras, 20% verde tonal profundo, 10% verde vibrante como acento.
Cada token semántico tiene un valor para light mode y otro para dark mode. Los nombres permanecen idénticos — solo cambian los valores subyacentes.
| Token | Light Mode | Dark Mode | |
|---|---|---|---|
| surface.primary | Cream #f7f5f3 |
→ | Charcoal #0f2c24 |
| surface.secondary | Off-white #f0eee9 |
→ | Darkgreen #14382e |
| surface.tertiary | Almost-white #f8f9f9 |
→ | Deep green #1a4d3a |
| surface.inverse | Charcoal #0f2c24 |
→ | Cream #f7f5f3 |
| surface.accent | Green-500 #3beb64 |
→ | Green-500 #3beb64 |
| Token | Light Mode | Dark Mode | |
|---|---|---|---|
| text.primary | Charcoal #0f2c24 |
→ | Off-white #f0eee9 |
| text.secondary | Secondary #768782 |
→ | Light-sec #a8b5b1 |
| text.tertiary | Tertiary #9faba7 |
→ | Dark-tert #7a8b86 |
| text.inverse | Off-white #f0eee9 |
→ | Charcoal #0f2c24 |
| text.accent | Green-500 #3beb64 |
→ | Green-400 #72f18b |
| Token | Light Mode | Dark Mode | |
|---|---|---|---|
| border.default | #cfd5d3 |
→ | #1e5c4a |
| border.active | Green-500 |
→ | Green-400 |
| interactive.default | Green-500 |
→ | Green-400 |
| interactive.hover | Green-600 |
→ | Green-500 |
La escalera de profundidad se mantiene en dark mode. Cada capa superior es ligeramente más clara que la anterior, creando la sensación de elevación sin depender de sombras.
En dark mode, cada capa que se eleva es más clara: charcoal → darkgreen → deep-green → mid-green. Esto replica la convención natural de que la luz proviene de arriba.
Los colores de texto se invierten para mantener legibilidad sobre fondos oscuros. El acento verde se aclara ligeramente (green-400 en lugar de green-500) para mejor contraste.
Cada componente adapta sus tokens semánticos automáticamente. Aquí se muestran las diferencias visuales clave.
En dark mode, el botón primario usa green-400 (#72f18b) para mayor contraste sobre fondos oscuros. El borde del botón secundario se reduce a 30% de opacidad del color inverso.
Diagnóstico, diseño y activación de cultura organizacional.
Diagnóstico, diseño y activación de cultura organizacional.
Las cards en dark mode usan surface.secondary (darkgreen) como fondo, con border sutil en #1e5c4a. La sombra aumenta opacidad para ser perceptible sobre fondos oscuros.
Los inputs en dark mode usan fondo semi-transparente (rgba blanco al 5%) con border en #1e5c4a. En focus, el border cambia a green-400.
Los colores de feedback se aclaran ligeramente en dark mode para mantener contraste. Los fondos aumentan su opacidad de 8% a 12%.
En dark mode, las sombras convencionales son prácticamente invisibles. La elevación se comunica mediante dos estrategias complementarias.
Luminosidad de superficie: Cada capa superior es más clara. La card (surface.tertiary = #1a4d3a) se distingue del fondo (surface.primary = #0f2c24) por su mayor luminosidad.
Sombras reforzadas: Las sombras en dark mode usan rgba(0,0,0) con mayor opacidad para ser perceptibles contra fondos oscuros.
| Nivel | Light Mode | Dark Mode |
|---|---|---|
| elevation.sm | 0 2px 4px rgba(15,44,36, 0.06) | 0 2px 4px rgba(0,0,0, 0.20) |
| elevation.md | 0 4px 12px rgba(15,44,36, 0.08) | 0 4px 12px rgba(0,0,0, 0.28) |
| elevation.lg | 0 8px 24px rgba(15,44,36, 0.12) | 0 8px 24px rgba(0,0,0, 0.36) |
En dark mode, nunca dependas solo de sombras para comunicar profundidad. Siempre combina sombra + diferencia de luminosidad de superficie. Si el ojo no distingue la sombra, la diferencia de brillo entre capas mantiene la jerarquía.
Todas las combinaciones de texto sobre superficie deben cumplir WCAG 2.1 AA. Los ratios de contraste se verifican para cada par de tokens.
En dark mode se usa green-400 (#72f18b) como acento textual en lugar de green-500 (#3beb64). Esto eleva el ratio de 6.7:1 a 8.9:1 contra charcoal, garantizando AAA.
El dark mode se activa con la clase dark-mode en el body o el atributo data-theme="dark". Todas las custom properties se sobreescriben en un solo bloque.
/* ── Dark mode semantic overrides ── */ body.dark-mode, [data-theme="dark"] { /* Surfaces */ --surface-primary: #0f2c24; --surface-secondary: #14382e; --surface-tertiary: #1a4d3a; --surface-inverse: #f7f5f3; --surface-accent: #3beb64; /* Text */ --text-primary: #f0eee9; --text-secondary: #a8b5b1; --text-tertiary: #7a8b86; --text-inverse: #0f2c24; --text-accent: #72f18b; /* Borders */ --border-default: #1e5c4a; --border-active: #72f18b; /* Interactive */ --interactive-default: #72f18b; --interactive-hover: #3beb64; /* Elevation (darker, higher opacity) */ --elevation-sm: 0 2px 4px rgba(0,0,0,0.20); --elevation-md: 0 4px 12px rgba(0,0,0,0.28); --elevation-lg: 0 8px 24px rgba(0,0,0,0.36); }
// Toggle dark mode con localStorage const toggle = document.getElementById('theme-toggle'); const prefersDark = window.matchMedia('(prefers-color-scheme: dark)'); // Inicializar desde preferencia guardada o del sistema function initTheme() { const saved = localStorage.getItem('flow-theme'); if (saved) { document.body.classList.toggle('dark-mode', saved === 'dark'); } else { document.body.classList.toggle('dark-mode', prefersDark.matches); } } // Toggle y guardar preferencia toggle.addEventListener('click', () => { document.body.classList.toggle('dark-mode'); const isDark = document.body.classList.contains('dark-mode'); localStorage.setItem('flow-theme', isDark ? 'dark' : 'light'); }); // Escuchar cambio de preferencia del sistema prefersDark.addEventListener('change', (e) => { if (!localStorage.getItem('flow-theme')) { document.body.classList.toggle('dark-mode', e.matches); } }); initTheme();
<!-- Toggle de tema en header/nav --> <button id="theme-toggle" class="theme-toggle" aria-label="Cambiar modo de color" role="switch" aria-checked="false" > <span class="icon-sun">☀️</span> <span class="icon-moon">🌙</span> </button>
Activa el toggle para ver la transición de tokens en tiempo real.
Reglas esenciales para implementar dark mode consistente en cualquier producto o pieza digital de Flow.
Usar siempre tokens semánticos (--surface-primary) en lugar de colores literales (#f7f5f3) para que el dark mode funcione automáticamente.
Hardcodear colores en componentes. Si usas color: #0f2c24 directamente, no se invertirá al activar dark mode.
Testear feedback states (success, error, warning) en ambos modos. Los colores de feedback se aclaran en dark mode para mantener contraste.
Usar negro puro (#000000) como fondo. Flow dark mode siempre usa charcoal (#0f2c24) y darkgreen (#14382e) para mantener calidez de marca.
Respetar la preferencia del sistema operativo (prefers-color-scheme) como default, con opción de override manual.
Depender solo de sombras para comunicar profundidad. En dark mode, combinar siempre sombra + diferencia de luminosidad entre capas.
Usar green-400 (#72f18b) como acento textual en dark mode. Es más claro que green-500 y garantiza contraste AAA sobre charcoal.
Añadir transiciones largas (>500ms) al cambio de tema. Flow usa 300ms (duration.default) para que el cambio se sienta inmediato pero suave.
Las fotografías no se invierten ni se alteran en dark mode. Sin embargo, se recomienda reducir ligeramente su brillo (filter: brightness(0.9)) para evitar que deslumbren sobre fondos oscuros. Los íconos SVG que usan currentColor se adaptan automáticamente.
El gradiente hero (charcoal → darkgreen → deep-green) funciona bien en ambos modos porque ya es inherentemente oscuro. Para gradientes claros usados en light mode, crear versiones dark alternativas que mantengan la dirección pero usen la paleta oscura.