Tema representativo de entrevista

Entrevista frontend: ¿Cuándo pueden las consultas CSS scroll-state reemplazar la detección de desplazamiento en JavaScript?

FrontendDifícil
Equipo editorial de Offer.ccPublicado Actualizado

Pregunta

¿Cómo usarías las consultas CSS scroll-state para responder a estados de sticky, snap o desplazable en lugar de escuchar eventos de scroll en JavaScript?

Planteamiento y cuándo aplica

Planteamiento de entrevista: una página tiene un encabezado sticky, tarjetas con snap horizontal y un indicador de que se puede desplazar más contenido. ¿Cómo decidirías cuándo cambiar los estilos? Compara las consultas CSS scroll-state con los listeners de scroll en JavaScript y explica qué harías cuando el navegador carece de soporte.

El límite es el estilizado declarativo: agregar una sombra cuando un encabezado se fija (sticks), resaltar una tarjeta ajustada (snapped) o mostrar una sugerencia mientras haya más contenido desplazable. El estado de negocio, los gestos complejos, el progreso de desplazamiento preciso y los reportes al servidor siguen perteneciendo a JavaScript. MDN describe cuatro familias de consultas scroll-state: scrollable, scrolled, snapped y stuck.

Qué evalúa el entrevistador

El entrevistador quiere ver si seleccionas la API según el tipo de estado y luego manejas las restricciones de soporte y accesibilidad. Una respuesta sólida menciona que el contenedor de consulta necesita container-type y que los estilos de respuesta apuntan a descendientes; también evita tratar una consulta de estado CSS como un sistema de eventos general.

Una respuesta débil memoriza la sintaxis de @container. Una respuesta sólida explica cuándo JavaScript sigue siendo necesario, cómo usar @supports para la mejora progresiva, cómo respetar prefers-reduced-motion y cómo probar casos reales de sticky, snap, desbordamiento y navegadores no compatibles.

Preguntas aclaratorias antes de responder

Primero pregunta si el requerimiento es retroalimentación visual o lógica de negocio. Las sombras, los colores y la visibilidad de sugerencias encajan en CSS; registrar la dirección del scroll, hacer una petición de red o manejar una máquina de estados necesita JavaScript.

Luego confirma el origen del estado: ¿el elemento tiene realmente position: sticky, la lista define scroll-snap-type y el contenedor de scroll tiene desbordamiento visible? Sin esos prerrequisitos, la consulta relevante permanece en false.

Finalmente confirma la línea base de navegadores y los requerimientos de movimiento. Chrome for Developers documenta las consultas de contenedor scroll-state en Chrome 133; un diseño de producción debe comenzar con estilos que no dependan de la funcionalidad, envolver las mejoras en supports y respetar el movimiento reducido.

Estructura de respuesta de 30 segundos

Di algo como:

“Separaría el estado de estilo de los eventos de negocio. Sticky, snap y desplazabilidad son estados del navegador, por lo que establecería container-type: scroll-state en el elemento relevante y usaría @container con scroll-state para dar estilo a sus descendientes. Para analíticas, progreso exacto o acciones de negocio entre componentes, mantendría JavaScript. Haría que el CSS por defecto sea utilizable, añadiría la funcionalidad tras @supports, deshabilitaría el movimiento no esencial para usuarios con reduced-motion y probaría desbordamiento, snap, entrada por teclado y navegadores no compatibles”.

Respuesta detallada paso a paso

Establecer un contenedor de consulta scroll-state

Declara el elemento cuyo estado se consultará como un contenedor scroll-state. Asígnale un nombre cuando múltiples contenedores puedan coincidir:

css
.sticky-heading {
  position: sticky;
  top: 0;
  container-type: scroll-state;
  container-name: heading;
}

La regla de respuesta debe apuntar a un descendiente, no al mismo elemento que posee container-type. La guía de Chrome muestra esta relación de capa intermedia explícitamente.

Elegir el descriptor según el estado

stuck consulta si un elemento sticky está fijado a un borde; snapped consulta si un elemento está a punto de ajustarse a un contenedor snap; scrollable consulta si queda desbordamiento en una dirección; scrolled consulta sobre la dirección de desplazamiento más reciente. Responden a preguntas distintas y no son intercambiables.

Por ejemplo, agrega una sombra a la navegación mientras está fija (stuck):

css
@container heading scroll-state(stuck: top) {
  .nav {
    box-shadow: 0 5px 12px rgb(0 0 0 / 18%);
  }
}

Colocar el objetivo de estilo debajo del contenedor

Cambiar el fondo directamente en el elemento que posee container-type hace que la regla de separación sea fácil de violar. Mantén un contenedor interno como el objetivo de respuesta. El componente se mantiene reutilizable y la transición de estado es más fácil de probar.

Usar scrollable para la detectabilidad

Para una lista horizontal de tarjetas, muestra una sugerencia de degradado mientras la lista aún pueda desplazarse hacia su final en línea (inline end):

css
.carousel {
  container-type: scroll-state;
  container-name: cards;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
}

@container cards scroll-state(scrollable: inline-end) {
  .next-hint {
    opacity: 1;
  }
}

La sugerencia es complementaria. No comuniques “más contenido” solo mediante el color; conserva el desplazamiento por teclado, el foco visible y una etiqueta de texto.

Usar snapped para retroalimentación de la tarjeta actual

snapped tiene significado solo cuando existen un ancestro snap y un eje snap válido. Resaltar la tarjeta actual puede mejorar la retroalimentación visual, pero no es una señal confiable para confirmar una selección de negocio. Una selección real todavía necesita un control accesible y estado en JavaScript.

Usar scrolled para retroalimentación direccional

Una barra de herramientas superior puede reaccionar a la dirección de desplazamiento más reciente, pero el movimiento direccional puede agregar carga cognitiva. Mantenlo accesible por defecto; usa scrolled: block-start o block-end solo cuando el beneficio sea claro, y agrega una transición.

Mejora progresiva y alternativa (fallback)

El estilo base debe seguir siendo utilizable cuando scroll-state no esté soportado:

css
.nav {
  box-shadow: none;
}

@supports (container-type: scroll-state) {
  .sticky-heading {
    container-type: scroll-state;
  }

  @container scroll-state(stuck: top) {
    .nav {
      box-shadow: 0 5px 12px rgb(0 0 0 / 18%);
    }
  }
}

Si un navegador antiguo debe recibir un indicador similar, usa una alternativa puntual con IntersectionObserver o ResizeObserver. No recrees un ciclo completo de eventos de scroll en el fallback.

Movimiento y verificación

Chrome recomienda colocar el movimiento no esencial detrás de prefers-reduced-motion: no-preference. Prueba bordes superior e inferior, ausencia de desbordamiento, operación por teclado, desplazamiento táctil, estados intermedios de snap, zoom y un navegador no compatible. Confirma que los cambios de estado nunca oculten el foco ni el contenido.

Respuesta de ejemplo de alta calidad

“Primero preguntaría si esto es retroalimentación visual o un evento de negocio. Para una sombra sticky, un resalte de snap o una sugerencia de scroll, establecería container-type: scroll-state en el elemento sticky o de snap y usaría consultas stuck, snapped o scrollable para que responda un descendiente. Las analíticas, el cálculo de progreso y el envío de selección se quedan en JavaScript. Entregaría un CSS por defecto utilizable, agregaría la mejora detrás de @supports, mantendría una alternativa estática y reduciría el movimiento cuando se solicite. Probaría desbordamiento real, snap, entrada por teclado y navegadores antiguos”.

Errores comunes

Tratar scroll-state como un evento de scroll

Patrón de fallo: usar una consulta para disparar analíticas, una petición o un envío de negocio. Por qué falla: CSS expresa una condición de estilo, no una carga útil de evento ni un momento de negocio confiable. Solución: mantén los estilos en CSS y las acciones de negocio en controles semánticos más JavaScript.

Olvidar el contenedor de consulta

Patrón de fallo: escribir @container scroll-state(stuck: top) sin container-type: scroll-state. Por qué falla: el navegador no tiene una fuente de estado para evaluar. Solución: verifica el prerrequisito de sticky, snap o desbordamiento, luego declara un contenedor con nombre.

Dar estilo al propio contenedor

Patrón de fallo: hacer que un mismo elemento sea tanto el contenedor como el objetivo del estilo de estado. Por qué falla: la consulta apunta a los descendientes y la estructura se vuelve frágil. Solución: agrega un contenedor interno y aplica la retroalimentación de estado allí.

Reemplazar la retroalimentación accesible solo con color

Patrón de fallo: cambiar únicamente el color para mostrar la tarjeta con snap actual o más contenido. Por qué falla: el color por sí solo no transmite el estado de forma confiable y puede excluir a usuarios con baja visión. Solución: proporciona texto, foco, controles o un indicador de desplazamiento interactivo.

Ignorar el soporte y los límites de movimiento

Patrón de fallo: asumir que todos los navegadores soportan la funcionalidad y forzar movimiento durante el desplazamiento. Por qué falla: los navegadores antiguos se degradan y el movimiento puede causar molestias. Solución: usa @supports, mantén la base utilizable y respeta reduced-motion.

Preguntas de seguimiento y respuestas

¿Qué pasa si necesitas un porcentaje exacto de desplazamiento?

scroll-state expone estados discretos, no un porcentaje continuo. Usa JavaScript para leer el rango de desplazamiento y limitar la frecuencia de actualizaciones (throttling), separando lecturas, cómputo y escrituras para que cada evento de desplazamiento no desencadene el layout sincrónicamente.

¿Qué pasa si la consulta de sticky siempre es false?

Verifica el bloque contenedor del elemento sticky, top o inset, el desbordamiento de los ancestros y si el elemento realmente entra en su rango de sticky. Solo entonces inspecciona container-type y el selector descendiente.

¿Qué pasa si el resalte de snap parpadea?

Verifica el eje de snap, scroll-snap-align y las dimensiones del objetivo. Evita transiciones en propiedades que cambian el layout con frecuencia. Mantén explícitos el foco y el estado de selección; el resaltado es solo un estado visual.

¿Qué pasa si los navegadores antiguos deben verse igual?

Define primero un estilo estático utilizable, luego añade un pequeño fallback con IntersectionObserver o ResizeObserver para la señal clave. No copies un sistema complejo de listeners de scroll; si la mejora cuesta más de lo que aporta, acepta una sombra estática o un indicador siempre visible.

¿Qué pasa si el usuario prefiere movimiento reducido?

Mantén la jerarquía de información sin traslación ni escalado forzados. Cambia solo una sombra, borde o color estático, y permite que prefers-reduced-motion anule las transiciones y animaciones.

Fuentes públicas

Preguntas relacionadas