Tema representativo de entrevista

Entrevista de Frontend: ¿Cómo usar content-visibility sin provocar Layout Shift?

FrontendDifícil
Equipo editorial de Offer.ccPublicado Actualizado

Pregunta

Un feed de contenido tiene cientos de tarjetas, y tanto el primer renderizado como el desplazamiento (scrolling) se sienten lentos. Explica cómo usarías content-visibility: auto para diferir el renderizado fuera de pantalla, cómo contain-intrinsic-size evita saltos en la barra de desplazamiento y cómo verificarías que INP, la estabilidad del layout y la accesibilidad no sufrieran regresiones.

Pregunta y escenarios adecuados

Eres responsable de una lista larga, un feed de artículos o un panel de administración. El DOM contiene cientos de secciones de contenido; inicialmente solo son visibles unas pocas tarjetas, pero el navegador sigue realizando tareas de estilo, layout y pintura para subárboles fuera de pantalla. La entrevista te pide reducir el trabajo de renderizado en el hilo principal mediante contención CSS, preservando al mismo tiempo la búsqueda en página, el foco y el acceso mediante lectores de pantalla.

Asume que la página se puede dividir en tarjetas o secciones independientes, que sus alturas varían pero no son infinitas, y que los navegadores objetivo admiten content-visibility: auto. Los navegadores no compatibles deben seguir renderizando la página correctamente.

Qué evalúa el entrevistador

  • Si separas los cuellos de botella de red, scripts y renderizado antes de optimizar.
  • Si puedes explicar la contención de layout, estilo y pintura implícita en auto, además de la contención de tamaño fuera de pantalla.
  • Si anticipas los errores en el tamaño del marcador de posición que generan saltos en la barra de desplazamiento y riesgo de CLS.
  • Si validas el rendimiento junto con el foco, la búsqueda en página, el comportamiento del lector de pantalla y las lecturas del DOM.

Una respuesta débil da una sola declaración CSS. Una respuesta sólida establece los límites, la estrategia de dimensionamiento, el costo de las lecturas de layout forzadas y el plan de respaldo (fallback).

Preguntas para aclarar antes de responder

  1. ¿La lentitud ocurre en el primer renderizado, al hacer scroll, después de filtrar o tras un clic? La fase determina si mides el trabajo de renderizado, de scripts o de red.
  2. ¿Son estables las alturas de las tarjetas? Una mayor varianza aumenta el valor de las mediciones reales o de que contain-intrinsic-size: auto recuerde los tamaños renderizados.
  3. ¿El contenido fuera de pantalla debe seguir siendo indexable, recibir foco y estar expuesto a la tecnología de asistencia? Si es así, hidden no es un reemplazo directo de display: none.
  4. ¿El código lee offsetHeight o getBoundingClientRect() durante cada desplazamiento o actualización de estado? Dichas lecturas pueden volver a meter el trabajo de renderizado omitido en la ruta crítica.

Marco de respuesta de 30 segundos

“Primero usaría el panel Performance para confirmar que renderizar subárboles fuera de pantalla es el cuello de botella. Dividiría el feed en secciones independientes, aplicaría content-visibility: auto a las secciones no críticas y proporcionaría un contain-intrinsic-size realista para que la contención de tamaño no haga que parezcan vacías. Luego probaría el foco, la búsqueda en página y el comportamiento de las tecnologías de asistencia, y auditaría las lecturas de DOM que fuerzan el layout. Finalmente, compararía el primer renderizado, el scroll, INP, CLS y el fallback de propiedad no admitida contra un control.”

Respuesta detallada paso a paso

1. Establecer límites de renderizado

Divide la página larga en secciones o tarjetas independientes. Un cambio de layout dentro de un límite no debe afectar a regiones no relacionadas; de lo contrario, la contención puede ocultar una dependencia real y producir un layout incorrecto.

css
.story {
  content-visibility: auto;
  contain-intrinsic-size: auto 720px;
}

Cuando una sección está lejos del viewport, auto permite al navegador omitir partes del trabajo de estilo, layout y pintura de los descendientes; a medida que se acerca al viewport, el navegador la renderiza bajo demanda. El DOM sigue estando presente.

2. Proporcionar un marcador de posición de tamaño

Una sección fuera de pantalla se dimensiona temporalmente sin examinar su contenido. Sin una altura explícita o un tamaño intrínseco, puede verse como una caja vacía muy baja, alterando la longitud de la barra de desplazamiento y la posición de scroll del usuario.

contain-intrinsic-size: auto 720px suministra una estimación inicial. Después del renderizado, el navegador puede recordar el tamaño real. Deriva la estimación a partir de muestras de producción en lugar de elegir un número decorativo. Si hay gran varianza, agrupa los tipos de contenido o proporciona una sugerencia de tamaño junto con los datos.

3. Distinguir entre auto, hidden y display none

  • auto: omite el renderizado fuera de pantalla y lo reanuda cerca del viewport; el contenido permanece en el DOM y en el árbol de accesibilidad.
  • hidden: mantiene el estado de renderizado omitiendo siempre el renderizado; es adecuado para una vista inactiva, no para cualquier necesidad de ocultar la accesibilidad.
  • display: none: elimina el estado de layout y renderizado, por lo que mostrarlo requiere reconstruir dicho estado.

Si el contenido debe ser invisible para la tecnología de asistencia, usa aria-hidden semánticamente correcto o elimínalo del DOM, y asegúrate de que el foco no pueda entrar en la región oculta.

4. Auditar lecturas que anulan la optimización

Las lecturas frecuentes de layout en una sección con content-visibility pueden obligar al navegador a calcular anticipadamente el subárbol omitido. Mide solo cuando sea necesario, agrupa lecturas antes de escrituras y evita alternar lecturas y escrituras que desencadenen layout sincrónico.

js
requestAnimationFrame(() => {
  const height = card.getBoundingClientRect().height;
  card.style.setProperty('--measured-height', `${height}px`);
});

Esto ilustra el timing, no una receta universal. Usa una grabación de Performance para demostrar que una lectura genera una tarea larga (long task) antes de eliminarla.

5. Verificar con métricas orientadas al usuario

Como mínimo, prueba:

  • Primer renderizado y tiempo total de renderizado, para demostrar que el trabajo fuera de pantalla se redujo.
  • INP o long tasks en el momento del clic, para ver si el hilo principal ganó margen disponible.
  • CLS y posición de scroll, para detectar saltos por el tamaño de los marcadores de posición.
  • Tabulación con teclado, búsqueda en página y un lector de pantalla, para verificar el comportamiento de accesibilidad de auto.
  • Un navegador no compatible, para asegurar que el comportamiento por defecto de visible siga renderizando todo el contenido.

El ejemplo de web.dev redujo el renderizado de una página particular de 232ms a 30ms. Trata eso como el resultado de un experimento, no como una promesa para cada sitio; tu conclusión debe provenir de tus propias mediciones de control y tratamiento.

Respuesta de muestra de alta calidad

Definiría el síntoma como un trabajo de renderizado excesivo para contenido fuera de pantalla, y luego confirmaría en el panel Performance que el estilo, el layout o la pintura dominan el hilo principal. Tras la confirmación, dividiría la página en secciones independientes, establecería content-visibility: auto y estimaría contain-intrinsic-size a partir de contenido representativo. El navegador podrá entonces omitir el renderizado de los descendientes fuera de pantalla manteniendo intactos el DOM y el acceso de las tecnologías de asistencia.

No me detendría en el tiempo del primer renderizado. Verificaría el movimiento de la barra de desplazamiento, el foco y la búsqueda en página, y buscaría lecturas de getBoundingClientRect u offsetHeight que fuercen el layout durante el scroll o las actualizaciones de estado. Compararía el primer renderizado, las long tasks de scroll, INP, CLS y datos de usuarios reales. Si las alturas de las tarjetas varían drásticamente o los límites tienen dependencias de layout entre secciones, revisaría la división y la estrategia de dimensionamiento. Los navegadores no compatibles obtienen el comportamiento predeterminado de visible, por lo que la optimización se mantiene como una mejora progresiva.

Errores comunes

  • Error → Agregar content-visibility: auto a toda la página → Por qué falla → límites poco claros ocultan dependencias de layout e imposibilitan la atribución → Solución → dividir en secciones independientes y registrar primero una línea base.
  • Error → Omitir el dimensionamiento intrínseco → Por qué falla → la contención de tamaño puede subestimar la altura de la sección, empeorando el scroll y CLS → Solución → estimar a partir de muestras y observar la posición real de scroll.
  • Error → Usar hidden como sinónimo de aria-hiddenPor qué falla → la ocultación visual y la semántica de accesibilidad difieren → Solución → elegir eliminación del DOM, aria-hidden o contenido preservado según la semántica del producto.
  • Error → Eliminar cada lectura de DOM relacionada con el rendimiento → Por qué falla → algunas mediciones son necesarias y la eliminación a ciegas rompe el comportamiento → Solución → usar una grabación para identificar lecturas que realmente fuerzan el layout.

Preguntas de seguimiento y respuestas

¿Usarías un único tamaño intrínseco si las alturas de las tarjetas varían por un factor de cinco?

No. Una sola estimación haría que algunas tarjetas fueran dramáticamente demasiado bajas o demasiado altas. Agruparía los tipos de contenido por categorías, preferiría los tamaños renderizados recordados y, si fuera necesario, devolvería sugerencias de tamaño desde el servidor. Usaría CLS y el error de scroll para decidir si la complejidad adicional de agrupar vale la pena.

¿Es suficiente content-visibility si el producto requiere que la decodificación de video fuera de pantalla se detenga por completo?

No. Controla principalmente el trabajo de renderizado y no reemplaza la gestión del ciclo de vida de los medios. Pausaría y reanudaría el video cuando la visibilidad cambie, me aseguraría de que esa lógica no lea repetidamente el layout de subárboles omitidos y mediría la política de medios por separado de la optimización CSS.

¿Cómo lo enviarías a producción para navegadores que no admiten la propiedad?

No haría que la funcionalidad dependiera de ella. El valor por defecto es visible, por lo que la página sigue completa; la mejora progresiva y el monitoreo de compatibilidad pueden mostrar si los navegadores más antiguos necesitan paginación o una lista virtual en su lugar.

¿Cómo demuestras que la ganancia provino de esta propiedad y no de un DOM más pequeño?

Mantén los mismos datos y la misma estructura de DOM y cambia únicamente la propiedad CSS en un control local o A/B. Registra el tiempo de renderizado, las long tasks, INP y CLS. Si la paginación, la carga diferida de imágenes o la programación de scripts también cambiaron, el resultado no se puede atribuir a una sola propiedad.

Fuentes públicas

Preguntas relacionadas