State Management nativo con CSS (Baseline 2026)
El 40% del JavaScript que enviamos al cliente para gestionar la interfaz es deuda técnica evitable[^1]. Con :has() consolidado como estándar estable[^2] y las Custom Properties madurando en todos los motores, podemos mover la lógica de estado efímero directamente a la capa de estilos, reduciendo drásticamente el tiempo de hidratación y el TBT (Total Blocking Time) en dispositivos móviles — algo crítico en contextos como Itapúa, donde la latencia y la potencia de los dispositivos varían enormemente.
El par de herramientas que reemplazan useState
Gracias a Baseline 2026, hoy contamos con dos herramientas que juntas reemplazan casi cualquier useState puramente cosmético:
:has(): El "selector padre" que nos permite reaccionar a cambios en los hijos (como un checkbox o un input enfocado).- Custom Properties como estado: Variables CSS que cambian de valor según el estado del DOM y se heredan en cascada, eliminando la necesidad de JS para propagar cambios visuales.
Ejemplo: De React a CSS Puro
Mirá la diferencia de arquitectura al implementar un toggle de estado:
const [isOpen, setIsOpen] = useState(false);
return (
<div className={isOpen ? "active" : ""}>
<button onClick={() => setIsOpen(!isOpen)}>Toggle</button>
<div className="panel">Contenido</div>
</div>
);.toggle-panel:has(.toggle-input:checked) {
--state: open;
}
.toggle-panel .panel {
opacity: 0;
max-height: 0;
transition:
opacity 0.3s,
max-height 0.3s;
}
.toggle-panel:has(.toggle-input:checked) .panel {
opacity: 1;
max-height: 500px;
}El HTML necesario es mínimo y semántico:
<div class="toggle-panel">
<input type="checkbox" class="toggle-input" id="toggle-1" hidden />
<label for="toggle-1">Abrir panel</label>
<div class="panel">Contenido oculto que aparece sin JS.</div>
</div>
Por qué el motor de renderizado lo agradece
Al mover el estado al CSS, el navegador optimiza las fases de Style y Layout de forma nativa. A diferencia de JS, donde el hilo principal se bloquea durante la ejecución del script y la posterior reconciliación del Virtual DOM, CSS permite que estas transiciones ocurran de forma declarativa. El navegador planifica las actualizaciones de pantalla con mayor eficiencia — 60fps constantes sin competir por el main thread.
Esta filosofía de Zero-JS para UI cosmética es la misma que aplico en mi portfolio con Lighthouse 100 y que complementa el uso de CSS Masonry nativo.
¿Cuándo soltar el JS?
No todo debe ir al CSS. La clave está en distinguir entre Estado de Negocio y Estado de UI.
CSS vs JS para State Management
CSS (Estado de UI)
- Visibilidad de Modales/Tabs
- Validaciones visuales de formularios
- Temas (Dark/Light/High Contrast)
- Animaciones ligadas a interacción
JS (Estado de Negocio)
- Persistencia en Base de Datos
- Lógica de carrito de compras
- Autenticación y Sesiones
- Cálculos matemáticos complejos
Si solo cambia cómo se ve, va al CSS. Si cambia datos, queda en JS.
Delegar la cosmética al CSS no es solo una cuestión de "limpieza", es una decisión de Soberanía Tecnológica: tu sitio web se vuelve más resiliente, más barato de mantener y, sobre todo, mucho más rápido para el usuario final. Si querés profundizar en cómo simplificar tu arquitectura CSS, mirá La Navaja de Ockham aplicada a CSS.
[^1]: Estimación basada en análisis de bundles de producción con frameworks modernos. Ver HTTP Archive — JavaScript y The Cost of JavaScript.
[^2]: :has() alcanzó estatus Baseline en 2024 con soporte en Chromium 105+, Safari 15.4+ y Firefox 121+. Ver MDN — :has().