0 Comentarios
Si estás empezando en frontend y te suena eso de “en mi Chrome funciona” (pero en el navegador de tu cliente no), Modernizr es de esas herramientas que te hacen respirar un poco más tranquilo. Es simple en concepto, pero potente en el día a día: detecta si una funcionalidad existe de verdad en el navegador del usuario y te ayuda a decidir qué experiencia ofrecer.
Resumen rápido:
Modernizr hace feature detection: devuelve true/false según soporte real de una característica.
Evita el “user-agent sniffing” (adivinar por navegador/versión), que suele acabar mal.
Puedes usarlo con CSS (clases en HTML) y con JavaScript (objeto Modernizr).
Lo ideal es generar un build a medida para no cargar tests que no necesitas.
Modernizr es “un pequeño código JavaScript que detecta automáticamente la disponibilidad de tecnologías web” en el navegador del usuario. Dicho como lo usaríamos nosotros en un proyecto real: te dice qué puede hacer el navegador, sin suposiciones.
¿Y para qué sirve esto hoy, si ya tenemos navegadores modernos, frameworks y mil librerías?
Para compatibilidad web sin dramas: no todos los usuarios actualizan, y no todos navegan desde el mismo tipo de dispositivo.
Para diseñar con progressive enhancement: empezar con una base sólida (que funciona en casi cualquier lado) y añadir mejoras si el navegador lo soporta.
Para tomar decisiones de UX sin romper nada: si hay soporte, genial; si no, damos un fallback digno (y listo).
Y sí: incluso si trabajas con React/Vue/Next, esto sigue teniendo sentido cuando te toca una funcionalidad “puntiaguda” (p. ej. una API concreta, un detalle CSS, o un evento del dispositivo).
El “user-agent sniffing” es básicamente: “si el navegador es X, asumo que soporta Y”. El problema es que eso es como decidir si alguien sabe cocinar solo por su edad. A veces aciertas, pero no es fiable.
Modernizr lo explica con un ejemplo muy claro: si basas tus decisiones en UA sniffing, cuando un navegador se actualiza o alguien cambia el user-agent, tu lógica se vuelve absurda y puedes acabar bloqueando a usuarios que sí soportan la funcionalidad (o dejando entrar a quienes no).
En cambio, con feature detection haces esto:
if (Modernizr.awesomeNewFeature) { // muestro la experiencia mejorada } else { // doy una alternativa sencilla que funciona }
La diferencia parece pequeña… pero en producción es enorme. Estás comprobando capacidad real, no identidad del navegador.
La idea central es muy sencilla: Modernizr ejecuta tests (comprobaciones) y guarda resultados en dos sitios:
Clases en el HTML para que CSS pueda reaccionar (por ejemplo .cssgradients o .no-cssgradients).
El objeto global Modernizr con propiedades booleanas para usarlas en JavaScript.
Y aquí viene lo importante: no es “para parchear”. Es para diseñar una experiencia escalable:
Base funcional (mínima, estable, accesible).
Mejoras progresivas cuando hay soporte.
Fallbacks coherentes cuando no lo hay.
Cuando lo haces así, de repente la compatiblidad deja de ser una guerra y se convierte en una estrategia.
Aquí Modernizr es bastante honesto: no hay un “modernizr.js universal” que lo traiga todo por defecto. La filosofía actual es: elige los tests que necesitas y genera un build pequeño. Eso mejora rendimiento y evita cargar detecciones inútiles.
La vía rápida (y muy típica al empezar) es usar el generador de builds: seleccionas features y descargas tu archivo. La clave es que seas selectivo: si solo necesitas detectar flexbox y webp, no cargues 80 tests “por si acaso”.
Consejo de oficina: nosotros solemos empezar por una lista corta y luego, si aparece un caso real, añadimos un test más. No al revés.
Si quieres hacerlo bien desde el minuto 1 (o ya estás en un proyecto con CI), usa el CLI con npm:
npm install -g modernizr
modernizr -c modernizr-config.json
La gracia está en ese modernizr-config.json: lo puedes sacar desde el generador (“Command Line Config”) y luego versionarlo con el proyecto. Así el build es reproducible.
Si no quieres ejecutar el comando a mano cada vez, puedes integrarlo en tu build. La documentación menciona flujos con tareas (por ejemplo Grunt vía grunt-modernizr) para automatizarlo y generar tu fichero final cuando compilas.
Hoy en día, lo típico es meterlo en npm scripts, o en tu pipeline (GitHub Actions, GitLab CI…). La idea es la misma: que el build salga siempre igual y no dependa de “quién lo genera”.
Tres opciones que conviene conocer (porque te evitan sustos):
classPrefix: añade un prefijo a todas las clases que inserta Modernizr (útil para evitar colisiones).
enableJSClass: controla si cambia no-js por js en el
enableClasses: permite desactivar por completo el añadido de clases.
Si estás empezando, esta parte es la más “amable”: Modernizr añade clases al HTML indicando soporte o no soporte. Por ejemplo, con gradientes tendrás algo tipo:
html class="cssgradients"
html class="no-cssgradients"
Y entonces puedes hacer CSS condicional limpio.
La convención es literalmente esa: feature si hay soporte, no-feature si no lo hay. Eso hace facilísimo escribir mejoras progresivas:
.no-cssgradients .header { background: url("images/glossybutton.png"); } .cssgradients .header { background-image: linear-gradient(cornflowerblue, rebeccapurple); }
Es un ejemplo clásico porque se entiende rápido: si no hay gradientes, pones una imagen.
Esto pasa más de lo que parece: Modernizr tiene un detect que añade .hidden… y tú ya usas .hidden en tu CSS para ocultar cosas. Resultado: caos.
Solución: classPrefix. Por ejemplo, supports- o mzr- o lo que tenga sentido:
{ "classPrefix": "supports-", "feature-detects": ["dom/hidden"] }
Así en vez de .hidden tendrás .supports-hidden.
Tres patrones que usamos mucho:
Mejora progresiva en componentes: el componente funciona sin la feature, y solo “se vuelve premium” si hay soporte.
Fallback visual: si falta una capacidad, mantén el diseño digno (sin romper layout).
Mantenimiento con intención: escribe CSS que deje claro “qué se gana” con la feature, no solo “qué se arregla”.
Si un día vuelves a ese código, lo agradecerás. Tú y nosotros también, si lo mantenemos.
En JavaScript, Modernizr expone un objeto Modernizr con propiedades por test. Y esas propiedades son, a efectos prácticos, booleanos.
Ejemplo típico:
if (Modernizr.webp) { // carga imágenes webp } else { // carga jpg/png }
La clave es que tu código sea legible: “si hay soporte, hago esto; si no, hago esto otro”. No lo conviertas en un árbol de decisiones infinito.
Hay un helper muy útil: Modernizr.on(feature, cb), que te permite reaccionar cuando el test está listo:
Modernizr.on('flash', function (result) { if (result) { // hay flash } else { // no hay flash } });
Sí, Flash es un ejemplo viejuno, pero el mecanismo es válido.
Aquí es donde Modernizr deja de ser “solo clases” y se convierte en una caja de herramientas bastante seria.
Si tu caso no está cubierto, puedes añadir tus propias detecciones con Modernizr.addTest():
Modernizr.addTest('hasjquery', 'jQuery' in window);
O con función si lo necesitas:
Modernizr.addTest('itstuesday', function () {
var d = new Date();
return d.getDay() === 2;
});
Un detalle que a mucha gente se le escapa: los nombres se guardan en minúsculas dentro del objeto (por ejemplo Modernizr.itstuesday).
Dos joyitas:
Modernizr.mq('(min-width: 900px)') para saber si el viewport cumple una media query.
Modernizr.testAllProps('display', 'grid') (o similar) para probar propiedades/valores CSS con soporte de prefijos cuando haga falta.
Ejemplo rápido con mq:
if (Modernizr.mq('(min-width: 900px)')) { // estamos en viewport grande }
Para lidiar con prefijos (sí, aún aparecen en proyectos legacy), tienes varias utilidades:
Modernizr.prefixed('boxSizing') devuelve la propiedad correcta soportada o false.
Modernizr.prefixedCSS('transition') devuelve la propiedad en kebab-case (p. ej. -moz-transition en Firefox antiguo).
Modernizr.prefixedCSSValue('display', 'flex') o para valores complejos tipo gradientes.
Esto es útil cuando estás haciendo código que necesita la “forma exacta” que entiende el navegador, sobre todo en integraciones raras o estilos generados.
Cuando no basta con mirar propiedades, existe Modernizr.testStyles(rule, callback) que inyecta CSS y nodos para comprobar cosas más “finas”:
Modernizr.testStyles('#modernizr { width: 9px; }', function (elem) { Modernizr.addTest('widthworks', elem.style.width === '9px'); });
Esto te da un nivel de control que, sinceramente, salva proyectos cuando heredas compatibilidad antigua o escenarios edge.
Modernizr puede detectar un montón de cosas, pero no necesitas memorizar una lista eterna. Mejor pensar por categorías.
Layout: flexbox, grid (según el test que incluyas), box-sizing…
Efectos: gradients, filters, transforms, transitions…
Animaciones: soporte de @keyframes incluso con prefijos (vía Modernizr.atRule('@keyframes')).
Form validation / input types combinables (la doc muestra ejemplos de combinación de tests).
Imágenes modernas: webp (y variantes como alpha/animation en algunos tests).
Workers, APIs específicas, etc. (dependiendo de los detects que selecciones).
Eventos: Modernizr.hasEvent('blur'), o eventos más específicos sobre window si lo pasas como segundo argumento.
Capacidades: touch/pointer/fullscreen suelen estar disponibles como detects seleccionables, según build.
Aquí es donde se gana o se pierde el partido.
Error típico: meter Modernizr para “arreglar” un bug de un navegador concreto y llenarte de condiciones que nadie entiende.
Lo sano es pensar:
“¿Cuál es mi experiencia base?” y luego “¿Qué mejora aplico si existe soporte?”. Si lo haces al revés, terminas con una web frágil.
Una mini regla que usamos nosotros:
Si la feature afecta a funcionalidad (p. ej. una API JS necesaria) → mira polyfill o alternativa.
Si afecta a presentación (p. ej. un efecto CSS) → fallback visual y listo.
Si afecta a UX crítica (p. ej. interacción táctil) → experiencia alternativa (no solo “se ve peor”, sino “se usa bien”).
Y no pasa nada por decir: “en navegadores antiguos esta parte es más simple”. Siempre que sea utilizable, vale.
Dos cosas que marcan diferencia:
Build mínimo: tests que realmente uses (no coleccionismo).
Evitar tests custom súper complejos en runtime si no aportan valor. A veces, @supports o una comprobación nativa te basta.
Modernizr no es obligatorio. Es una herramienta. Y como toda herramienta, a veces hay alternativas más simples.
Para CSS, @supports es fantástico:
@supports (display: grid) { .layout { display: grid; } }
Si tu problema es 90% CSS, este enfoque suele ser más directo, sin cargar JavaScript extra.
Para JavaScript, muchas veces puedes detectar de forma nativa:
if ('serviceWorker' in navigator) { // ok }
Y cargar polyfills bajo demanda (si tu bundler lo facilita). Esto suele ser muy limpio en proyectos modernos.
Nosotros lo mantenemos cuando:
Hay un cliente con base de usuarios en entornos variados (corporativo, kioskos, dispositivos antiguos).
Hay necesidades de detección transversal (CSS + JS + eventos) y queremos una “fuente de verdad”.
Queremos clases en HTML para que diseño/maquetación resuelvan cosas sin tocar JS.
Si tu público es 99% evergreen y tu problema es una sola feature, quizá no compensa.
Aquí va lo práctico, lo que te salva cuando el proyecto crece.
Detectar: ¿qué capability falta exactamente? (no “Safari es malo”, sino “no soporta X”).
Actuar: ¿fallback, polyfill o alternativa? Decide una.
Medir: ¿cuánta gente cae en el fallback? (analytics, logs, soporte).
Ajustar: si el fallback se usa mucho, inviértele cariño; si no, mantenlo simple.
¿Tu build incluye solo los tests necesarios?
¿Has evitado colisiones con classPrefix si hacía falta?
¿Estás usando no-js/js de forma consciente (y no para ocultar cosas importantes)?
¿Tus fallbacks mantienen accesibilidad y rendimiento razonable?
¿Has probado en 2–3 navegadores reales (no solo emulador)?
Y ya que estamos, si te estás planteando una web que funcione bien en todos lados (y que además convierta, que es el punto), nosotros en Overant solemos ayudar justo con eso: arquitectura, rendimiento, compatibilidad, SEO… Si quieres que lo revisemos contigo, aquí tienes nuestra página de diseño web en Alicante.
No. Modernizr detecta soporte; un polyfill implementa una funcionalidad que falta. Muchas veces se usan juntos (detectas y, si falta, cargas polyfill).
No siempre. Pero puede venirte genial si necesitas una capa consistente de detección (CSS + JS) o compatibilidad más “de batalla”.
Las clases son perfectas para estilos y mejoras visuales. El objeto Modernizr es mejor para decisiones de lógica en runtime.
Genera un build a medida con solo los tests que usas y revisa periódicamente si sobran.
Crea uno con Modernizr.addTest() o usa Modernizr.testStyles() si necesitas una comprobación más compleja.
Si te quedas con una sola idea, que sea esta: no diseñamos para navegadores, diseñamos para capacidades. Y cuando trabajas así, tu web se vuelve más robusta, más fácil de mantener y, sinceramente, más agradable de evolucionar. Aunque algún día te toque pelear con un navegador rarito… bueno, al menos tendrás un plan.

¿Qué te ha parecido este artículo?