Modernizr: guía completa para detectar funcionalidades del navegador y mejorar la compatibilidad web

Marco Risco
De la mente de: Marco Risco 04-Mar-2026 Programación
Modernizr: guía completa para detectar funcionalidades del navegador y mejorar la compatibilidad web 0 Comentarios
Basado en 0 votos

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.

¿Qué es y para qué sirve en proyectos frontend actuales?

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).

Detección de características vs. “user-agent sniffing”: por qué sigue siendo una buena idea

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.

Cómo funciona: tests, resultados booleanos y decisiones de experiencia (progressive enhancement)

La idea central es muy sencilla: Modernizr ejecuta tests (comprobaciones) y guarda resultados en dos sitios:

  1. Clases en el HTML para que CSS pueda reaccionar (por ejemplo .cssgradients o .no-cssgradients).

  2. 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.

Cómo instalarlo y generar un build a medida (sin cargar código innecesario)

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.

Descarga desde el generador: elegir solo los tests que necesitas

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.

Build por línea de comandos con npm: flujos de trabajo reproducibles

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.

Integración en automatizaciones (por ejemplo con tareas de build)

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”.

Opciones de configuración clave: prefijos de clase, clases activadas y control de “no-js”

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 

    . Por defecto lo hace.
  • enableClasses: permite desactivar por completo el añadido de clases.

Uso con CSS: clases en el elemento raíz para estilos condicionales

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.

Convención de clases: “feature” y “no-feature” (y cómo aprovecharlas bien)

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.

Evitar colisiones con tus estilos: estrategia de prefijado

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.

Patrones prácticos: degradación elegante, mejoras progresivas y mantenimiento

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.

Uso con JavaScript: el objeto global y su lectura segura en runtime

En JavaScript, Modernizr expone un objeto Modernizr con propiedades por test. Y esas propiedades son, a efectos prácticos, booleanos.

Acceder a resultados de tests sin complicarte: condicionales claros

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.

Helpers y utilidades: escuchar resultados, crear detecciones propias y más

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.

API imprescindible que conviene conocer

Aquí es donde Modernizr deja de ser “solo clases” y se convierte en una caja de herramientas bastante seria.

Añadir tests personalizados para necesidades reales de tu producto

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).

Comprobar reglas CSS y media queries desde código

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
}

Gestionar prefijos: propiedades, valores y compatibilidad histórica

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.

Tests avanzados con inyección de estilos para casos difíciles

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.

Qué puede detectar: categorías de funcionalidades habituales en la web moderna

Modernizr puede detectar un montón de cosas, pero no necesitas memorizar una lista eterna. Mejor pensar por categorías.

CSS (layout, efectos, tipografía, animaciones)

  • 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')).

HTML5 y APIs del navegador (almacenamiento, multimedia, sensores, etc.)

  • 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 y capacidades del dispositivo (touch, pointer, fullscreen, etc.)

  • 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.

Buenas prácticas y errores típicos al usar detección de funcionalidades

Aquí es donde se gana o se pierde el partido.

No conviertas los tests en “parches”: prioriza la mejora progresiva

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.

Cómo decidir si necesitas polyfill, fallback visual o experiencia alternativa

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.

Rendimiento: minimizar tests, tamaño del archivo y coste en runtime

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.

Alternativas y cuándo elegir otra estrategia (sin perder compatibilidad)

Modernizr no es obligatorio. Es una herramienta. Y como toda herramienta, a veces hay alternativas más simples.

CSS @supports y enfoque “CSS first”

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.

Feature detection nativa + polyfills bajo demanda

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.

Compatibilidad en proyectos modernos: cuándo compensa mantenerlo

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.

Checklist final para implementarlo en producción sin sorpresas

Aquí va lo práctico, lo que te salva cuando el proyecto crece.

Plantilla de decisión rápida: detectar → actuar → medir → ajustar

  1. Detectar: ¿qué capability falta exactamente? (no “Safari es malo”, sino “no soporta X”).

  2. Actuar: ¿fallback, polyfill o alternativa? Decide una.

  3. Medir: ¿cuánta gente cae en el fallback? (analytics, logs, soporte).

  4. Ajustar: si el fallback se usa mucho, inviértele cariño; si no, mantenlo simple.

Auditoría de compatibilidad: qué revisar antes del despliegue

  • ¿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.

FAQ sobre Modernizr (preguntas típicas)

¿Modernizr es lo mismo que un polyfill?

No. Modernizr detecta soporte; un polyfill implementa una funcionalidad que falta. Muchas veces se usan juntos (detectas y, si falta, cargas polyfill).

¿Tengo que usar Modernizr en proyectos modernos con frameworks?

No siempre. Pero puede venirte genial si necesitas una capa consistente de detección (CSS + JS) o compatibilidad más “de batalla”.

¿Qué diferencia hay entre usar clases en HTML y usar Modernizr.algo en JS?

Las clases son perfectas para estilos y mejoras visuales. El objeto Modernizr es mejor para decisiones de lógica en runtime.

¿Cómo evito que Modernizr engorde mi web?

Genera un build a medida con solo los tests que usas y revisa periódicamente si sobran.

¿Qué hago si un test no existe para lo que necesito?

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?
Deja tu comentario
Acepto facilitar mis datos con la finalidad de dejar mis comentarios en el blog
Acepto recibir información comercial
¿Necesitas hablar? ¡Contacta con nosotros!